Compare commits
	
		
			No commits in common. 'main' and 'androidx' have entirely different histories. 
		
	
	
		
	| @ -1,176 +0,0 @@ | ||||
| # ---> Android | ||||
| # Gradle files | ||||
| .gradle/ | ||||
| build/ | ||||
| 
 | ||||
| # Local configuration file (sdk path, etc) | ||||
| local.properties | ||||
| 
 | ||||
| # Log/OS Files | ||||
| *.log | ||||
| 
 | ||||
| # Android Studio generated files and folders | ||||
| captures/ | ||||
| .externalNativeBuild/ | ||||
| .cxx/ | ||||
| *.apk | ||||
| output.json | ||||
| 
 | ||||
| # IntelliJ | ||||
| *.iml | ||||
| .idea/ | ||||
| misc.xml | ||||
| deploymentTargetDropDown.xml | ||||
| render.experimental.xml | ||||
| 
 | ||||
| # Keystore files | ||||
| *.jks | ||||
| *.keystore | ||||
| 
 | ||||
| # Google Services (e.g. APIs or Firebase) | ||||
| google-services.json | ||||
| 
 | ||||
| # Android Profiling | ||||
| *.hprof | ||||
| 
 | ||||
| # ---> CMake | ||||
| CMakeLists.txt.user | ||||
| CMakeCache.txt | ||||
| CMakeFiles | ||||
| CMakeScripts | ||||
| Testing | ||||
| Makefile | ||||
| cmake_install.cmake | ||||
| install_manifest.txt | ||||
| compile_commands.json | ||||
| CTestTestfile.cmake | ||||
| _deps | ||||
| 
 | ||||
| # ---> Gradle | ||||
| .gradle | ||||
| **/build/ | ||||
| !src/**/build/ | ||||
| 
 | ||||
| # Ignore Gradle GUI config | ||||
| gradle-app.setting | ||||
| 
 | ||||
| # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) | ||||
| !gradle-wrapper.jar | ||||
| 
 | ||||
| # Avoid ignore Gradle wrappper properties | ||||
| !gradle-wrapper.properties | ||||
| 
 | ||||
| # Cache of project | ||||
| .gradletasknamecache | ||||
| 
 | ||||
| # Eclipse Gradle plugin generated files | ||||
| # Eclipse Core | ||||
| .project | ||||
| # JDT-specific (Eclipse Java Development Tools) | ||||
| .classpath | ||||
| 
 | ||||
| # ---> Java | ||||
| # Compiled class file | ||||
| *.class | ||||
| 
 | ||||
| # Log file | ||||
| *.log | ||||
| 
 | ||||
| # BlueJ files | ||||
| *.ctxt | ||||
| 
 | ||||
| # Mobile Tools for Java (J2ME) | ||||
| .mtj.tmp/ | ||||
| 
 | ||||
| # Package Files # | ||||
| *.jar | ||||
| *.war | ||||
| *.nar | ||||
| *.ear | ||||
| *.zip | ||||
| *.tar.gz | ||||
| *.rar | ||||
| 
 | ||||
| # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml | ||||
| hs_err_pid* | ||||
| replay_pid* | ||||
| 
 | ||||
| # ---> JetBrains | ||||
| # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider | ||||
| # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 | ||||
| 
 | ||||
| # User-specific stuff | ||||
| .idea/**/workspace.xml | ||||
| .idea/**/tasks.xml | ||||
| .idea/**/usage.statistics.xml | ||||
| .idea/**/dictionaries | ||||
| .idea/**/shelf | ||||
| 
 | ||||
| # AWS User-specific | ||||
| .idea/**/aws.xml | ||||
| 
 | ||||
| # Generated files | ||||
| .idea/**/contentModel.xml | ||||
| 
 | ||||
| # Sensitive or high-churn files | ||||
| .idea/**/dataSources/ | ||||
| .idea/**/dataSources.ids | ||||
| .idea/**/dataSources.local.xml | ||||
| .idea/**/sqlDataSources.xml | ||||
| .idea/**/dynamic.xml | ||||
| .idea/**/uiDesigner.xml | ||||
| .idea/**/dbnavigator.xml | ||||
| 
 | ||||
| # Gradle | ||||
| .idea/**/gradle.xml | ||||
| .idea/**/libraries | ||||
| 
 | ||||
| # Gradle and Maven with auto-import | ||||
| # When using Gradle or Maven with auto-import, you should exclude module files, | ||||
| # since they will be recreated, and may cause churn.  Uncomment if using | ||||
| # auto-import. | ||||
| # .idea/artifacts | ||||
| # .idea/compiler.xml | ||||
| # .idea/jarRepositories.xml | ||||
| # .idea/modules.xml | ||||
| # .idea/*.iml | ||||
| # .idea/modules | ||||
| # *.iml | ||||
| # *.ipr | ||||
| 
 | ||||
| # CMake | ||||
| cmake-build-*/ | ||||
| 
 | ||||
| # Mongo Explorer plugin | ||||
| .idea/**/mongoSettings.xml | ||||
| 
 | ||||
| # File-based project format | ||||
| *.iws | ||||
| 
 | ||||
| # IntelliJ | ||||
| out/ | ||||
| 
 | ||||
| # mpeltonen/sbt-idea plugin | ||||
| .idea_modules/ | ||||
| 
 | ||||
| # JIRA plugin | ||||
| atlassian-ide-plugin.xml | ||||
| 
 | ||||
| # Cursive Clojure plugin | ||||
| .idea/replstate.xml | ||||
| 
 | ||||
| # SonarLint plugin | ||||
| .idea/sonarlint/ | ||||
| 
 | ||||
| # Crashlytics plugin (for Android Studio and IntelliJ) | ||||
| com_crashlytics_export_strings.xml | ||||
| crashlytics.properties | ||||
| crashlytics-build.properties | ||||
| fabric.properties | ||||
| 
 | ||||
| # Editor-based Rest Client | ||||
| .idea/httpRequests | ||||
| 
 | ||||
| # Android studio 3.1+ serialized cache file | ||||
| .idea/caches/build_file_checksums.ser | ||||
| 
 | ||||
| @ -1,2 +1,13 @@ | ||||
| # android-libs | ||||
| # Android-Libs | ||||
| 
 | ||||
| ## modules | ||||
| 
 | ||||
| - lib_base:common util classes and base classes,including BaseActivity, BaseFragment, BaseStateFragment, BaseListFragment, BaseAdapter, MVVM, MVP, MultiStateView, Dagger2, AAC, RxJava2, Kotlin, etc. | ||||
| - lib_media_selector:gets photos or videos from system. | ||||
| - lib_qrcode:library for scanning and generating qrcode. | ||||
| - lib_network:RxJava + Retrofit + OkHttp. | ||||
| - lib_cache: DiskLruCache + MMKV. | ||||
| 
 | ||||
| ## how to use | ||||
| 
 | ||||
| refer [AndroidArchitecture](https://github.com/Ztiany/AndroidArchitecture) | ||||
|  | ||||
| @ -0,0 +1,22 @@ | ||||
| *.iml | ||||
| .gradle | ||||
| /local.properties | ||||
| /.idea | ||||
| .DS_Store | ||||
| /build | ||||
| /captures | ||||
| .externalNativeBuild | ||||
| *.iml | ||||
| .idea/ | ||||
| .gradle | ||||
| /local.properties | ||||
| .DS_Store | ||||
| /build | ||||
| /captures | ||||
| *.apk | ||||
| *.ap_ | ||||
| *.dex | ||||
| *.class | ||||
| bin/ | ||||
| gen/ | ||||
| local.properties | ||||
| @ -0,0 +1,28 @@ | ||||
| # Android Development Base Library | ||||
| 
 | ||||
| ## 1 Third part Libraries | ||||
| 
 | ||||
| - [AndroidX](https://developer.android.com/jetpack/androidx) | ||||
| - [RxJava(2)](https://github.com/ReactiveX/RxJava) | ||||
| - [RxAndroid(2)](https://github.com/ReactiveX/RxAndroid) | ||||
| - [AutoDispose](https://github.com/uber/AutoDispose) | ||||
| - [Dagger2](https://github.com/google/dagger) | ||||
| - [Glide](https://github.com/bumptech/glide) | ||||
| - [OkHttp](https://github.com/square/okhttp) | ||||
| - [Timber](https://github.com/JakeWharton/timber) | ||||
| - [WrapperAdapter](https://github.com/Ztiany/WrapperAdapter) | ||||
| - [MultiTypeAdapter](https://github.com/drakeet/MultiType) | ||||
| - [AndroidUtilCode](https://github.com/Blankj/AndroidUtilCode) | ||||
| 
 | ||||
| ##  2 Environment | ||||
| 
 | ||||
| - AndroidStudio 3+ | ||||
| - Java8 with desugar | ||||
| 
 | ||||
| ## 3 How do we make Dagger2 working with SavedStateViewModel? | ||||
| 
 | ||||
| refer | ||||
| 
 | ||||
| - [saving-ui-state-with-viewmodel-savedstate-and-dagger](https://proandroiddev.com/saving-ui-state-with-viewmodel-savedstate-and-dagger-f77bcaeb8b08#7f89) | ||||
| - [AssistedInject](https://github.com/square/AssistedInject/issues/81) | ||||
| - [github-commit-browser](https://github.com/Nimrodda/github-commit-browser) | ||||
| @ -0,0 +1,130 @@ | ||||
| apply plugin: 'com.android.library' | ||||
| apply plugin: 'kotlin-android' | ||||
| apply plugin: 'kotlin-android-extensions' | ||||
| apply plugin: 'kotlin-kapt' | ||||
| 
 | ||||
| androidExtensions { | ||||
|     experimental = true | ||||
| } | ||||
| 
 | ||||
| android { | ||||
|     compileSdkVersion rootProject.compileSdkVersion | ||||
|     buildToolsVersion rootProject.buildToolsVersion | ||||
| 
 | ||||
|     defaultConfig { | ||||
| 
 | ||||
|         minSdkVersion rootProject.minSdkVersion | ||||
|         targetSdkVersion rootProject.targetSdkVersion | ||||
|         versionCode 1 | ||||
|         versionName "1.0" | ||||
| 
 | ||||
|         javaCompileOptions { | ||||
|             annotationProcessorOptions { | ||||
|                 includeCompileClasspath false | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     buildTypes { | ||||
|         release { | ||||
|             minifyEnabled false | ||||
|             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     lintOptions { | ||||
|         abortOnError false | ||||
|     } | ||||
| 
 | ||||
|     compileOptions { | ||||
|         sourceCompatibility JavaVersion.VERSION_1_8 | ||||
|         targetCompatibility JavaVersion.VERSION_1_8 | ||||
|     } | ||||
| 
 | ||||
|     kotlinOptions { | ||||
|         jvmTarget = "1.8" | ||||
|     } | ||||
| 
 | ||||
|     sourceSets { | ||||
|         main { | ||||
|             java.srcDirs += "src/github/java" | ||||
|             res.srcDirs += "src/github/res" | ||||
|         } | ||||
|     } | ||||
|      | ||||
| } | ||||
| 
 | ||||
| dependencies { | ||||
|     api fileTree(dir: 'libs', include: ['*.jar']) | ||||
| 
 | ||||
|     //测试 | ||||
|     testImplementation testLibraries.junit | ||||
| 
 | ||||
|     //AndroidSupport | ||||
|     api androidLibraries.supportV4 | ||||
|     api androidLibraries.appcompat | ||||
|     api androidLibraries.recyclerView | ||||
|     api androidLibraries.material | ||||
|     api androidLibraries.percentLayout | ||||
|     api androidLibraries.constraintLayout | ||||
|     api androidLibraries.viewpager2 | ||||
|     api androidLibraries.annotation | ||||
| 
 | ||||
|     //AAC | ||||
|     api androidLibraries.archRuntime | ||||
|     api androidLibraries.archCommon | ||||
|     api androidLibraries.fragmentKtx | ||||
|     api androidLibraries.lifecycleCommon | ||||
|     api androidLibraries.lifecycleCommonJava8 | ||||
|     api androidLibraries.lifecycleRuntimeKtx | ||||
|     api androidLibraries.lifecycleLiveDataCore | ||||
|     api androidLibraries.lifecycleLiveData | ||||
|     api androidLibraries.lifecycleLiveKtx | ||||
|     api androidLibraries.lifecycleViewModel | ||||
|     api androidLibraries.lifecycleViewModelKtx | ||||
|     api androidLibraries.lifecycleExtensions | ||||
|     api androidLibraries.lifecycleReactiveStreams | ||||
| 
 | ||||
|     //Kotlin | ||||
|     api kotlinLibraries.kotlinStdlib | ||||
|     api kotlinLibraries.kotlinReflect | ||||
|     api kotlinLibraries.kotlinCoroutines | ||||
|     api kotlinLibraries.kotlinAndroidCoroutines | ||||
|     api kotlinLibraries.kotlinxCoroutinesRx2 | ||||
| 
 | ||||
|     //RxJava | ||||
|     api thirdLibraries.rxJava | ||||
|     api thirdLibraries.rxAndroid | ||||
|     api thirdLibraries.rxBinding | ||||
|     api thirdLibraries.autoDispose | ||||
|     api thirdLibraries.autoDisposeAndroid | ||||
|     api thirdLibraries.autoDisposeLifecycle | ||||
|     api thirdLibraries.autoDisposeLifecycleArchcomponents | ||||
| 
 | ||||
|     /*Dagger2*/ | ||||
|     api thirdLibraries.dagger2 | ||||
|     api thirdLibraries.jsr305 | ||||
|     api thirdLibraries.dagger2Android | ||||
|     api thirdLibraries.dagger2AndroidSupport | ||||
|     kapt thirdLibraries.dagger2Apt | ||||
|     kapt thirdLibraries.dagger2AndroidApt | ||||
| 
 | ||||
|     //LoadMore | ||||
|     api uiLibraries.wrapperAdapter | ||||
| 
 | ||||
|     //Adapter | ||||
|     api uiLibraries.multiType | ||||
| 
 | ||||
|     //Log | ||||
|     api thirdLibraries.timber | ||||
| 
 | ||||
|     //ImageLoader | ||||
|     api thirdLibraries.glide | ||||
|     api thirdLibraries.glideOkHttp | ||||
|     api thirdLibraries.okHttp | ||||
| 
 | ||||
|     //Utils | ||||
|     api thirdLibraries.utilcode | ||||
|     api thirdLibraries.jOOR | ||||
|     api thirdLibraries.supportOptional | ||||
| } | ||||
| @ -0,0 +1,17 @@ | ||||
| # Add project specific ProGuard rules here. | ||||
| # By default, the flags in this file are appended to flags specified | ||||
| # in E:\DeveloperTools\sdk/tools/proguard/proguard-android.txt | ||||
| # You can edit the include path and order by changing the proguardFiles | ||||
| # directive in build.gradle. | ||||
| # | ||||
| # For more details, see | ||||
| #   http://developer.android.com/guide/developing/tools/proguard.html | ||||
| 
 | ||||
| # Add any project specific keep options here: | ||||
| 
 | ||||
| # If your project uses WebView with JS, uncomment the following | ||||
| # and specify the fully qualified class name to the JavaScript interface | ||||
| # class: | ||||
| #-keepclassmembers class fqcn.of.javascript.interface.for.webview { | ||||
| #   public *; | ||||
| #} | ||||
| @ -0,0 +1,583 @@ | ||||
| /* | ||||
|  * Copyright (C) 2017 Jared Rummler | ||||
|  * | ||||
|  *  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *  you may not use this file except in compliance with the License. | ||||
|  *  You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  *  Unless required by applicable law or agreed to in writing, software | ||||
|  *  distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *  See the License for the specific language governing permissions and | ||||
|  *  limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.jaredrummler.android.util; | ||||
| 
 | ||||
| import android.os.Build; | ||||
| import android.text.Html; | ||||
| import android.text.Spanned; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import java.util.Iterator; | ||||
| import java.util.LinkedList; | ||||
| 
 | ||||
| import androidx.annotation.RequiresApi; | ||||
| 
 | ||||
| /** | ||||
|  * <p>Build a valid HTML string for a {@link TextView}.</p> | ||||
|  * | ||||
|  * <p>Example usage:</p> | ||||
|  * | ||||
|  * <pre> | ||||
|  * <code> | ||||
|  * HtmlBuilder html = new HtmlBuilder() | ||||
|  * .p() | ||||
|  * .b() | ||||
|  * .font().color("red").face("sans-serif-condensed").text("Hello").close() | ||||
|  * .close() | ||||
|  * .close(); | ||||
|  * | ||||
|  * // html.toString():
 | ||||
|  * // <p><b><font color="red" face="sans-serif-condensed">Hello</font></b></p>
 | ||||
|  * | ||||
|  * yourEditText.setText(html.toSpan()); | ||||
|  * </code> | ||||
|  * </pre> | ||||
|  * | ||||
|  * <p>HTML Tags Supported by {@link TextView}:</p> | ||||
|  * | ||||
|  * <ul> | ||||
|  * <li><code><a href="..."></code></li> | ||||
|  * <li><code><b></code></li> | ||||
|  * <li><code><big></code></li> | ||||
|  * <li><code><blockquote></code></li> | ||||
|  * <li><code><br></code></li> | ||||
|  * <li><code><cite></code></li> | ||||
|  * <li><code><dfn></code></li> | ||||
|  * <li><code><div align="..."></code></li> | ||||
|  * <li><code><em></code></li> | ||||
|  * <li><code><font color="..." face="..."></code></li> | ||||
|  * <li><code><h1></code></li> | ||||
|  * <li><code><h2></code></li> | ||||
|  * <li><code><h3></code></li> | ||||
|  * <li><code><h4></code></li> | ||||
|  * <li><code><h5></code></li> | ||||
|  * <li><code><h6></code></li> | ||||
|  * <li><code><i></code></li> | ||||
|  * <li><code><img src="..."></code></li> | ||||
|  * <li><code><p></code></li> | ||||
|  * <li><code><small></code></li> | ||||
|  * <li><code><strike></code></li> | ||||
|  * <li><code><strong></code></li> | ||||
|  * <li><code><sub></code></li> | ||||
|  * <li><code><sup></code></li> | ||||
|  * <li><code><tt></code></li> | ||||
|  * <li><code><u></code></li> | ||||
|  * <li><code><ul></code> (Android 7.0+)</li> | ||||
|  * <li><code><li></code> (Android 7.0+)</li> | ||||
|  * </ul> | ||||
|  * | ||||
|  * @see <a href='https://github.com/jaredrummler/HtmlBuilder'>HtmlBuilder</a>
 | ||||
|  */ | ||||
| public class HtmlBuilder { | ||||
| 
 | ||||
|     private final StringBuilder html = new StringBuilder(); | ||||
| 
 | ||||
|     private final LinkedList<String> tags = new LinkedList<>(); | ||||
| 
 | ||||
|     public HtmlBuilder open(String element, String data) { | ||||
|         tags.add(element); | ||||
|         html.append('<'); | ||||
|         html.append(element); | ||||
|         if (data != null) { | ||||
|             html.append(' ').append(data); | ||||
|         } | ||||
|         html.append('>'); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder open(String element) { | ||||
|         return open(element, null); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder close(String element) { | ||||
|         html.append("</").append(element).append('>'); | ||||
|         for (Iterator<String> iterator = tags.iterator(); iterator.hasNext(); ) { | ||||
|             if (iterator.next().equals(element)) { | ||||
|                 iterator.remove(); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder close() { | ||||
|         if (tags.isEmpty()) { | ||||
|             return this; | ||||
|         } | ||||
|         html.append("</").append(tags.removeLast()).append('>'); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder close(char element) { | ||||
|         return close(String.valueOf(element)); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder append(boolean b) { | ||||
|         html.append(b); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder append(char c) { | ||||
|         html.append(c); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder append(int i) { | ||||
|         html.append(i); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder append(long l) { | ||||
|         html.append(l); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder append(float f) { | ||||
|         html.append(f); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder append(double d) { | ||||
|         html.append(d); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder append(Object obj) { | ||||
|         html.append(obj); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder append(String str) { | ||||
|         html.append(str); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder append(StringBuffer sb) { | ||||
|         html.append(sb); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder append(char[] chars) { | ||||
|         html.append(chars); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder append(char[] str, int offset, int len) { | ||||
|         html.append(str, offset, len); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder append(CharSequence csq) { | ||||
|         html.append(csq); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder append(CharSequence csq, int start, int end) { | ||||
|         html.append(csq, start, end); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder append(Tag tag) { | ||||
|         html.append(tag.toString()); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder a(String href, String text) { | ||||
|         return append(String.format("<a href=\"%s\">%s</a>", href, text)); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder b() { | ||||
|         return open("b"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder b(String text) { | ||||
|         html.append("<b>").append(text).append("</b>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder big() { | ||||
|         return open("big"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder big(String text) { | ||||
|         html.append("<big>").append(text).append("</big>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder blockquote() { | ||||
|         return open("blockquote"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder blockquote(String text) { | ||||
|         html.append("<blockquote>").append(text).append("</blockquote>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder br() { | ||||
|         html.append("<br>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder cite() { | ||||
|         return open("cite"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder cite(String text) { | ||||
|         html.append("<cite>").append(text).append("</cite>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder dfn() { | ||||
|         return open("dfn"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder dfn(String text) { | ||||
|         html.append("<dfn>").append(text).append("</dfn>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder div() { | ||||
|         return open("div"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder div(String align) { | ||||
|         html.append(String.format("<div align=\"%s\">", align)); | ||||
|         tags.add("div"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder em() { | ||||
|         return open("em"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder em(String text) { | ||||
|         html.append("<em>").append(text).append("</em>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public Font font() { | ||||
|         return new Font(this); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder font(int color, String text) { | ||||
|         return font().color(color).text(text).close(); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder font(String face, String text) { | ||||
|         return font().face(face).text(text).close(); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder h1() { | ||||
|         return open("h1"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder h1(String text) { | ||||
|         html.append("<h1>").append(text).append("</h1>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder h2() { | ||||
|         return open("h2"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder h2(String text) { | ||||
|         html.append("<h2>").append(text).append("</h2>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder h3() { | ||||
|         return open("h3"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder h3(String text) { | ||||
|         html.append("<h3>").append(text).append("</h3>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder h4() { | ||||
|         return open("h4"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder h4(String text) { | ||||
|         html.append("<h4>").append(text).append("</h4>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder h5() { | ||||
|         return open("h5"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder h5(String text) { | ||||
|         html.append("<h5>").append(text).append("</h5>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder h6() { | ||||
|         return open("h6"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder h6(String text) { | ||||
|         html.append("<h6>").append(text).append("</h6>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder i() { | ||||
|         return open("i"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder i(String text) { | ||||
|         html.append("<i>").append(text).append("</i>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public Img img() { | ||||
|         return new Img(this); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder img(String src) { | ||||
|         return img().src(src).close(); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder p() { | ||||
|         return open("p"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder p(String text) { | ||||
|         html.append("<p>").append(text).append("</p>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder small() { | ||||
|         return open("small"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder small(String text) { | ||||
|         html.append("<small>").append(text).append("</small>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder strike() { | ||||
|         return open("strike"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder strike(String text) { | ||||
|         html.append("<strike>").append(text).append("</strike>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder strong() { | ||||
|         return open("strong"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder strong(String text) { | ||||
|         html.append("<strong>").append(text).append("</strong>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder sub() { | ||||
|         return open("sub"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder sub(String text) { | ||||
|         html.append("<sub>").append(text).append("</sub>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder sup() { | ||||
|         return open("sup"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder sup(String text) { | ||||
|         html.append("<sup>").append(text).append("</sup>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder tt() { | ||||
|         return open("tt"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder tt(String text) { | ||||
|         html.append("<tt>").append(text).append("</tt>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder u() { | ||||
|         return open("u"); | ||||
|     } | ||||
| 
 | ||||
|     public HtmlBuilder u(String text) { | ||||
|         html.append("<u>").append(text).append("</u>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     @RequiresApi(Build.VERSION_CODES.N) | ||||
|     public HtmlBuilder ul() { | ||||
|         return open("ul"); | ||||
|     } | ||||
| 
 | ||||
|     @RequiresApi(Build.VERSION_CODES.N) | ||||
|     public HtmlBuilder li() { | ||||
|         return open("li"); | ||||
|     } | ||||
| 
 | ||||
|     @RequiresApi(Build.VERSION_CODES.N) | ||||
|     public HtmlBuilder li(String text) { | ||||
|         html.append("<li>").append(text).append("</li>"); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     @RequiresApi(Build.VERSION_CODES.N) | ||||
|     public Spanned build(int flags) { | ||||
|         return Html.fromHtml(html.toString(), flags); | ||||
|     } | ||||
| 
 | ||||
|     public Spanned build() { | ||||
|         //noinspection deprecation
 | ||||
|         return Html.fromHtml(html.toString()); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public String toString() { | ||||
|         return html.toString(); | ||||
|     } | ||||
| 
 | ||||
|     public static class Tag { | ||||
| 
 | ||||
|         final HtmlBuilder builder; | ||||
|         final String element; | ||||
|         String separator = ""; | ||||
| 
 | ||||
|         public Tag(HtmlBuilder builder, String element) { | ||||
|             this.builder = builder; | ||||
|             this.element = element; | ||||
|             open(); | ||||
|         } | ||||
| 
 | ||||
|         protected void open() { | ||||
|             builder.append('<').append(element).append(' '); | ||||
|         } | ||||
| 
 | ||||
|         public HtmlBuilder close() { | ||||
|             return builder.append("</").append(element).append('>'); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public String toString() { | ||||
|             return builder.toString(); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public static class Font extends Tag { | ||||
| 
 | ||||
|         public Font() { | ||||
|             this(new HtmlBuilder()); | ||||
|         } | ||||
| 
 | ||||
|         public Font(HtmlBuilder builder) { | ||||
|             super(builder, "font"); | ||||
|         } | ||||
| 
 | ||||
|         public Font size(int size) { | ||||
|             builder.append(separator).append("size=\"").append(size).append('\"'); | ||||
|             separator = " "; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public Font size(String size) { | ||||
|             builder.append(separator).append("size=\"").append(size).append('\"'); | ||||
|             separator = " "; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public Font color(int color) { | ||||
|             return color(String.format("#%06X", (0xFFFFFF & color))); | ||||
|         } | ||||
| 
 | ||||
|         public Font color(String color) { | ||||
|             builder.append(separator).append("color=\"").append(color).append('\"'); | ||||
|             separator = " "; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public Font face(String face) { | ||||
|             builder.append(separator).append("face=\"").append(face).append('\"'); | ||||
|             separator = " "; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public Font text(String text) { | ||||
|             builder.append('>').append(text); | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public static class Img extends Tag { | ||||
| 
 | ||||
|         public Img() { | ||||
|             this(new HtmlBuilder()); | ||||
|         } | ||||
| 
 | ||||
|         public Img(HtmlBuilder builder) { | ||||
|             super(builder, "img"); | ||||
|         } | ||||
| 
 | ||||
|         public Img src(String src) { | ||||
|             builder.append(separator).append("src=\"").append(src).append('\"'); | ||||
|             separator = " "; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public Img alt(String alt) { | ||||
|             builder.append(separator).append("alt=\"").append(alt).append('\"'); | ||||
|             separator = " "; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public Img height(String height) { | ||||
|             builder.append(separator).append("height=\"").append(height).append('\"'); | ||||
|             separator = " "; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public Img height(int height) { | ||||
|             builder.append(separator).append("height=\"").append(height).append('\"'); | ||||
|             separator = " "; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public Img width(String width) { | ||||
|             builder.append(separator).append("width=\"").append(width).append('\"'); | ||||
|             separator = " "; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public Img width(int width) { | ||||
|             builder.append(separator).append("width=\"").append(width).append('\"'); | ||||
|             separator = " "; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public HtmlBuilder close() { | ||||
|             return builder.append('>'); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,7 @@ | ||||
| <manifest package="com.android.base"> | ||||
| 
 | ||||
|     <application> | ||||
| 
 | ||||
|     </application> | ||||
| 
 | ||||
| </manifest> | ||||
| @ -0,0 +1,79 @@ | ||||
| package com.android.base.adapter; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| /** | ||||
|  * <pre> | ||||
|  *     注意数据源引用的替换,只有setDataSource方法会把elements替换掉之前的数据源引用,其他方法都是基于现有数据集合做删除与添加操作。 | ||||
|  * </pre> | ||||
|  * | ||||
|  * @author Ztiany | ||||
|  * Date : 2016-09-12 11:33 | ||||
|  * Email: 1169654504@qq.com | ||||
|  */ | ||||
| public interface DataManager<T> { | ||||
| 
 | ||||
|     void add(T element); | ||||
| 
 | ||||
|     void addAt(int location, T element); | ||||
| 
 | ||||
|     void addItems(List<T> elements); | ||||
| 
 | ||||
|     /** | ||||
|      * 添加元素前会使用equals方法进行比较。 | ||||
|      * | ||||
|      * @param elements 元素 | ||||
|      */ | ||||
|     void addItemsChecked(List<T> elements); | ||||
| 
 | ||||
|     void addItemsAt(int location, List<T> elements); | ||||
| 
 | ||||
|     void replace(T oldElement, T newElement); | ||||
| 
 | ||||
|     void replaceAt(int index, T element); | ||||
| 
 | ||||
|     /** | ||||
|      * 清除之前集合中的数据,然后把elements添加到之前的集合中,不会使用elements作为数据源 | ||||
|      * | ||||
|      * @param elements 元素 | ||||
|      */ | ||||
|     void replaceAll(List<T> elements); | ||||
| 
 | ||||
|     /** | ||||
|      * 此方法会使用 newDataSource 替换掉之前的数据源,而不对之前的数据源做任何操作。 | ||||
|      * | ||||
|      * @param newDataSource        新的数据集 | ||||
|      * @param notifyDataSetChanged 是否调用adapter的notifyDataSetChanged方法 | ||||
|      */ | ||||
|     void setDataSource(List<T> newDataSource, boolean notifyDataSetChanged); | ||||
| 
 | ||||
|     void remove(T element); | ||||
| 
 | ||||
|     void removeAt(int index); | ||||
| 
 | ||||
|     void removeItems(List<T> elements); | ||||
| 
 | ||||
|     void removeItems(List<T> elements, boolean isSuccessive); | ||||
| 
 | ||||
|     @Nullable | ||||
|     T getItem(int position); | ||||
| 
 | ||||
|     List<T> getItems(); | ||||
| 
 | ||||
|     int getDataSize(); | ||||
| 
 | ||||
|     boolean contains(T element); | ||||
| 
 | ||||
|     boolean isEmpty(); | ||||
| 
 | ||||
|     void clear(); | ||||
| 
 | ||||
|     /** | ||||
|      * @param t element | ||||
|      * @return -1 if not contains this element | ||||
|      */ | ||||
|     int indexItem(T t); | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,73 @@ | ||||
| package com.android.base.adapter; | ||||
| 
 | ||||
| import android.util.SparseArray; | ||||
| import android.view.View; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import androidx.annotation.IdRes; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.StringRes; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * @author Ztiany | ||||
|  * Email :1169654504@qq.com | ||||
|  * Date :015-12-29 20:47 | ||||
|  */ | ||||
| public class ItemHelper { | ||||
| 
 | ||||
|     private View mItemView; | ||||
|     private SparseArray<View> views; | ||||
| 
 | ||||
|     public ItemHelper(View itemView) { | ||||
|         mItemView = itemView; | ||||
|         views = new SparseArray<>(); | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("unchecked,WeakerAccess") | ||||
|     public <T extends View> T getView(int viewId) { | ||||
|         View view = views.get(viewId); | ||||
|         if (view == null) { | ||||
|             view = mItemView.findViewById(viewId); | ||||
|             views.put(viewId, view); | ||||
|         } | ||||
|         return (T) view; | ||||
|     } | ||||
| 
 | ||||
|     public ItemHelper setText(CharSequence str, @IdRes int viewId) { | ||||
|         ((TextView) getView(viewId)).setText(str == null ? "" : str); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public ItemHelper setText(@StringRes int strId, @IdRes int viewId) { | ||||
|         ((TextView) getView(viewId)).setText(strId); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public ItemHelper setTag(@NonNull Object object, @IdRes int tagId, @IdRes int viewID) { | ||||
|         View view = getView(viewID); | ||||
|         view.setTag(tagId, object); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public ItemHelper setTag(@NonNull Object object, @IdRes int viewID) { | ||||
|         View view = getView(viewID); | ||||
|         view.setTag(object); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     public <T> T getTag(@IdRes int tagId, @IdRes int viewID) { | ||||
|         View view = getView(viewID); | ||||
|         @SuppressWarnings("unchecked") | ||||
|         T tag = (T) view.getTag(tagId); | ||||
|         return tag; | ||||
|     } | ||||
| 
 | ||||
|     public <T> T getTag(@IdRes int viewID) { | ||||
|         View view = getView(viewID); | ||||
|         @SuppressWarnings("unchecked") | ||||
|         T tag = (T) view.getTag(); | ||||
|         return tag; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,185 @@ | ||||
| package com.android.base.adapter.list; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.BaseAdapter; | ||||
| 
 | ||||
| import com.android.base.R; | ||||
| import com.android.base.adapter.DataManager; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| /** | ||||
|  * absListView通用的Adapter,注意:只有setDataSource才能替换原有数据源的引用。 | ||||
|  * | ||||
|  * @param <T> 数据模型 | ||||
|  * @author Ztiany | ||||
|  */ | ||||
| @SuppressWarnings("unused") | ||||
| public abstract class BaseListAdapter<T, VH extends ViewHolder> extends BaseAdapter implements DataManager<T> { | ||||
| 
 | ||||
|     private final Context mContext; | ||||
|     private final static int ITEM_ID = R.id.base_item_tag_view_id; | ||||
|     private DataManager<T> mDataManager; | ||||
|     private final LayoutInflater mLayoutInflater; | ||||
| 
 | ||||
|     public BaseListAdapter(@NonNull Context context) { | ||||
|         this(context, null); | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("all") | ||||
|     public BaseListAdapter(Context context, List<T> data) { | ||||
|         this.mContext = context; | ||||
|         mLayoutInflater = LayoutInflater.from(context); | ||||
|         mDataManager = new ListDataManagerImpl<>(data, this); | ||||
|     } | ||||
| 
 | ||||
|     public Context getContext() { | ||||
|         return mContext; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int getCount() { | ||||
|         return getDataSize(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public long getItemId(int position) { | ||||
|         return position; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public View getView(int position, View convertView, ViewGroup parent) { | ||||
|         VH viewHolder; | ||||
|         int type = getItemViewType(position); | ||||
|         if (convertView == null) { | ||||
|             viewHolder = onCreateViewHolder(mLayoutInflater, parent, type); | ||||
|             viewHolder.mItemView.setTag(ITEM_ID, viewHolder); | ||||
|         } else { | ||||
|             viewHolder = (VH) convertView.getTag(ITEM_ID); | ||||
|         } | ||||
|         viewHolder.setPosition(position); | ||||
|         viewHolder.setType(type); | ||||
|         T item = getItem(position); | ||||
|         onBindData(viewHolder, item); | ||||
|         return viewHolder.mItemView; | ||||
|     } | ||||
| 
 | ||||
|     protected abstract void onBindData(@NonNull VH viewHolder, T item); | ||||
| 
 | ||||
|     @NonNull | ||||
|     protected abstract VH onCreateViewHolder(@NonNull LayoutInflater layoutInflater, @NonNull ViewGroup parent, int type); | ||||
| 
 | ||||
|     @Override | ||||
|     public int getItemViewType(int position) { | ||||
|         return super.getItemViewType(position); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int getViewTypeCount() { | ||||
|         return super.getViewTypeCount(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void add(T elem) { | ||||
|         mDataManager.add(elem); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addAt(int location, T elem) { | ||||
|         mDataManager.addAt(location, elem); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addItems(List<T> elements) { | ||||
|         mDataManager.addItems(elements); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addItemsChecked(List<T> elements) { | ||||
|         mDataManager.addItemsChecked(elements); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addItemsAt(int location, List<T> elements) { | ||||
|         mDataManager.addItemsAt(location, elements); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void replace(T oldElem, T newElem) { | ||||
|         mDataManager.replace(oldElem, newElem); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void replaceAt(int index, T elem) { | ||||
|         mDataManager.replaceAt(index, elem); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void replaceAll(List<T> elements) { | ||||
|         mDataManager.replaceAll(elements); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void remove(T elem) { | ||||
|         mDataManager.remove(elem); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void removeItems(List<T> elements) { | ||||
|         mDataManager.removeItems(elements); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void removeItems(List<T> elements, boolean isSuccessive) { | ||||
|         mDataManager.removeItems(elements, isSuccessive); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void removeAt(int index) { | ||||
|         mDataManager.removeAt(index); | ||||
|     } | ||||
| 
 | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public T getItem(int position) { | ||||
|         return mDataManager.getItem(position); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public final int getDataSize() { | ||||
|         return mDataManager.getDataSize(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean contains(T elem) { | ||||
|         return mDataManager.contains(elem); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void setDataSource(List<T> elements, boolean notifyDataSetChanged) { | ||||
|         mDataManager.setDataSource(elements, notifyDataSetChanged); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int indexItem(T t) { | ||||
|         return mDataManager.indexItem(t); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void clear() { | ||||
|         mDataManager.clear(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public List<T> getItems() { | ||||
|         return mDataManager.getItems(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,191 @@ | ||||
| package com.android.base.adapter.list; | ||||
| 
 | ||||
| import android.widget.BaseAdapter; | ||||
| 
 | ||||
| import com.android.base.adapter.DataManager; | ||||
| import com.android.base.utils.common.Checker; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| 
 | ||||
| final class ListDataManagerImpl<T> implements DataManager<T> { | ||||
| 
 | ||||
|     private List<T> mData; | ||||
|     private BaseAdapter mBaseAdapter; | ||||
| 
 | ||||
|     ListDataManagerImpl(List<T> tList, BaseAdapter adapter) { | ||||
|         mData = tList; | ||||
|         mBaseAdapter = adapter; | ||||
|     } | ||||
| 
 | ||||
|     private void checkData() { | ||||
|         if (mData == null) { | ||||
|             mData = new ArrayList<>(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void add(T elem) { | ||||
|         checkData(); | ||||
|         mData.add(elem); | ||||
|         notifyDataSetChanged(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addAt(int location, T elem) { | ||||
|         checkData(); | ||||
|         mData.add(location, elem); | ||||
|         notifyDataSetChanged(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addItems(List<T> elements) { | ||||
|         checkData(); | ||||
|         mData.addAll(elements); | ||||
|         notifyDataSetChanged(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addItemsChecked(List<T> elements) { | ||||
|         if (Checker.isEmpty(elements)) { | ||||
|             return; | ||||
|         } | ||||
|         if (mData == null) { | ||||
|             addItems(elements); | ||||
|             return; | ||||
|         } | ||||
|         for (T element : elements) { | ||||
|             if (element == null) { | ||||
|                 continue; | ||||
|             } | ||||
|             mData.remove(element); | ||||
|         } | ||||
|         mData.addAll(elements); | ||||
|         notifyDataSetChanged(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addItemsAt(int location, List<T> elements) { | ||||
|         checkData(); | ||||
|         mData.addAll(location, elements); | ||||
|         notifyDataSetChanged(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void replace(T oldElem, T newElem) { | ||||
|         if (mData != null && mData.contains(oldElem)) { | ||||
|             replaceAt(mData.indexOf(oldElem), newElem); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void replaceAt(int index, T elem) { | ||||
|         if (mData != null && mData.size() > index) { | ||||
|             mData.set(index, elem); | ||||
|             notifyDataSetChanged(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void replaceAll(List<T> elements) { | ||||
|         if (mData == null) { | ||||
|             mData = new ArrayList<>(); | ||||
|         } | ||||
|         mData.clear(); | ||||
|         if (elements != null) { | ||||
|             mData.addAll(elements); | ||||
|         } | ||||
|         notifyDataSetChanged(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void setDataSource(List<T> newDataSource, boolean notifyDataSetChanged) { | ||||
|         mData = newDataSource; | ||||
|         if (notifyDataSetChanged) { | ||||
|             notifyDataSetChanged(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void remove(T elem) { | ||||
|         if (mData != null && mData.contains(elem)) { | ||||
|             mData.remove(elem); | ||||
|             notifyDataSetChanged(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void removeItems(List<T> elements) { | ||||
|         if (mData != null && mData.containsAll(elements)) { | ||||
|             mData.removeAll(elements); | ||||
|             notifyDataSetChanged(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void removeItems(List<T> elements, boolean isSuccessive) { | ||||
|         removeItems(elements); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void removeAt(int index) { | ||||
|         if (mData != null && mData.size() > index) { | ||||
|             mData.remove(index); | ||||
|             notifyDataSetChanged(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     @Nullable | ||||
|     public T getItem(int position) { | ||||
|         if (mData != null && mData.size() > position) { | ||||
|             return mData.get(position); | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public final int getDataSize() { | ||||
|         return mData == null ? 0 : mData.size(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean contains(T elem) { | ||||
|         return mData != null && mData.contains(elem); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean isEmpty() { | ||||
|         return mData == null || mData.size() == 0; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void clear() { | ||||
|         if (mData != null && !mData.isEmpty()) { | ||||
|             mData.clear(); | ||||
|             notifyDataSetChanged(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int indexItem(T t) { | ||||
|         List<T> items = getItems(); | ||||
|         if (items == null) { | ||||
|             return -1; | ||||
|         } | ||||
|         return items.indexOf(t); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public List<T> getItems() { | ||||
|         return mData; | ||||
|     } | ||||
| 
 | ||||
|     private void notifyDataSetChanged() { | ||||
|         mBaseAdapter.notifyDataSetChanged(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,21 @@ | ||||
| package com.android.base.adapter.list; | ||||
| 
 | ||||
| import android.view.View; | ||||
| 
 | ||||
| import com.android.base.adapter.ItemHelper; | ||||
| 
 | ||||
| @SuppressWarnings("unused") | ||||
| public class SmartViewHolder extends ViewHolder { | ||||
| 
 | ||||
|     private final ItemHelper mHelper; | ||||
| 
 | ||||
|     public SmartViewHolder(View itemView) { | ||||
|         super(itemView); | ||||
|         mHelper = new ItemHelper(itemView); | ||||
|     } | ||||
| 
 | ||||
|     public ItemHelper helper() { | ||||
|         return mHelper; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,35 @@ | ||||
| package com.android.base.adapter.list; | ||||
| 
 | ||||
| import android.view.View; | ||||
| 
 | ||||
| public class ViewHolder { | ||||
| 
 | ||||
|     protected final View mItemView; | ||||
|     private int mPosition; | ||||
|     private int mType; | ||||
| 
 | ||||
|     public ViewHolder(View itemView) { | ||||
|         mItemView = itemView; | ||||
|     } | ||||
| 
 | ||||
|     public int getPosition() { | ||||
|         return mPosition; | ||||
|     } | ||||
| 
 | ||||
|     void setPosition(int position) { | ||||
|         mPosition = position; | ||||
|     } | ||||
| 
 | ||||
|     public int getType() { | ||||
|         return mType; | ||||
|     } | ||||
| 
 | ||||
|     void setType(int type) { | ||||
|         mType = type; | ||||
|     } | ||||
| 
 | ||||
|     public View getItemView() { | ||||
|         return mItemView; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,154 @@ | ||||
| package com.android.base.adapter.pager; | ||||
| 
 | ||||
| import android.os.Build; | ||||
| import android.util.SparseArray; | ||||
| import android.view.View; | ||||
| 
 | ||||
| /** | ||||
|  * The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of | ||||
|  * storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the | ||||
|  * start of a layout. By construction, they are displaying current information. At the end of | ||||
|  * layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that | ||||
|  * could potentially be used by the adapter to avoid allocating views unnecessarily. | ||||
|  * <p> | ||||
|  * This class was taken from Android's implementation of {@link android.widget.AbsListView} which | ||||
|  * is copyrighted 2006 The Android Open Source Project. | ||||
|  */ | ||||
| public class RecycleBin { | ||||
| 
 | ||||
|   /** | ||||
|    * Views that were on screen at the start of layout. This array is populated at the start of | ||||
|    * layout, and at the end of layout all view in activeViews are moved to scrapViews. | ||||
|    * Views in activeViews represent a contiguous range of Views, with position of the first | ||||
|    * view store in mFirstActivePosition. | ||||
|    */ | ||||
|   private View[] activeViews = new View[0]; | ||||
|   private int[] activeViewTypes = new int[0]; | ||||
| 
 | ||||
|   /** Unsorted views that can be used by the adapter as a convert view. */ | ||||
|   private SparseArray<View>[] scrapViews; | ||||
| 
 | ||||
|   private int viewTypeCount; | ||||
| 
 | ||||
|   private SparseArray<View> currentScrapViews; | ||||
| 
 | ||||
|   public void setViewTypeCount(int viewTypeCount) { | ||||
|     if (viewTypeCount < 1) { | ||||
|       throw new IllegalArgumentException("Can't have a viewTypeCount < 1"); | ||||
|     } | ||||
|     //noinspection unchecked
 | ||||
|     SparseArray<View>[] scrapViews = new SparseArray[viewTypeCount]; | ||||
|     for (int i = 0; i < viewTypeCount; i++) { | ||||
|       scrapViews[i] = new SparseArray<View>(); | ||||
|     } | ||||
|     this.viewTypeCount = viewTypeCount; | ||||
|     currentScrapViews = scrapViews[0]; | ||||
|     this.scrapViews = scrapViews; | ||||
|   } | ||||
| 
 | ||||
|   protected boolean shouldRecycleViewType(int viewType) { | ||||
|     return viewType >= 0; | ||||
|   } | ||||
| 
 | ||||
|   /** @return A view from the ScrapViews collection. These are unordered. */ | ||||
|   View getScrapView(int position, int viewType) { | ||||
|     if (viewTypeCount == 1) { | ||||
|       return retrieveFromScrap(currentScrapViews, position); | ||||
|     } else if (viewType >= 0 && viewType < scrapViews.length) { | ||||
|       return retrieveFromScrap(scrapViews[viewType], position); | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Put a view into the ScrapViews list. These views are unordered. | ||||
|    * | ||||
|    * @param scrap The view to add | ||||
|    */ | ||||
|   void addScrapView(View scrap, int position, int viewType) { | ||||
|     if (viewTypeCount == 1) { | ||||
|       currentScrapViews.put(position, scrap); | ||||
|     } else { | ||||
|       scrapViews[viewType].put(position, scrap); | ||||
|     } | ||||
| 
 | ||||
|     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { | ||||
|       scrap.setAccessibilityDelegate(null); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** Move all views remaining in activeViews to scrapViews. */ | ||||
|   void scrapActiveViews() { | ||||
|     final View[] activeViews = this.activeViews; | ||||
|     final int[] activeViewTypes = this.activeViewTypes; | ||||
|     final boolean multipleScraps = viewTypeCount > 1; | ||||
| 
 | ||||
|     SparseArray<View> scrapViews = currentScrapViews; | ||||
|     final int count = activeViews.length; | ||||
|     for (int i = count - 1; i >= 0; i--) { | ||||
|       final View victim = activeViews[i]; | ||||
|       if (victim != null) { | ||||
|         int whichScrap = activeViewTypes[i]; | ||||
| 
 | ||||
|         activeViews[i] = null; | ||||
|         activeViewTypes[i] = -1; | ||||
| 
 | ||||
|         if (!shouldRecycleViewType(whichScrap)) { | ||||
|           continue; | ||||
|         } | ||||
| 
 | ||||
|         if (multipleScraps) { | ||||
|           scrapViews = this.scrapViews[whichScrap]; | ||||
|         } | ||||
|         scrapViews.put(i, victim); | ||||
| 
 | ||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { | ||||
|           victim.setAccessibilityDelegate(null); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     pruneScrapViews(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Makes sure that the size of scrapViews does not exceed the size of activeViews. | ||||
|    * (This can happen if an adapter does not recycle its views). | ||||
|    */ | ||||
|   private void pruneScrapViews() { | ||||
|     final int maxViews = activeViews.length; | ||||
|     final int viewTypeCount = this.viewTypeCount; | ||||
|     final SparseArray<View>[] scrapViews = this.scrapViews; | ||||
|     for (int i = 0; i < viewTypeCount; ++i) { | ||||
|       final SparseArray<View> scrapPile = scrapViews[i]; | ||||
|       int size = scrapPile.size(); | ||||
|       final int extras = size - maxViews; | ||||
|       size--; | ||||
|       for (int j = 0; j < extras; j++) { | ||||
|         scrapPile.remove(scrapPile.keyAt(size--)); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   static View retrieveFromScrap(SparseArray<View> scrapViews, int position) { | ||||
|     int size = scrapViews.size(); | ||||
|     if (size > 0) { | ||||
|       // See if we still have a view for this position.
 | ||||
|       for (int i = 0; i < size; i++) { | ||||
|         int fromPosition = scrapViews.keyAt(i); | ||||
|         View view = scrapViews.get(fromPosition); | ||||
|         if (fromPosition == position) { | ||||
|           scrapViews.remove(fromPosition); | ||||
|           return view; | ||||
|         } | ||||
|       } | ||||
|       int index = size - 1; | ||||
|       View r = scrapViews.valueAt(index); | ||||
|       scrapViews.remove(scrapViews.keyAt(index)); | ||||
|       return r; | ||||
|     } else { | ||||
|       return null; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,118 @@ | ||||
| package com.android.base.adapter.pager; | ||||
| 
 | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.AdapterView; | ||||
| 
 | ||||
| import org.jetbrains.annotations.NotNull; | ||||
| 
 | ||||
| import androidx.viewpager.widget.PagerAdapter; | ||||
| 
 | ||||
| /** | ||||
|  * A {@link PagerAdapter} which behaves like an {@link android.widget.Adapter} with view types and view recycling. | ||||
|  * | ||||
|  * @see <a href='https://github.com/JakeWharton/salvage'>JakeWharton/salvage</a>
 | ||||
|  */ | ||||
| public abstract class RecyclingPagerAdapter extends PagerAdapter { | ||||
| 
 | ||||
|     static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE; | ||||
| 
 | ||||
|     private final RecycleBin recycleBin; | ||||
| 
 | ||||
|     public RecyclingPagerAdapter() { | ||||
|         this(new RecycleBin()); | ||||
|     } | ||||
| 
 | ||||
|     RecyclingPagerAdapter(RecycleBin recycleBin) { | ||||
|         this.recycleBin = recycleBin; | ||||
|         recycleBin.setViewTypeCount(getViewTypeCount()); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void notifyDataSetChanged() { | ||||
|         recycleBin.scrapActiveViews(); | ||||
|         super.notifyDataSetChanged(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public final Object instantiateItem(@NotNull 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, @NotNull Object object) { | ||||
|         View view = (View) object; | ||||
|         container.removeView(view); | ||||
|         int viewType = getItemViewType(position); | ||||
|         if (viewType != IGNORE_ITEM_VIEW_TYPE) { | ||||
|             recycleBin.addScrapView(view, position, viewType); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public final boolean isViewFromObject(@NotNull View view, @NotNull Object object) { | ||||
|         return view == 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); | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,33 @@ | ||||
| package com.android.base.adapter.pager | ||||
| 
 | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import kotlinx.android.extensions.LayoutContainer | ||||
| 
 | ||||
| /** | ||||
|  * 如果使用可缩放的 View 作为 pager,可能不适合使用此Adapter | ||||
|  * | ||||
|  * @param <T>  数据 | ||||
|  * @param <VH> View Holder类型 | ||||
| </VH></T> */ | ||||
| abstract class SimpleViewPagerAdapter<T>(data: List<T>) : ViewPagerAdapter<T, KtViewHolder>(data) { | ||||
| 
 | ||||
|     override fun createViewHolder(container: ViewGroup): KtViewHolder { | ||||
|         val layout = provideLayout(container) | ||||
|         val itemView = if (layout is Int) { | ||||
|             LayoutInflater.from(container.context).inflate(layout, container, false) | ||||
|         } else { | ||||
|             layout as View | ||||
|         } | ||||
|         return KtViewHolder(itemView) | ||||
|     } | ||||
| 
 | ||||
|     /**provide a layout id or a View*/ | ||||
|     abstract fun provideLayout(parent: ViewGroup): Any | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| class KtViewHolder(itemView: View) : ViewPagerAdapter.ViewHolder(itemView), LayoutContainer { | ||||
|     override val containerView: View = itemView | ||||
| } | ||||
| @ -0,0 +1,69 @@ | ||||
| package com.android.base.adapter.pager; | ||||
| 
 | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| 
 | ||||
| import com.android.base.R; | ||||
| import com.android.base.utils.common.Checker; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| /** | ||||
|  * 如果使用可缩放的 View 作为 pager,可能不适合使用此 Adapter | ||||
|  * | ||||
|  * @param <T>  数据 | ||||
|  * @param <VH> View Holder类型 | ||||
|  */ | ||||
| public abstract class ViewPagerAdapter<T, VH extends ViewPagerAdapter.ViewHolder> extends RecyclingPagerAdapter { | ||||
| 
 | ||||
|     private List<T> mData; | ||||
| 
 | ||||
|     public ViewPagerAdapter(@Nullable List<T> data) { | ||||
|         mData = data; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public View getView(int position, View convertView, ViewGroup container) { | ||||
|         VH viewHolder; | ||||
|         if (convertView == null) { | ||||
|             viewHolder = createViewHolder(container); | ||||
|         } else { | ||||
|             viewHolder = (VH) convertView.getTag(R.id.base_item_tag_view_id); | ||||
|         } | ||||
|         T item = getItem(position); | ||||
|         onBindData(viewHolder, item); | ||||
|         return viewHolder.itemView; | ||||
|     } | ||||
| 
 | ||||
|     protected abstract VH createViewHolder(@NonNull ViewGroup container); | ||||
| 
 | ||||
|     protected abstract void onBindData(@NonNull VH viewHolder, @NonNull T item); | ||||
| 
 | ||||
|     @Override | ||||
|     public int getCount() { | ||||
|         return Checker.isEmpty(mData) ? 0 : mData.size(); | ||||
|     } | ||||
| 
 | ||||
|     public T getItem(int position) { | ||||
|         if (position < 0 || position >= mData.size()) { | ||||
|             return null; | ||||
|         } | ||||
|         return mData.get(position); | ||||
|     } | ||||
| 
 | ||||
|     public static class ViewHolder { | ||||
| 
 | ||||
|         public View itemView; | ||||
| 
 | ||||
|         public ViewHolder(@NonNull View itemView) { | ||||
|             this.itemView = itemView; | ||||
|             itemView.setTag(R.id.base_item_tag_view_id, this); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,48 @@ | ||||
| package com.android.base.adapter.pager; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import androidx.fragment.app.Fragment; | ||||
| import androidx.fragment.app.FragmentManager; | ||||
| import androidx.fragment.app.FragmentPagerAdapter; | ||||
| 
 | ||||
| @SuppressWarnings("unused") | ||||
| public class ViewPagerFragmentAdapter extends FragmentPagerAdapter { | ||||
| 
 | ||||
|     private final List<ViewPagerInfo> mTabs; | ||||
|     private Context mContext; | ||||
| 
 | ||||
|     public ViewPagerFragmentAdapter(FragmentManager fragmentManager, Context context) { | ||||
|         super(fragmentManager); | ||||
|         mContext = context; | ||||
|         mTabs = new ArrayList<>(); | ||||
|     } | ||||
| 
 | ||||
|     public void setDataSource(List<ViewPagerInfo> viewPagerInfoList) { | ||||
|         mTabs.clear(); | ||||
|         mTabs.addAll(viewPagerInfoList); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int getCount() { | ||||
|         return mTabs.size(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Fragment getItem(int position) { | ||||
|         ViewPagerInfo viewPagerInfo = mTabs.get(position); | ||||
|         return Fragment.instantiate(mContext, viewPagerInfo.clazz.getName(), viewPagerInfo.args); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public CharSequence getPageTitle(int position) { | ||||
|         return mTabs.get(position).title; | ||||
|     } | ||||
| 
 | ||||
|     public List<ViewPagerInfo> getTabs() { | ||||
|         return mTabs; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,19 @@ | ||||
| package com.android.base.adapter.pager; | ||||
| 
 | ||||
| import android.os.Bundle; | ||||
| 
 | ||||
| import androidx.fragment.app.Fragment; | ||||
| 
 | ||||
| @SuppressWarnings("all") | ||||
| public class ViewPagerInfo { | ||||
| 
 | ||||
|     public final Class<? extends Fragment> clazz; | ||||
|     public final Bundle args; | ||||
|     public final String title; | ||||
| 
 | ||||
|     public ViewPagerInfo(String title, Class<? extends Fragment> clazz, Bundle args) { | ||||
|         this.title = title; | ||||
|         this.clazz = clazz; | ||||
|         this.args = args; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,51 @@ | ||||
| package com.android.base.adapter.pager; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| 
 | ||||
| import org.jetbrains.annotations.NotNull; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import androidx.fragment.app.Fragment; | ||||
| import androidx.fragment.app.FragmentManager; | ||||
| import androidx.fragment.app.FragmentStatePagerAdapter; | ||||
| 
 | ||||
| public class ViewPagerStateFragmentAdapter extends FragmentStatePagerAdapter { | ||||
| 
 | ||||
|     private final List<ViewPagerInfo> mTabs; | ||||
|     private Context mContext; | ||||
| 
 | ||||
|     public ViewPagerStateFragmentAdapter(FragmentManager fragmentManager, Context context) { | ||||
|         super(fragmentManager); | ||||
|         mContext = context; | ||||
|         mTabs = new ArrayList<>(); | ||||
|     } | ||||
| 
 | ||||
|     public void setDataSource(List<ViewPagerInfo> viewPagerInfoList) { | ||||
|         mTabs.clear(); | ||||
|         mTabs.addAll(viewPagerInfoList); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int getCount() { | ||||
|         return mTabs.size(); | ||||
|     } | ||||
| 
 | ||||
|     @NotNull | ||||
|     @Override | ||||
|     public Fragment getItem(int position) { | ||||
|         ViewPagerInfo viewPagerInfo = mTabs.get(position); | ||||
|         return Fragment.instantiate(mContext, viewPagerInfo.clazz.getName(), viewPagerInfo.args); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public CharSequence getPageTitle(int position) { | ||||
|         return mTabs.get(position).title; | ||||
|     } | ||||
| 
 | ||||
|     protected List<ViewPagerInfo> getTabs() { | ||||
|         return mTabs; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,50 @@ | ||||
| package com.android.base.adapter.recycler; | ||||
| 
 | ||||
| 
 | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
| 
 | ||||
| /** | ||||
|  * @see <a href='http://www.cezcb.com/2018/08/24/PagingWithHeader/'>PagingWithHeader</a>
 | ||||
|  */ | ||||
| class AdapterDataObserverProxy extends RecyclerView.AdapterDataObserver { | ||||
| 
 | ||||
|     private RecyclerView.AdapterDataObserver adapterDataObserver; | ||||
|     private int headerCount; | ||||
| 
 | ||||
|     AdapterDataObserverProxy(RecyclerView.AdapterDataObserver adapterDataObserver, int headerCount) { | ||||
|         this.adapterDataObserver = adapterDataObserver; | ||||
|         this.headerCount = headerCount; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onChanged() { | ||||
|         adapterDataObserver.onChanged(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onItemRangeChanged(int positionStart, int itemCount) { | ||||
|         adapterDataObserver.onItemRangeChanged(positionStart + headerCount, itemCount); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) { | ||||
|         adapterDataObserver.onItemRangeChanged(positionStart + headerCount, itemCount, payload); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onItemRangeInserted(int positionStart, int itemCount) { | ||||
|         adapterDataObserver.onItemRangeInserted(positionStart + headerCount, itemCount); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onItemRangeRemoved(int positionStart, int itemCount) { | ||||
|         adapterDataObserver.onItemRangeRemoved(positionStart + headerCount, itemCount); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { | ||||
|         super.onItemRangeMoved(fromPosition + headerCount, toPosition + headerCount, itemCount); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,266 @@ | ||||
| package com.android.base.adapter.recycler; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.view.ViewGroup; | ||||
| 
 | ||||
| import com.android.base.adapter.DataManager; | ||||
| import com.android.base.utils.common.Checker; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.concurrent.Executor; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.recyclerview.widget.AdapterListUpdateCallback; | ||||
| import androidx.recyclerview.widget.AsyncDifferConfig; | ||||
| import androidx.recyclerview.widget.AsyncListDiffer; | ||||
| import androidx.recyclerview.widget.DiffUtil; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
| 
 | ||||
| /** | ||||
|  * RecyclerView 的适配器,注意:  只有{@link #setDataSource(List, boolean)}才能替换原有数据源的引用。 | ||||
|  * | ||||
|  * @param <T> 当前列表使用的数据类型 | ||||
|  * @author Ztiany | ||||
|  * date :    2015-05-11 22:38 | ||||
|  * email:    1169654504@qq.com | ||||
|  */ | ||||
| @SuppressWarnings("unused") | ||||
| public abstract class DiffRecyclerAdapter<T, VH extends ViewHolder> extends RecyclerView.Adapter<VH> implements DataManager<T> { | ||||
| 
 | ||||
|     @NonNull | ||||
|     private Context mContext; | ||||
| 
 | ||||
|     private AsyncListDiffer<T> mAsyncListDiffer; | ||||
| 
 | ||||
|     private final int mHeaderCount; | ||||
| 
 | ||||
|     @SuppressWarnings("WeakerAccess") | ||||
|     public DiffRecyclerAdapter(@NonNull Context context, @NonNull DiffUtil.ItemCallback<T> itemCallback, @Nullable Executor executor, int headerCount) { | ||||
|         mContext = context; | ||||
|         mHeaderCount = headerCount; | ||||
| 
 | ||||
|         AsyncDifferConfig.Builder<T> tBuilder = new AsyncDifferConfig.Builder<>(itemCallback); | ||||
|         if (executor != null) { | ||||
|             tBuilder.setBackgroundThreadExecutor(executor); | ||||
|         } | ||||
|         AsyncDifferConfig<T> differConfig = tBuilder.build(); | ||||
| 
 | ||||
|         mAsyncListDiffer = new AsyncListDiffer<>(new AdapterListUpdateCallback(this), differConfig); | ||||
|     } | ||||
| 
 | ||||
|     public DiffRecyclerAdapter(@NonNull Context context, @NonNull DiffUtil.ItemCallback<T> itemCallback, int headerCount) { | ||||
|         this(context, itemCallback, null, headerCount); | ||||
|     } | ||||
| 
 | ||||
|     public DiffRecyclerAdapter(@NonNull Context context, @NonNull DiffUtil.ItemCallback<T> itemCallback) { | ||||
|         this(context, itemCallback, null, 0); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void registerAdapterDataObserver(@NonNull RecyclerView.AdapterDataObserver observer) { | ||||
|         if (mHeaderCount != 0) { | ||||
|             super.registerAdapterDataObserver(new AdapterDataObserverProxy(observer, mHeaderCount)); | ||||
|         } else { | ||||
|             super.registerAdapterDataObserver(observer); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public Context getContext() { | ||||
|         return mContext; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int getItemCount() { | ||||
|         return getDataSize(); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType); | ||||
| 
 | ||||
|     @Override | ||||
|     public void onBindViewHolder(@NonNull VH holder, int position, @NonNull List<Object> payloads) { | ||||
|         super.onBindViewHolder(holder, position, payloads); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public abstract void onBindViewHolder(@NonNull VH viewHolder, int position); | ||||
| 
 | ||||
|     public void notifyEntryChanged(T t) { | ||||
|         int itemPosition = indexItem(t); | ||||
|         if (itemPosition != -1) { | ||||
|             notifyItemChanged(itemPosition); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void add(T element) { | ||||
|         List<T> newList = newList(); | ||||
|         newList.add(element); | ||||
|         mAsyncListDiffer.submitList(newList); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addAt(int location, T element) { | ||||
|         if (location > getDataSize()) { | ||||
|             location = getDataSize(); | ||||
|         } | ||||
|         List<T> newList = newList(); | ||||
|         newList.add(location, element); | ||||
|         mAsyncListDiffer.submitList(newList); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addItems(List<T> elements) { | ||||
|         if (Checker.isEmpty(elements)) { | ||||
|             return; | ||||
|         } | ||||
|         List<T> newList = newList(); | ||||
|         newList.addAll(elements); | ||||
|         mAsyncListDiffer.submitList(newList); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addItemsChecked(List<T> elements) { | ||||
|         if (Checker.isEmpty(elements)) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         List<T> newList = newList(); | ||||
| 
 | ||||
|         for (T element : elements) { | ||||
|             if (element == null) { | ||||
|                 continue; | ||||
|             } | ||||
|             newList.remove(element); | ||||
|         } | ||||
| 
 | ||||
|         newList.addAll(elements); | ||||
|         mAsyncListDiffer.submitList(newList); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addItemsAt(int location, List<T> elements) { | ||||
|         if (Checker.isEmpty(elements)) { | ||||
|             return; | ||||
|         } | ||||
|         List<T> newList = newList(); | ||||
| 
 | ||||
|         if (location > newList.size()) { | ||||
|             location = newList.size(); | ||||
|         } | ||||
| 
 | ||||
|         newList.addAll(location, elements); | ||||
|         mAsyncListDiffer.submitList(newList); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void replace(T oldElement, T newElement) { | ||||
|         if (!contains(oldElement)) { | ||||
|             return; | ||||
|         } | ||||
|         List<T> newList = newList(); | ||||
|         newList.set(newList.indexOf(oldElement), newElement); | ||||
|         mAsyncListDiffer.submitList(newList); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void replaceAt(int index, T element) { | ||||
|         if (getDataSize() > index) { | ||||
|             List<T> newList = newList(); | ||||
|             newList.set(index, element); | ||||
|             mAsyncListDiffer.submitList(newList); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void replaceAll(List<T> elements) { | ||||
|         List<T> newList = new ArrayList<>(elements); | ||||
|         mAsyncListDiffer.submitList(newList); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void remove(T element) { | ||||
|         if (contains(element)) { | ||||
|             List<T> newList = newList(); | ||||
|             newList.remove(element); | ||||
|             mAsyncListDiffer.submitList(newList); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void removeItems(List<T> elements) { | ||||
|         if (Checker.isEmpty(elements) || isEmpty() || !getItems().containsAll(elements)) { | ||||
|             return; | ||||
|         } | ||||
|         List<T> newList = newList(); | ||||
|         newList.removeAll(elements); | ||||
|         mAsyncListDiffer.submitList(newList); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void removeItems(List<T> elements, boolean isSuccessive) { | ||||
|         removeItems(elements); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void removeAt(int index) { | ||||
|         if (getDataSize() > index) { | ||||
|             List<T> newList = newList(); | ||||
|             newList.remove(index); | ||||
|             mAsyncListDiffer.submitList(newList); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public T getItem(int position) { | ||||
|         return getDataSize() > position ? getItems().get(position) : null; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public final int getDataSize() { | ||||
|         return getItems() == null ? 0 : getItems().size(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean contains(T element) { | ||||
|         return !isEmpty() && getItems().contains(element); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void clear() { | ||||
|         mAsyncListDiffer.submitList(null); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void setDataSource(List<T> elements, boolean notifyDataSetChanged) { | ||||
|         mAsyncListDiffer.submitList(elements); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public List<T> getItems() { | ||||
|         return mAsyncListDiffer.getCurrentList(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean isEmpty() { | ||||
|         return getDataSize() == 0; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int indexItem(T t) { | ||||
|         return isEmpty() ? -1 : getItems().indexOf(t); | ||||
|     } | ||||
| 
 | ||||
|     private List<T> newList() { | ||||
|         if (getItems() == null) { | ||||
|             return new ArrayList<>(); | ||||
|         } else { | ||||
|             return new ArrayList<>(getItems()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,5 @@ | ||||
| package com.android.base.adapter.recycler; | ||||
| 
 | ||||
| public interface HeaderSize { | ||||
|       int getHeaderSize(); | ||||
| } | ||||
| @ -0,0 +1,38 @@ | ||||
| package com.android.base.adapter.recycler | ||||
| 
 | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import androidx.recyclerview.widget.RecyclerView | ||||
| 
 | ||||
| /** | ||||
|  * @author Ztiany | ||||
|  * Email: 1169654504@qq.com | ||||
|  * Date : 2017-09-13 15:33 | ||||
|  */ | ||||
| abstract class ItemViewBinder<T, VH : RecyclerView.ViewHolder> : com.drakeet.multitype.ItemViewBinder<T, VH>() { | ||||
| 
 | ||||
|     protected val dataManager: MultiTypeAdapter | ||||
|         get() = adapter as MultiTypeAdapter | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| abstract class SimpleItemViewBinder<T> : ItemViewBinder<T, KtViewHolder>() { | ||||
| 
 | ||||
|     override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): KtViewHolder { | ||||
|         val layout = provideLayout(inflater, parent) | ||||
|         val itemView = if (layout is Int) { | ||||
|             inflater.inflate(layout, parent, false) | ||||
|         } else | ||||
|             layout as View | ||||
|         return KtViewHolder(itemView).apply { | ||||
|             onViewHolderCreated(this) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected open fun onViewHolderCreated(viewHolder: KtViewHolder) = Unit | ||||
| 
 | ||||
|     /**provide a layout id or a View*/ | ||||
|     abstract fun provideLayout(inflater: LayoutInflater, parent: ViewGroup): Any | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,14 @@ | ||||
| package com.android.base.adapter.recycler | ||||
| 
 | ||||
| import android.view.View | ||||
| import kotlinx.android.extensions.CacheImplementation | ||||
| import kotlinx.android.extensions.ContainerOptions | ||||
| import kotlinx.android.extensions.LayoutContainer | ||||
| 
 | ||||
| /** | ||||
|  *@author Ztiany | ||||
|  *      Email: ztiany3@gmail.com | ||||
|  *      Date : 2018-11-12 17:07 | ||||
|  */ | ||||
| @ContainerOptions(cache = CacheImplementation.SPARSE_ARRAY) | ||||
| open class KtViewHolder(override val containerView: View) : ViewHolder(containerView), LayoutContainer | ||||
| @ -0,0 +1,169 @@ | ||||
| package com.android.base.adapter.recycler; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| 
 | ||||
| import com.android.base.adapter.DataManager; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * @see <a href='https://github.com/drakeet/MultiType'>drakeet/MultiTypeAdapter</a>
 | ||||
|  */ | ||||
| public class MultiTypeAdapter extends com.drakeet.multitype.MultiTypeAdapter implements DataManager<Object> { | ||||
| 
 | ||||
|     private final Context mContext; | ||||
| 
 | ||||
|     private RecyclerDataManagerImpl<Object> mRecyclerDataManager; | ||||
| 
 | ||||
|     public MultiTypeAdapter(Context context) { | ||||
|         super(); | ||||
|         mContext = context; | ||||
|         ArrayList<Object> objects = new ArrayList<>(); | ||||
|         mRecyclerDataManager = new RecyclerDataManagerImpl<>(objects, this); | ||||
|         super.setItems(objects); | ||||
|     } | ||||
| 
 | ||||
|     public MultiTypeAdapter(Context context, @NonNull List<?> items) { | ||||
|         super(); | ||||
|         mContext = context; | ||||
|         ArrayList<Object> objects = new ArrayList<>(items); | ||||
|         mRecyclerDataManager = new RecyclerDataManagerImpl<>(objects, this); | ||||
|         super.setItems(objects); | ||||
|     } | ||||
| 
 | ||||
|     public MultiTypeAdapter(Context context, @NonNull List<?> items, int initialCapacity) { | ||||
|         super(items, initialCapacity); | ||||
|         mContext = context; | ||||
|         ArrayList<Object> objects = new ArrayList<>(items); | ||||
|         mRecyclerDataManager = new RecyclerDataManagerImpl<>(objects, this); | ||||
|         super.setItems(objects); | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("unused") | ||||
|     public void notifyEntryChanged(Object entry) { | ||||
|         int itemPosition = indexItem(entry); | ||||
|         if (itemPosition != -1) { | ||||
|             notifyItemChanged(itemPosition); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void add(Object elem) { | ||||
|         mRecyclerDataManager.add(elem); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addAt(int location, Object elem) { | ||||
|         mRecyclerDataManager.addAt(location, elem); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addItems(List<Object> elements) { | ||||
|         mRecyclerDataManager.addItems(elements); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addItemsChecked(List<Object> elements) { | ||||
|         mRecyclerDataManager.addItemsChecked(elements); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addItemsAt(int location, List<Object> elements) { | ||||
|         mRecyclerDataManager.addItemsAt(location, elements); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void replace(Object oldElem, Object newElem) { | ||||
|         mRecyclerDataManager.replace(oldElem, newElem); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void replaceAt(int index, Object elem) { | ||||
|         mRecyclerDataManager.replaceAt(index, elem); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void replaceAll(List<Object> elements) { | ||||
|         mRecyclerDataManager.replaceAll(elements); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void setDataSource(@NonNull List<Object> newDataSource, boolean notifyDataSetChanged) { | ||||
|         super.setItems(newDataSource); | ||||
|         mRecyclerDataManager.setDataSource(newDataSource, notifyDataSetChanged); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void remove(Object elem) { | ||||
|         mRecyclerDataManager.remove(elem); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void removeAt(int index) { | ||||
|         mRecyclerDataManager.removeAt(index); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void removeItems(List<Object> elements) { | ||||
|         mRecyclerDataManager.removeItems(elements); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void removeItems(List<Object> elements, boolean isSuccessive) { | ||||
|         mRecyclerDataManager.removeItems(elements, isSuccessive); | ||||
|     } | ||||
| 
 | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public Object getItem(int position) { | ||||
|         return mRecyclerDataManager.getItem(position); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int getDataSize() { | ||||
|         return mRecyclerDataManager.getDataSize(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean contains(Object elem) { | ||||
|         return mRecyclerDataManager.contains(elem); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean isEmpty() { | ||||
|         return mRecyclerDataManager.isEmpty(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void clear() { | ||||
|         mRecyclerDataManager.clear(); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public List<Object> getItems() { | ||||
|         return mRecyclerDataManager.getItems(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int indexItem(Object o) { | ||||
|         return mRecyclerDataManager.indexItem(o); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void setItems(@NonNull List<?> items) { | ||||
|         ArrayList<Object> objects = new ArrayList<>(items); | ||||
|         super.setItems(objects); | ||||
|         mRecyclerDataManager.setDataSource(objects, true); | ||||
|     } | ||||
| 
 | ||||
|     public Context getContext() { | ||||
|         return mContext; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,177 @@ | ||||
| package com.android.base.adapter.recycler; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.view.ViewGroup; | ||||
| 
 | ||||
| import com.android.base.adapter.DataManager; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
| 
 | ||||
| /** | ||||
|  * RecyclerView 的适配器 | ||||
|  * 注意:  只有setDataSource才能替换原有数据源的引用。 | ||||
|  * | ||||
|  * @param <T> 当前列表使用的数据类型 | ||||
|  * @author Ztiany | ||||
|  * date :    2015-05-11 22:38 | ||||
|  * email:    1169654504@qq.com | ||||
|  */ | ||||
| public abstract class RecyclerAdapter<T, VH extends ViewHolder> extends RecyclerView.Adapter<VH> implements DataManager<T> { | ||||
| 
 | ||||
|     private RecyclerDataManagerImpl<T> mDataManager; | ||||
| 
 | ||||
|     @NonNull | ||||
|     private final Context mContext; | ||||
| 
 | ||||
|     public RecyclerAdapter(@NonNull Context context, List<T> data) { | ||||
|         mDataManager = new RecyclerDataManagerImpl<>(data, this); | ||||
|         this.mContext = context; | ||||
|     } | ||||
| 
 | ||||
|     public RecyclerAdapter(@NonNull Context context) { | ||||
|         this(context, null); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public Context getContext() { | ||||
|         return mContext; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int getItemCount() { | ||||
|         return getDataSize(); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType); | ||||
| 
 | ||||
|     @Override | ||||
|     public void onBindViewHolder(@NonNull VH holder, int position, @NonNull List<Object> payloads) { | ||||
|         super.onBindViewHolder(holder, position, payloads); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public abstract void onBindViewHolder(@NonNull VH viewHolder, int position); | ||||
| 
 | ||||
|     public void notifyEntryChanged(T t) { | ||||
|         int itemPosition = indexItem(t); | ||||
|         if (itemPosition != -1) { | ||||
|             notifyItemChanged(itemPosition); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ///////////////////////////////////////////////////////////////////////////
 | ||||
|     // DataManager
 | ||||
|     ///////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
|     @Override | ||||
|     public void add(T elem) { | ||||
|         mDataManager.add(elem); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addAt(int location, T elem) { | ||||
|         mDataManager.addAt(location, elem); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addItems(List<T> elements) { | ||||
|         mDataManager.addItems(elements); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addItemsChecked(List<T> elements) { | ||||
|         mDataManager.addItemsChecked(elements); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addItemsAt(int location, List<T> elements) { | ||||
|         mDataManager.addItemsAt(location, elements); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void replace(T oldElem, T newElem) { | ||||
|         mDataManager.replace(oldElem, newElem); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void replaceAt(int index, T elem) { | ||||
|         mDataManager.replaceAt(index, elem); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void replaceAll(List<T> elements) { | ||||
|         mDataManager.replaceAll(elements); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void remove(T elem) { | ||||
|         mDataManager.remove(elem); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void removeItems(List<T> elements) { | ||||
|         mDataManager.removeItems(elements); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void removeItems(List<T> elements, boolean isSuccessive) { | ||||
|         mDataManager.removeItems(elements, isSuccessive); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void removeAt(int index) { | ||||
|         mDataManager.removeAt(index); | ||||
|     } | ||||
| 
 | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public T getItem(int position) { | ||||
|         return mDataManager.getItem(position); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public final int getDataSize() { | ||||
|         return mDataManager.getDataSize(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean contains(T elem) { | ||||
|         return mDataManager.contains(elem); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void clear() { | ||||
|         mDataManager.clear(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void setDataSource(List<T> elements, boolean notifyDataSetChanged) { | ||||
|         mDataManager.setDataSource(elements, notifyDataSetChanged); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public List<T> getItems() { | ||||
|         return mDataManager.getItems(); | ||||
|     } | ||||
| 
 | ||||
|     protected final void setHeaderSize(HeaderSize headerSize) { | ||||
|         mDataManager.setHeaderSize(headerSize); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean isEmpty() { | ||||
|         return mDataManager.isEmpty(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int indexItem(T t) { | ||||
|         return mDataManager.indexItem(t); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,272 @@ | ||||
| package com.android.base.adapter.recycler; | ||||
| 
 | ||||
| 
 | ||||
| import com.android.base.adapter.DataManager; | ||||
| import com.android.base.utils.common.Checker; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
| 
 | ||||
| 
 | ||||
| final class RecyclerDataManagerImpl<T> implements DataManager<T> { | ||||
| 
 | ||||
|     private List<T> mData; | ||||
|     private RecyclerView.Adapter mAdapter; | ||||
|     private HeaderSize mHeaderSize; | ||||
| 
 | ||||
|     RecyclerDataManagerImpl(List<T> tList, RecyclerView.Adapter adapter) { | ||||
|         mData = tList; | ||||
|         mAdapter = adapter; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean isEmpty() { | ||||
|         return getDataSize() == 0; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void add(T element) { | ||||
|         addAt(getDataSize(), element); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addAt(int location, T element) { | ||||
|         if (mData != null) { | ||||
|             if (mData.isEmpty()) { | ||||
|                 mData.add(element); | ||||
|                 notifyItemInserted(getHeaderSize()); | ||||
|             } else { | ||||
|                 int size = mData.size(); | ||||
|                 int lastIndex = (location >= size ? size : location) + getHeaderSize(); | ||||
|                 mData.add(location, element); | ||||
|                 notifyItemInserted(lastIndex); | ||||
|             } | ||||
|         } else { | ||||
|             mData = new ArrayList<>(); | ||||
|             mData.add(element); | ||||
|             notifyDataSetChanged(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addItems(List<T> elements) { | ||||
|         addItemsAt(getDataSize(), elements); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addItemsChecked(List<T> elements) { | ||||
|         if (Checker.isEmpty(elements)) { | ||||
|             return; | ||||
|         } | ||||
|         if (mData == null) { | ||||
|             addItems(elements); | ||||
|             return; | ||||
|         } | ||||
|         boolean hasRemovedElements = false; | ||||
|         for (T element : elements) { | ||||
|             if (element == null) { | ||||
|                 continue; | ||||
|             } | ||||
|             if (mData.contains(element)) { | ||||
|                 mData.remove(element); | ||||
|                 if (!hasRemovedElements) { | ||||
|                     hasRemovedElements = true; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if (hasRemovedElements) { | ||||
|             mData.addAll(elements); | ||||
|             notifyDataSetChanged(); | ||||
|         } else { | ||||
|             addItems(elements); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void addItemsAt(int location, List<T> elements) { | ||||
|         if (Checker.isEmpty(elements)) { | ||||
|             return; | ||||
|         } | ||||
|         if (this.mData != null) { | ||||
| 
 | ||||
|             if (mData.isEmpty()) { | ||||
|                 mData.addAll(elements); | ||||
|                 notifyItemRangeInserted(getHeaderSize(), elements.size()); | ||||
|             } else { | ||||
|                 int size = mData.size(); | ||||
|                 int lastIndex = (location >= size ? size : location) + getHeaderSize(); | ||||
|                 this.mData.addAll(location, elements); | ||||
|                 notifyItemRangeInserted(lastIndex, elements.size()); | ||||
|             } | ||||
| 
 | ||||
|         } else { | ||||
|             this.mData = new ArrayList<>(elements.size()); | ||||
|             mData.addAll(elements); | ||||
|             notifyDataSetChanged(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void replace(T oldElem, T newElem) { | ||||
|         if (mData != null && mData.contains(oldElem)) { | ||||
|             replaceAt(mData.indexOf(oldElem), newElem); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void replaceAt(int index, T element) { | ||||
|         if (getDataSize() > index) { | ||||
|             mData.set(index, element); | ||||
|             notifyItemChanged(index + getHeaderSize()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void replaceAll(List<T> elements) { | ||||
|         if (mData == null) { | ||||
|             mData = new ArrayList<>(); | ||||
|         } | ||||
|         mData.clear(); | ||||
|         if (elements != null) { | ||||
|             mData.addAll(elements); | ||||
|         } | ||||
|         notifyDataSetChanged(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void setDataSource(List<T> newDataSource, boolean notifyDataSetChanged) { | ||||
|         mData = newDataSource; | ||||
|         if (notifyDataSetChanged) { | ||||
|             notifyDataSetChanged(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void remove(T element) { | ||||
|         if (mData == null || mData.isEmpty()) { | ||||
|             return; | ||||
|         } | ||||
|         if (mData.contains(element)) { | ||||
|             int index = mData.indexOf(element) + getHeaderSize(); | ||||
|             mData.remove(element); | ||||
|             notifyItemRemoved(index); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void removeAt(int index) { | ||||
|         if (getDataSize() > index) { | ||||
|             mData.remove(index); | ||||
|             notifyItemRemoved(index + getHeaderSize()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void removeItems(List<T> elements) { | ||||
|         if (!Checker.isEmpty(elements) && mData != null && mData.containsAll(elements)) { | ||||
|             mData.removeAll(elements); | ||||
|             notifyDataSetChanged(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void removeItems(List<T> elements, boolean isSuccessive) { | ||||
|         if (Checker.isEmpty(elements) || Checker.isEmpty(mData)) { | ||||
|             return; | ||||
|         } | ||||
|         if (!isSuccessive) { | ||||
|             removeItems(elements); | ||||
|             return; | ||||
|         } | ||||
|         T data = elements.get(0); | ||||
|         int index = mData.indexOf(data); | ||||
|         if (index == -1) { | ||||
|             removeItems(elements); | ||||
|         } else { | ||||
|             mData.removeAll(elements); | ||||
|             notifyItemRangeRemoved(index, elements.size()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public T getItem(int position) { | ||||
|         position = position - getHeaderSize(); | ||||
|         if (position >= 0 && getDataSize() > position) { | ||||
|             return mData.get(position); | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public final int getDataSize() { | ||||
|         return mData == null ? 0 : mData.size(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean contains(T element) { | ||||
|         return mData != null && mData.contains(element); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void clear() { | ||||
|         if (mData != null) { | ||||
|             mData.clear(); | ||||
|             notifyDataSetChanged(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int indexItem(T t) { | ||||
|         List<T> items = getItems(); | ||||
|         if (items == null) { | ||||
|             return -1; | ||||
|         } | ||||
|         return items.indexOf(t); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public List<T> getItems() { | ||||
|         return mData; | ||||
|     } | ||||
| 
 | ||||
|     ///////////////////////////////////////////////////////////////////////////
 | ||||
|     // Size
 | ||||
|     ///////////////////////////////////////////////////////////////////////////
 | ||||
|     private int getHeaderSize() { | ||||
|         return mHeaderSize == null ? 0 : mHeaderSize.getHeaderSize(); | ||||
|     } | ||||
| 
 | ||||
|     ///////////////////////////////////////////////////////////////////////////
 | ||||
|     // Adapter Call
 | ||||
|     ///////////////////////////////////////////////////////////////////////////
 | ||||
|     private void notifyItemChanged(int position) { | ||||
|         mAdapter.notifyItemChanged(position); | ||||
|     } | ||||
| 
 | ||||
|     private void notifyDataSetChanged() { | ||||
|         mAdapter.notifyDataSetChanged(); | ||||
|     } | ||||
| 
 | ||||
|     private void notifyItemInserted(int position) { | ||||
|         mAdapter.notifyItemInserted(position); | ||||
|     } | ||||
| 
 | ||||
|     private void notifyItemRangeInserted(int position, int size) { | ||||
|         mAdapter.notifyItemRangeInserted(position, size); | ||||
|     } | ||||
| 
 | ||||
|     private void notifyItemRemoved(int index) { | ||||
|         mAdapter.notifyItemRemoved(index); | ||||
|     } | ||||
| 
 | ||||
|     private void notifyItemRangeRemoved(int index, int size) { | ||||
|         mAdapter.notifyItemRangeRemoved(index, size); | ||||
|     } | ||||
| 
 | ||||
|     void setHeaderSize(HeaderSize headerSize) { | ||||
|         mHeaderSize = headerSize; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,41 @@ | ||||
| package com.android.base.adapter.recycler | ||||
| 
 | ||||
| import android.content.Context | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| 
 | ||||
| /** | ||||
|  * A simple way to build a simple list. If you want to build a multi type list, perhaps you need to use [MultiTypeAdapter]. | ||||
|  * | ||||
|  * @author Ztiany | ||||
|  * Email: ztiany3@gmail.com | ||||
|  * Date : 2019-01-15 11:41 | ||||
|  */ | ||||
| abstract class SimpleRecyclerAdapter<T>(context: Context, data: List<T>? = null) : RecyclerAdapter<T, KtViewHolder>(context, data) { | ||||
| 
 | ||||
|     private var layoutInflater: LayoutInflater = LayoutInflater.from(context) | ||||
| 
 | ||||
|     final override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): KtViewHolder { | ||||
|         val layout = provideLayout(parent, viewType) | ||||
|         val itemView = if (layout is Int) { | ||||
|             layoutInflater.inflate(layout, parent, false) | ||||
|         } else | ||||
|             layout as View | ||||
|         return KtViewHolder(itemView).apply { | ||||
|             onViewHolderCreated(this) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected open fun onViewHolderCreated(viewHolder: KtViewHolder) = Unit | ||||
| 
 | ||||
|     /**provide a layout id or a View*/ | ||||
|     abstract fun provideLayout(parent: ViewGroup, viewType: Int): Any | ||||
| 
 | ||||
|     override fun onBindViewHolder(viewHolder: KtViewHolder, position: Int) { | ||||
|         bind(viewHolder, getItem(position) ?: throw NullPointerException("SimpleRecyclerAdapter onBindViewHolder getItem return null")) | ||||
|     } | ||||
| 
 | ||||
|     protected abstract fun bind(viewHolder: KtViewHolder, item: T) | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,21 @@ | ||||
| package com.android.base.adapter.recycler; | ||||
| 
 | ||||
| import android.view.View; | ||||
| 
 | ||||
| import com.android.base.adapter.ItemHelper; | ||||
| 
 | ||||
| 
 | ||||
| public class SmartViewHolder extends ViewHolder { | ||||
| 
 | ||||
|     private final ItemHelper mHelper; | ||||
| 
 | ||||
|     public SmartViewHolder(View itemView) { | ||||
|         super(itemView); | ||||
|         mHelper = new ItemHelper(itemView); | ||||
|     } | ||||
| 
 | ||||
|     public ItemHelper helper() { | ||||
|         return mHelper; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,29 @@ | ||||
| package com.android.base.adapter.recycler; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.view.View; | ||||
| 
 | ||||
| import androidx.annotation.IdRes; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
| 
 | ||||
| 
 | ||||
| public class ViewHolder extends RecyclerView.ViewHolder { | ||||
| 
 | ||||
|     public ViewHolder(View itemView) { | ||||
|         super(itemView); | ||||
|     } | ||||
| 
 | ||||
|     protected Context getContext() { | ||||
|         return itemView.getContext(); | ||||
|     } | ||||
| 
 | ||||
|     public <V extends View> V findView(@IdRes int viewId) { | ||||
|         return itemView.findViewById(viewId); | ||||
|     } | ||||
| 
 | ||||
|     public ViewHolder setItemClickListener(View.OnClickListener onClickListener) { | ||||
|         itemView.setOnClickListener(onClickListener); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,67 @@ | ||||
| package com.android.base.app | ||||
| 
 | ||||
| import android.app.Activity | ||||
| import android.content.Context | ||||
| import android.os.Bundle | ||||
| import androidx.fragment.app.Fragment | ||||
| import androidx.fragment.app.FragmentActivity | ||||
| import androidx.fragment.app.FragmentManager | ||||
| import com.android.base.app.activity.ActivityDelegateOwner | ||||
| import com.android.base.app.dagger.Injectable | ||||
| import com.android.base.app.fragment.delegates.FragmentDelegateOwner | ||||
| import com.android.base.interfaces.ActivityLifecycleCallbacksAdapter | ||||
| import dagger.android.AndroidInjection | ||||
| import dagger.android.support.AndroidSupportInjection | ||||
| 
 | ||||
| internal class AndroidComponentLifecycleInjector : ActivityLifecycleCallbacksAdapter { | ||||
| 
 | ||||
|     private var isAutoInjectEnable = false | ||||
| 
 | ||||
|     var delegateInjector: DelegateInjector? = null | ||||
| 
 | ||||
|     fun enableAutoInject() { | ||||
|         isAutoInjectEnable = true | ||||
|     } | ||||
| 
 | ||||
|     override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) { | ||||
|         handleAutoInjectActivity(activity) | ||||
|         handleAutoInstallActivityDelegate(activity) | ||||
|         if (activity is FragmentActivity) { | ||||
|             injectFragmentLifecycle(activity) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun injectFragmentLifecycle(activity: FragmentActivity) { | ||||
|         activity.supportFragmentManager.registerFragmentLifecycleCallbacks(object : FragmentManager.FragmentLifecycleCallbacks() { | ||||
|             override fun onFragmentAttached(fm: FragmentManager, fragment: Fragment, context: Context) { | ||||
|                 handleAutoInjectFragment(fragment) | ||||
|                 handleAutoInstallFragmentDelegate(fragment) | ||||
|             } | ||||
|         }, true) | ||||
|     } | ||||
| 
 | ||||
|     private fun handleAutoInstallFragmentDelegate(fragment: Fragment) { | ||||
|         if (fragment is FragmentDelegateOwner) { | ||||
|             delegateInjector?.injectFragmentDelegate(fragment) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun handleAutoInstallActivityDelegate(activity: Activity?) { | ||||
|         if (activity is ActivityDelegateOwner) { | ||||
|             delegateInjector?.injectActivityDelegate(activity) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun handleAutoInjectActivity(activity: Activity?) { | ||||
|         if (isAutoInjectEnable && activity is Injectable) { | ||||
|             AndroidInjection.inject(activity) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun handleAutoInjectFragment(fragment: Fragment) { | ||||
|         if (isAutoInjectEnable && fragment is Injectable) { | ||||
|             AndroidSupportInjection.inject(fragment) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,80 @@ | ||||
| package com.android.base.app | ||||
| 
 | ||||
| import android.app.Activity | ||||
| import android.app.Application | ||||
| import android.content.Context | ||||
| import android.content.IntentFilter | ||||
| import android.content.res.Configuration | ||||
| import android.net.ConnectivityManager | ||||
| import com.android.base.app.utils.CrashHandler | ||||
| import com.android.base.receiver.NetStateReceiver | ||||
| import com.android.base.utils.BaseUtils | ||||
| import com.blankj.utilcode.util.AppUtils | ||||
| import com.blankj.utilcode.util.Utils.OnAppStatusChangedListener | ||||
| import io.reactivex.processors.BehaviorProcessor | ||||
| import timber.log.Timber | ||||
| import java.util.concurrent.atomic.AtomicBoolean | ||||
| 
 | ||||
| /** | ||||
|  * @author Ztiany | ||||
|  * Email: ztiany3@gmail.com | ||||
|  * Date : 2018-10-12 18:19 | ||||
|  */ | ||||
| internal class ApplicationDelegate internal constructor(private val androidComponentLifecycleInjector: AndroidComponentLifecycleInjector) { | ||||
| 
 | ||||
|     lateinit var application: Application | ||||
| 
 | ||||
|     private lateinit var crashHandler: CrashHandler | ||||
| 
 | ||||
|     /** 获取可观察的 app 生命周期  */ | ||||
|     val appStatus: BehaviorProcessor<Boolean> = BehaviorProcessor.create() | ||||
| 
 | ||||
|     private val onCreateCalled = AtomicBoolean(false) | ||||
|     private val onAttachBaseCalled = AtomicBoolean(false) | ||||
| 
 | ||||
|     fun attachBaseContext(base: Context) { | ||||
|         check(onAttachBaseCalled.compareAndSet(false, true)) { "Can only be called once" } | ||||
|     } | ||||
| 
 | ||||
|     fun onCreate(application: Application) { | ||||
|         check(onCreateCalled.compareAndSet(false, true)) { "Can only be called once" } | ||||
|         this.application = application | ||||
|         //工具类初始化 | ||||
|         BaseUtils.init(application) | ||||
|         //异常日志记录 | ||||
|         crashHandler = CrashHandler.register(application) | ||||
|         //网络状态 | ||||
|         application.registerReceiver(NetStateReceiver(), IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)) | ||||
|         //App前台后台 | ||||
|         listenActivityLifecycleCallbacks() | ||||
|         //声明周期回调 | ||||
|         application.registerActivityLifecycleCallbacks(androidComponentLifecycleInjector) | ||||
|     } | ||||
| 
 | ||||
|     fun onTerminate() {} | ||||
| 
 | ||||
|     fun onConfigurationChanged(newConfig: Configuration) {} | ||||
| 
 | ||||
|     fun onTrimMemory(level: Int) {} | ||||
| 
 | ||||
|     fun onLowMemory() {} | ||||
| 
 | ||||
|     private fun listenActivityLifecycleCallbacks() { | ||||
|         AppUtils.registerAppStatusChangedListener(object : OnAppStatusChangedListener { | ||||
|             override fun onBackground(activity: Activity?) { | ||||
|                 Timber.d("app进入后台") | ||||
|                 appStatus.onNext(false) | ||||
|             } | ||||
| 
 | ||||
|             override fun onForeground(activity: Activity?) { | ||||
|                 Timber.d("app进入前台") | ||||
|                 appStatus.onNext(true) | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     internal fun setCrashProcessor(crashProcessor: CrashProcessor) { | ||||
|         crashHandler.setCrashProcessor(crashProcessor) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,55 @@ | ||||
| package com.android.base.app | ||||
| 
 | ||||
| import android.app.Application | ||||
| import android.content.Context | ||||
| import android.content.res.Configuration | ||||
| import dagger.android.DispatchingAndroidInjector | ||||
| import dagger.android.HasAndroidInjector | ||||
| import javax.inject.Inject | ||||
| 
 | ||||
| open class BaseAppContext : Application() { | ||||
| 
 | ||||
|     override fun attachBaseContext(base: Context) { | ||||
|         super.attachBaseContext(base) | ||||
|         Sword.applicationDelegate.attachBaseContext(base) | ||||
|     } | ||||
| 
 | ||||
|     override fun onCreate() { | ||||
|         super.onCreate() | ||||
|         Sword.applicationDelegate.onCreate(this) | ||||
|     } | ||||
| 
 | ||||
|     override fun onLowMemory() { | ||||
|         super.onLowMemory() | ||||
|         Sword.applicationDelegate.onLowMemory() | ||||
|     } | ||||
| 
 | ||||
|     override fun onTrimMemory(level: Int) { | ||||
|         super.onTrimMemory(level) | ||||
|         Sword.applicationDelegate.onTrimMemory(level) | ||||
|     } | ||||
| 
 | ||||
|     override fun onConfigurationChanged(newConfig: Configuration) { | ||||
|         super.onConfigurationChanged(newConfig) | ||||
|         Sword.applicationDelegate.onConfigurationChanged(newConfig) | ||||
|     } | ||||
| 
 | ||||
|     override fun onTerminate() { | ||||
|         super.onTerminate() | ||||
|         Sword.applicationDelegate.onTerminate() | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  *@author Ztiany | ||||
|  *      Email: ztiany3@gmail.com | ||||
|  *      Date : 2019-10-12 10:59 | ||||
|  */ | ||||
| open class InjectorBaseAppContext : BaseAppContext(), HasAndroidInjector { | ||||
| 
 | ||||
|     @Inject lateinit var androidInjector: DispatchingAndroidInjector<Any> | ||||
| 
 | ||||
|     override fun androidInjector() = androidInjector | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,133 @@ | ||||
| package com.android.base.app | ||||
| 
 | ||||
| import android.app.Activity | ||||
| import android.content.Context | ||||
| import com.android.base.app.activity.ActivityDelegateOwner | ||||
| import com.android.base.app.fragment.animator.FragmentAnimator | ||||
| import com.android.base.app.fragment.delegates.FragmentDelegateOwner | ||||
| import com.android.base.app.fragment.tools.FragmentConfig | ||||
| import com.android.base.app.ui.LoadingView | ||||
| import com.android.base.app.ui.PageNumber | ||||
| import com.android.base.app.ui.RefreshLoadViewFactory | ||||
| import com.android.base.app.ui.RefreshLoadViewFactory.Factory | ||||
| import com.android.base.app.ui.RefreshViewFactory | ||||
| import com.android.base.receiver.NetworkState | ||||
| import com.blankj.utilcode.util.ActivityUtils | ||||
| import com.blankj.utilcode.util.AppUtils | ||||
| import io.reactivex.Flowable | ||||
| import io.reactivex.plugins.RxJavaPlugins | ||||
| import timber.log.Timber | ||||
| 
 | ||||
| /** | ||||
|  * useful tools for android development, just like a sword. | ||||
|  * | ||||
|  * @author Ztiany | ||||
|  * Email: ztiany3@gmail.com | ||||
|  * Date : 2018-04-16 17:12 | ||||
|  */ | ||||
| object Sword { | ||||
| 
 | ||||
|     private val androidComponentLifecycleInjector = AndroidComponentLifecycleInjector() | ||||
| 
 | ||||
|     /** Application lifecycle delegate */ | ||||
|     internal val applicationDelegate = ApplicationDelegate(androidComponentLifecycleInjector) | ||||
| 
 | ||||
|     /** 错误类型分类器 */ | ||||
|     var errorClassifier: ErrorClassifier? = null | ||||
| 
 | ||||
|     /** dialog 最小展示时间  */ | ||||
|     var minimumShowingDialogMills: Long = 0 | ||||
| 
 | ||||
|     /** 用于创建 LoadingView*/ | ||||
|     var loadingViewFactory: ((Context) -> LoadingView)? = null | ||||
| 
 | ||||
|     /**网络状态监听器*/ | ||||
|     fun networkState(): Flowable<NetworkState> = NetworkState.observableState() | ||||
| 
 | ||||
|     fun setCrashProcessor(crashProcessor: CrashProcessor): Sword { | ||||
|         applicationDelegate.setCrashProcessor(crashProcessor) | ||||
|         return this | ||||
|     } | ||||
| 
 | ||||
|     fun setDefaultPageStart(pageStart: Int): Sword { | ||||
|         PageNumber.setDefaultPageStart(pageStart) | ||||
|         return this | ||||
|     } | ||||
| 
 | ||||
|     fun setDefaultPageSize(defaultPageSize: Int): Sword { | ||||
|         PageNumber.setDefaultPageSize(defaultPageSize) | ||||
|         return this | ||||
|     } | ||||
| 
 | ||||
|     /** 设置一个默认的布局 id,在使用 Fragments 中相关方法时,如果没有传入特定的容器 id  时,则使用设置的默认布局 id。  */ | ||||
|     fun setDefaultFragmentContainerId(defaultContainerId: Int): Sword { | ||||
|         FragmentConfig.setDefaultContainerId(defaultContainerId) | ||||
|         return this | ||||
|     } | ||||
| 
 | ||||
|     /**设置默认的 Fragment 转场动画*/ | ||||
|     fun setDefaultFragmentAnimator(animator: FragmentAnimator?): Sword { | ||||
|         FragmentConfig.setDefaultFragmentAnimator(animator) | ||||
|         return this | ||||
|     } | ||||
| 
 | ||||
|     /** RxJava2的一个重要的设计理念是:不吃掉任何一个异常。产生的问题是,当RxJava2“downStream”取消订阅后,“upStream”仍有可能抛出异常,这时由于已经取消订阅, | ||||
|      * “downStream”无法处理异常,此时的异常无人处理,便会导致程序崩溃,解决方案:在Application设置RxJavaPlugin的ErrorHandler。 | ||||
|      * refer: [RxJava2使用过程中遇到的坑](https://github.com/qqiabc521/blog/issues/3) */ | ||||
|     fun setupRxJavaErrorHandler(): Sword { | ||||
|         RxJavaPlugins.setErrorHandler { | ||||
|             Timber.d("RxJavaPlugins error handler receives error: $it") | ||||
|         } | ||||
|         return this | ||||
|     } | ||||
| 
 | ||||
|     fun enableAutoInject(): Sword { | ||||
|         androidComponentLifecycleInjector.enableAutoInject() | ||||
|         return this | ||||
|     } | ||||
| 
 | ||||
|     fun setDelegateInjector(delegateInjector: DelegateInjector): Sword { | ||||
|         androidComponentLifecycleInjector.delegateInjector = delegateInjector | ||||
|         return this | ||||
|     } | ||||
| 
 | ||||
|     /** 获取可观察的 app 生命周期,发射 tue 表示 app 切换到前台,发射 false 表示 app 切换到后台  */ | ||||
|     val appState: Flowable<Boolean> | ||||
|         get() = applicationDelegate.appStatus | ||||
| 
 | ||||
|     /** 获取当前 resume 的 Activity */ | ||||
|     val topActivity: Activity? | ||||
|         get() = ActivityUtils.getTopActivity() | ||||
| 
 | ||||
|     /** App是否在前台运行 */ | ||||
|     val isForeground: Boolean | ||||
|         get() = AppUtils.isAppForeground() | ||||
| 
 | ||||
|     fun registerRefreshLoadViewFactory(factory: Factory): Sword { | ||||
|         RefreshLoadViewFactory.registerFactory(factory) | ||||
|         return this | ||||
|     } | ||||
| 
 | ||||
|     fun registerRefreshViewFactory(factory: RefreshViewFactory.Factory): Sword { | ||||
|         RefreshViewFactory.registerFactory(factory) | ||||
|         return this | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| interface CrashProcessor { | ||||
|     fun uncaughtException(thread: Thread, ex: Throwable) | ||||
| } | ||||
| 
 | ||||
| interface ErrorClassifier { | ||||
|     fun isNetworkError(throwable: Throwable): Boolean | ||||
|     fun isServerError(throwable: Throwable): Boolean | ||||
| } | ||||
| 
 | ||||
| interface DelegateInjector { | ||||
| 
 | ||||
|     fun injectFragmentDelegate(fragment: FragmentDelegateOwner) | ||||
| 
 | ||||
|     fun injectActivityDelegate(activity: ActivityDelegateOwner) | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,79 @@ | ||||
| /* | ||||
|  * Copyright (C) 2018 The Android Open Source Project | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.android.base.app.aac | ||||
| 
 | ||||
| import androidx.fragment.app.Fragment | ||||
| import androidx.fragment.app.FragmentActivity | ||||
| import androidx.lifecycle.DefaultLifecycleObserver | ||||
| import androidx.lifecycle.Lifecycle | ||||
| import androidx.lifecycle.LifecycleOwner | ||||
| import kotlin.properties.ReadWriteProperty | ||||
| import kotlin.reflect.KProperty | ||||
| 
 | ||||
| class AutoClearedValue<T : Any>( | ||||
|         lifecycle: Lifecycle, | ||||
|         private val _event: Lifecycle.Event, | ||||
|         private val onCleared: (() -> Unit)? | ||||
| ) : ReadWriteProperty<Fragment, T> { | ||||
| 
 | ||||
|     private var _value: T? = null | ||||
| 
 | ||||
|     init { | ||||
|         lifecycle.addObserver(object : DefaultLifecycleObserver { | ||||
| 
 | ||||
|             override fun onPause(owner: LifecycleOwner) { | ||||
|                 clearValue(Lifecycle.Event.ON_PAUSE) | ||||
|             } | ||||
| 
 | ||||
|             override fun onStop(owner: LifecycleOwner) { | ||||
|                 clearValue(Lifecycle.Event.ON_STOP) | ||||
|             } | ||||
| 
 | ||||
|             override fun onDestroy(owner: LifecycleOwner) { | ||||
|                 clearValue(Lifecycle.Event.ON_DESTROY) | ||||
|             } | ||||
| 
 | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     override fun getValue(thisRef: Fragment, property: KProperty<*>): T { | ||||
|         return _value | ||||
|                 ?: throw IllegalStateException("should never call auto-cleared-value get when it might not be available") | ||||
|     } | ||||
| 
 | ||||
|     override fun setValue(thisRef: Fragment, property: KProperty<*>, value: T) { | ||||
|         _value = value | ||||
|     } | ||||
| 
 | ||||
|     private fun clearValue(event: Lifecycle.Event) { | ||||
|         if (_event == event) { | ||||
|             _value = null | ||||
|             onCleared?.invoke() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| fun <T : Any> Fragment.autoCleared( | ||||
|         event: Lifecycle.Event = Lifecycle.Event.ON_DESTROY, | ||||
|         onCleared: (() -> Unit)? = null | ||||
| ) = AutoClearedValue<T>(this.lifecycle, event, onCleared) | ||||
| 
 | ||||
| fun <T : Any> FragmentActivity.autoCleared( | ||||
|         event: Lifecycle.Event = Lifecycle.Event.ON_DESTROY, | ||||
|         onCleared: (() -> Unit)? = null | ||||
| ) = AutoClearedValue<T>(this.lifecycle, event, onCleared) | ||||
| @ -0,0 +1,23 @@ | ||||
| package com.android.base.app.aac | ||||
| 
 | ||||
| import androidx.lifecycle.MutableLiveData | ||||
| 
 | ||||
| fun <T> MutableLiveData<T>.init(t: T): MutableLiveData<T> { | ||||
|     this.postValue(t) | ||||
|     return this | ||||
| } | ||||
| 
 | ||||
| fun <T> MutableLiveData<T>.touchOff() { | ||||
|     this.postValue(this.value) | ||||
| } | ||||
| 
 | ||||
| fun <T> MutableLiveData<List<T>>.append(list: List<T>) { | ||||
|     val value = this.value | ||||
|     if (value == null) { | ||||
|         this.postValue(list) | ||||
|     } else { | ||||
|         val mutableList = value.toMutableList() | ||||
|         mutableList.addAll(list) | ||||
|         this.postValue(mutableList) | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,116 @@ | ||||
| package com.android.base.app.aac; | ||||
| 
 | ||||
| 
 | ||||
| import java.lang.ref.WeakReference; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.ListIterator; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.lifecycle.LifecycleOwner; | ||||
| import androidx.lifecycle.MediatorLiveData; | ||||
| import androidx.lifecycle.Observer; | ||||
| import timber.log.Timber; | ||||
| 
 | ||||
| public class SingleLiveData<T> extends MediatorLiveData<T> { | ||||
| 
 | ||||
|     private int mVersion = 0; | ||||
| 
 | ||||
|     private final List<WeakReference<ObserverWrapper<? super T>>> mWrapperObserverList = new ArrayList<>(); | ||||
| 
 | ||||
|     @Override | ||||
|     public void setValue(T value) { | ||||
|         mVersion++; | ||||
|         super.setValue(value); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) { | ||||
|         super.observe(owner, getOrNewObserver(observer, mVersion)); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void observeForever(@NonNull Observer<? super T> observer) { | ||||
|         super.observeForever(getOrNewObserver(observer, mVersion)); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public void removeObserver(@NonNull Observer<? super T> observer) { | ||||
|         if (observer instanceof ObserverWrapper) { | ||||
|             super.removeObserver(observer); | ||||
|             removeWrapper((ObserverWrapper) observer); | ||||
|             Timber.d("removeObserver() called with: observer = wrapper = [" + observer + "]"); | ||||
|         } else { | ||||
|             ObserverWrapper<? super T> wrapper = findWrapper(observer); | ||||
|             Timber.d("removeObserver() called with: observer = [" + observer + "], wrapper = [" + wrapper + "]"); | ||||
|             super.removeObserver(wrapper); | ||||
|             removeWrapper(wrapper); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void removeWrapper(ObserverWrapper observer) { | ||||
|         ListIterator<WeakReference<ObserverWrapper<? super T>>> iterator = mWrapperObserverList.listIterator(); | ||||
|         while (iterator.hasNext()) { | ||||
|             WeakReference<ObserverWrapper<? super T>> next = iterator.next(); | ||||
|             ObserverWrapper<? super T> item = next.get(); | ||||
|             if (item == observer) { | ||||
|                 iterator.remove(); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private ObserverWrapper<? super T> findWrapper(Observer<? super T> observer) { | ||||
|         ListIterator<WeakReference<ObserverWrapper<? super T>>> iterator = mWrapperObserverList.listIterator(); | ||||
| 
 | ||||
|         ObserverWrapper<? super T> target = null; | ||||
| 
 | ||||
|         while (iterator.hasNext()) { | ||||
|             WeakReference<ObserverWrapper<? super T>> next = iterator.next(); | ||||
|             ObserverWrapper<? super T> item = next.get(); | ||||
|             if (item == null) { | ||||
|                 iterator.remove(); | ||||
|             } else if (item.mOrigin == observer) { | ||||
|                 target = item; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return target; | ||||
|     } | ||||
| 
 | ||||
|     private Observer<? super T> getOrNewObserver(@NonNull Observer<? super T> observer, int observerVersion) { | ||||
|         ObserverWrapper<? super T> wrapper = findWrapper(observer); | ||||
| 
 | ||||
|         if (wrapper == null) { | ||||
|             wrapper = new ObserverWrapper<>(observerVersion, observer); | ||||
|             mWrapperObserverList.add(new WeakReference<>(wrapper)); | ||||
|         } | ||||
| 
 | ||||
|         Timber.d("getOrNewObserver() called with: observer = [" + observer + "], observerVersion = [" + observerVersion + "], wrapper = [" + wrapper + "]"); | ||||
| 
 | ||||
|         return wrapper; | ||||
|     } | ||||
| 
 | ||||
|     private class ObserverWrapper<E> implements Observer<E> { | ||||
| 
 | ||||
|         private final int mObserverVersion; | ||||
| 
 | ||||
|         private final Observer<E> mOrigin; | ||||
| 
 | ||||
|         private ObserverWrapper(int observerVersion, Observer<E> origin) { | ||||
|             mObserverVersion = observerVersion; | ||||
|             mOrigin = origin; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public void onChanged(@Nullable E t) { | ||||
|             if (mObserverVersion < mVersion && mOrigin != null) { | ||||
|                 mOrigin.onChanged(t); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,62 @@ | ||||
| package com.android.base.app.aac; | ||||
| 
 | ||||
| import android.util.Log; | ||||
| 
 | ||||
| import java.util.concurrent.atomic.AtomicBoolean; | ||||
| 
 | ||||
| import androidx.annotation.MainThread; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.lifecycle.LifecycleOwner; | ||||
| import androidx.lifecycle.MutableLiveData; | ||||
| import androidx.lifecycle.Observer; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * A lifecycle-aware observable that sends only new updates after subscription, used for events like | ||||
|  * navigation and Snackbar messages. | ||||
|  * <p> | ||||
|  * This avoids a common problem with events: on configuration change (like rotation) an update | ||||
|  * can be emitted if the observer is active. This LiveData only calls the observable if there's an | ||||
|  * explicit call to setValue() or call(). | ||||
|  * <p> | ||||
|  * Note that only one observer is going to be notified of changes. | ||||
|  * | ||||
|  * @see <a href='https://github.com/googlesamples/android-architecture/blob/dev-todo-mvvm-live/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/SingleLiveEvent.java'>SingleLiveEvent</a>
 | ||||
|  */ | ||||
| public class SingleLiveEvent<T> extends MutableLiveData<T> { | ||||
| 
 | ||||
|     private static final String TAG = "SingleLiveEvent"; | ||||
| 
 | ||||
|     private final AtomicBoolean mPending = new AtomicBoolean(false); | ||||
| 
 | ||||
|     @MainThread | ||||
|     public void observe(@NonNull LifecycleOwner owner, @NonNull final Observer<? super T> observer) { | ||||
| 
 | ||||
|         if (hasActiveObservers()) { | ||||
|             Log.w(TAG, "Multiple observers registered but only one will be notified of changes."); | ||||
|         } | ||||
| 
 | ||||
|         // Observe the internal MutableLiveData
 | ||||
|         super.observe(owner, t -> { | ||||
|             if (mPending.compareAndSet(true, false)) { | ||||
|                 observer.onChanged(t); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     @MainThread | ||||
|     public void setValue(@Nullable T t) { | ||||
|         mPending.set(true); | ||||
|         super.setValue(t); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Used for cases where T is Void, to make calls cleaner. | ||||
|      */ | ||||
|     @MainThread | ||||
|     public void call() { | ||||
|         setValue(null); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,69 @@ | ||||
| package com.android.base.app.activity; | ||||
| 
 | ||||
| import android.app.Activity; | ||||
| import android.content.Intent; | ||||
| import android.os.Bundle; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Activity生命周期代理 | ||||
|  * | ||||
|  * @author Ztiany | ||||
|  * Date : 2016-05-06 15:04 | ||||
|  * Email: 1169654504@qq.com | ||||
|  */ | ||||
| @SuppressWarnings("unused") | ||||
| public interface ActivityDelegate<T extends Activity> { | ||||
| 
 | ||||
|     default void onAttachedToActivity(@NonNull T activity) { | ||||
|     } | ||||
| 
 | ||||
|     default void onDetachedFromActivity() { | ||||
|     } | ||||
| 
 | ||||
|     default void onCreateBeforeSetContentView(@Nullable Bundle savedInstanceState) { | ||||
|     } | ||||
| 
 | ||||
|     default void onCreateAfterSetContentView(@Nullable Bundle savedInstanceState) { | ||||
|     } | ||||
| 
 | ||||
|     default void onSaveInstanceState(Bundle savedInstanceState) { | ||||
|     } | ||||
| 
 | ||||
|     default void onStart() { | ||||
|     } | ||||
| 
 | ||||
|     default void onResume() { | ||||
|     } | ||||
| 
 | ||||
|     default void onPause() { | ||||
|     } | ||||
| 
 | ||||
|     default void onStop() { | ||||
|     } | ||||
| 
 | ||||
|     default void onDestroy() { | ||||
|     } | ||||
| 
 | ||||
|     default void onActivityResult(int requestCode, int resultCode, Intent data) { | ||||
|     } | ||||
| 
 | ||||
|     default void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { | ||||
|     } | ||||
| 
 | ||||
|     default void onRestoreInstanceState(Bundle savedInstanceState) { | ||||
|     } | ||||
| 
 | ||||
|     default void onRestart() { | ||||
|     } | ||||
| 
 | ||||
|     default void onPostCreate(@Nullable Bundle savedInstanceState) { | ||||
|     } | ||||
| 
 | ||||
|     default void onResumeFragments() { | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,20 @@ | ||||
| package com.android.base.app.activity; | ||||
| 
 | ||||
| 
 | ||||
| import com.github.dmstocking.optional.java.util.function.Predicate; | ||||
| 
 | ||||
| import androidx.annotation.UiThread; | ||||
| 
 | ||||
| @UiThread | ||||
| @SuppressWarnings("unused") | ||||
| public interface ActivityDelegateOwner { | ||||
| 
 | ||||
|     void addDelegate(ActivityDelegate fragmentDelegate); | ||||
| 
 | ||||
|     boolean removeDelegate(ActivityDelegate fragmentDelegate); | ||||
| 
 | ||||
|     ActivityDelegate findDelegate(Predicate<ActivityDelegate> predicate); | ||||
| 
 | ||||
|     ActivityState getStatus(); | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,138 @@ | ||||
| package com.android.base.app.activity; | ||||
| 
 | ||||
| import android.content.Intent; | ||||
| import android.os.Bundle; | ||||
| 
 | ||||
| import com.github.dmstocking.optional.java.util.function.Predicate; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.annotation.UiThread; | ||||
| import androidx.appcompat.app.AppCompatActivity; | ||||
| 
 | ||||
| /** | ||||
|  * @author Ztiany | ||||
|  * Email: 1169654504@qq.com | ||||
|  * Date : 2016-12-20 11:43 | ||||
|  */ | ||||
| @UiThread | ||||
| final class ActivityDelegates { | ||||
| 
 | ||||
|     private final List<ActivityDelegate> mDelegates; | ||||
|     private AppCompatActivity mBaseActivity; | ||||
| 
 | ||||
|     ActivityDelegates(AppCompatActivity baseActivity) { | ||||
|         mDelegates = new ArrayList<>(4); | ||||
|         mBaseActivity = baseActivity; | ||||
|     } | ||||
| 
 | ||||
|     void callOnCreateBeforeSetContentView(@Nullable Bundle savedInstanceState) { | ||||
|         for (ActivityDelegate activityDelegate : mDelegates) { | ||||
|             activityDelegate.onCreateBeforeSetContentView(savedInstanceState); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void callOnCreateAfterSetContentView(@Nullable Bundle savedInstanceState) { | ||||
|         for (ActivityDelegate activityDelegate : mDelegates) { | ||||
|             activityDelegate.onCreateAfterSetContentView(savedInstanceState); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void callOnRestoreInstanceState(Bundle savedInstanceState) { | ||||
|         for (ActivityDelegate activityDelegate : mDelegates) { | ||||
|             activityDelegate.onRestoreInstanceState(savedInstanceState); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void callOnPostCreate(@Nullable Bundle savedInstanceState) { | ||||
|         for (ActivityDelegate activityDelegate : mDelegates) { | ||||
|             activityDelegate.onPostCreate(savedInstanceState); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void callOnSaveInstanceState(Bundle outState) { | ||||
|         for (ActivityDelegate activityDelegate : mDelegates) { | ||||
|             activityDelegate.onSaveInstanceState(outState); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void callOnDestroy() { | ||||
|         for (ActivityDelegate activityDelegate : mDelegates) { | ||||
|             activityDelegate.onDestroy(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void callOnStop() { | ||||
|         for (ActivityDelegate activityDelegate : mDelegates) { | ||||
|             activityDelegate.onStop(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void callOnPause() { | ||||
|         for (ActivityDelegate activityDelegate : mDelegates) { | ||||
|             activityDelegate.onPause(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void callOnResume() { | ||||
|         for (ActivityDelegate activityDelegate : mDelegates) { | ||||
|             activityDelegate.onResume(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void callOnStart() { | ||||
|         for (ActivityDelegate activityDelegate : mDelegates) { | ||||
|             activityDelegate.onStart(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void callOnRestart() { | ||||
|         for (ActivityDelegate activityDelegate : mDelegates) { | ||||
|             activityDelegate.onRestart(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void callOnActivityResult(int requestCode, int resultCode, Intent data) { | ||||
|         for (ActivityDelegate activityDelegate : mDelegates) { | ||||
|             activityDelegate.onActivityResult(requestCode, resultCode, data); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void callOnRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { | ||||
|         for (ActivityDelegate activityDelegate : mDelegates) { | ||||
|             activityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void callOnResumeFragments() { | ||||
|         for (ActivityDelegate activityDelegate : mDelegates) { | ||||
|             activityDelegate.onResumeFragments(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("unchecked") | ||||
|     void addActivityDelegate(ActivityDelegate activityDelegate) { | ||||
|         mDelegates.add(activityDelegate); | ||||
|         activityDelegate.onAttachedToActivity(mBaseActivity); | ||||
|     } | ||||
| 
 | ||||
|     boolean removeActivityDelegate(ActivityDelegate activityDelegate) { | ||||
|         boolean remove = mDelegates.remove(activityDelegate); | ||||
|         if (remove) { | ||||
|             activityDelegate.onDetachedFromActivity(); | ||||
|         } | ||||
|         return remove; | ||||
|     } | ||||
| 
 | ||||
|     ActivityDelegate findDelegate(Predicate<ActivityDelegate> predicate) { | ||||
|         for (ActivityDelegate delegate : mDelegates) { | ||||
|             if (predicate.test(delegate)) { | ||||
|                 return delegate; | ||||
|             } | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,11 @@ | ||||
| package com.android.base.app.activity | ||||
| 
 | ||||
| enum class ActivityState { | ||||
|     INITIALIZED, | ||||
|     CREATE, | ||||
|     START, | ||||
|     RESUME, | ||||
|     PAUSE, | ||||
|     STOP, | ||||
|     DESTROY | ||||
| } | ||||
| @ -0,0 +1,72 @@ | ||||
| package com.android.base.app.activity; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import androidx.fragment.app.Fragment; | ||||
| import androidx.fragment.app.FragmentActivity; | ||||
| import androidx.fragment.app.FragmentManager; | ||||
| 
 | ||||
| /** | ||||
|  * <pre> | ||||
|  *        1.Fragment需要自己处理BackPress事件,如果不处理,就交给子Fragment处理。都不处理则由Activity处理 | ||||
|  *        2.BackPress的传递由低层往深层传递,同一层级的中外层中的 Fragment优先处理。 | ||||
|  *        3.在Fragment中嵌套使用Fragment时,请使用getSupportChildFragmentManager | ||||
|  * </pre> | ||||
|  */ | ||||
| @SuppressWarnings("WeakerAccess") | ||||
| public class BackHandlerHelper { | ||||
| 
 | ||||
|     /** | ||||
|      * 将back事件分发给 FragmentManager 中管理的子Fragment,如果该 FragmentManager 中的所有Fragment都 | ||||
|      * 没有处理back事件,则尝试 FragmentManager.popBackStack() | ||||
|      * | ||||
|      * @return 如果处理了back键则返回 <b>true</b> | ||||
|      * @see #handleBackPress(Fragment) | ||||
|      * @see #handleBackPress(FragmentActivity) | ||||
|      */ | ||||
|     public static boolean handleBackPress(FragmentManager fragmentManager) { | ||||
|         List<Fragment> fragments = fragmentManager.getFragments(); | ||||
|         for (int i = fragments.size() - 1; i >= 0; i--) { | ||||
|             Fragment child = fragments.get(i); | ||||
|             if (isFragmentBackHandled(child)) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 将back事件分发给Fragment中的子Fragment, | ||||
|      * 该方法调用了 {@link #handleBackPress(FragmentManager)} | ||||
|      * | ||||
|      * @return 如果处理了back键则返回 <b>true</b> | ||||
|      */ | ||||
|     public static boolean handleBackPress(Fragment fragment) { | ||||
|         return handleBackPress(fragment.getChildFragmentManager()); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 将back事件分发给Activity中的子Fragment, | ||||
|      * 该方法调用了 {@link #handleBackPress(FragmentManager)} | ||||
|      * | ||||
|      * @return 如果处理了back键则返回 <b>true</b> | ||||
|      */ | ||||
|     public static boolean handleBackPress(FragmentActivity fragmentActivity) { | ||||
|         return handleBackPress(fragmentActivity.getSupportFragmentManager()); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 判断Fragment是否处理了Back键 | ||||
|      * | ||||
|      * @return 如果处理了back键则返回 <b>true</b> | ||||
|      */ | ||||
|     @SuppressWarnings("WeakerAccess") | ||||
|     public static boolean isFragmentBackHandled(Fragment fragment) { | ||||
|         return fragment != null | ||||
|                 && fragment.isVisible() | ||||
|                 && fragment.getUserVisibleHint() //getUserVisibleHint默认情况下为true,在ViewPager中会被使用到。
 | ||||
|                 && fragment instanceof OnBackPressListener | ||||
|                 && ((OnBackPressListener) fragment).onBackPressed(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,191 @@ | ||||
| package com.android.base.app.activity | ||||
| 
 | ||||
| import android.content.Intent | ||||
| import android.os.Bundle | ||||
| import android.view.View | ||||
| import androidx.annotation.UiThread | ||||
| import androidx.appcompat.app.AppCompatActivity | ||||
| import androidx.fragment.app.Fragment | ||||
| import com.android.base.rx.autodispose.AutoDisposeLifecycleOwnerEx | ||||
| import com.android.base.utils.android.compat.AndroidVersion | ||||
| import com.github.dmstocking.optional.java.util.function.Predicate | ||||
| import timber.log.Timber | ||||
| 
 | ||||
| /** | ||||
|  * 基础 BaseActivity 封装: | ||||
|  * | ||||
|  * 1. 封装通用流程。 | ||||
|  * 2. [onBackPressed] 事件分发,优先交给 [Fragment] 处理。 | ||||
|  * 3. 提供 RxJava 的生命周期绑定。 | ||||
|  * | ||||
|  * @author Ztiany | ||||
|  * Date : 2016-05-04 15:40 | ||||
|  * Email: 1169654504@qq.com | ||||
|  */ | ||||
| abstract class BaseActivity : AppCompatActivity(), ActivityDelegateOwner, AutoDisposeLifecycleOwnerEx { | ||||
| 
 | ||||
|     @Suppress("LeakingThis") | ||||
|     private val activityDelegates = ActivityDelegates(this) | ||||
| 
 | ||||
|     private var activityState = ActivityState.INITIALIZED | ||||
| 
 | ||||
|     private fun tag() = this.javaClass.simpleName | ||||
| 
 | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
|         Timber.tag(tag()).d("---->onCreate before call super") | ||||
|         initialize(savedInstanceState) | ||||
|         activityDelegates.callOnCreateBeforeSetContentView(savedInstanceState) | ||||
|         super.onCreate(savedInstanceState) | ||||
|         Timber.tag(tag()).d("---->onCreate after call super  bundle = $savedInstanceState") | ||||
| 
 | ||||
|         when (val layout = layout()) { | ||||
|             is View -> setContentView(layout) | ||||
|             is Int -> setContentView(layout) | ||||
|             null -> Timber.d("layout() return null layout") | ||||
|             else -> throw IllegalArgumentException("layout() return type no support, layout = $layout") | ||||
|         } | ||||
| 
 | ||||
|         activityState = ActivityState.CREATE | ||||
|         activityDelegates.callOnCreateAfterSetContentView(savedInstanceState) | ||||
|         setupView(savedInstanceState) | ||||
|     } | ||||
| 
 | ||||
|     override fun onRestart() { | ||||
|         Timber.tag(tag()).d("---->onRestart before call super") | ||||
|         super.onRestart() | ||||
|         Timber.tag(tag()).d("---->onRestart after call super  ") | ||||
|         activityDelegates.callOnRestart() | ||||
|     } | ||||
| 
 | ||||
|     override fun onStart() { | ||||
|         Timber.tag(tag()).d("---->onStart before call super") | ||||
|         super.onStart() | ||||
|         Timber.tag(tag()).d("---->onStart after call super") | ||||
|         activityState = ActivityState.START | ||||
|         activityDelegates.callOnStart() | ||||
|     } | ||||
| 
 | ||||
|     override fun onResume() { | ||||
|         Timber.tag(tag()).d("---->onResume before call super") | ||||
|         super.onResume() | ||||
|         Timber.tag(tag()).d("---->onResume after call super") | ||||
|         activityState = ActivityState.RESUME | ||||
|         activityDelegates.callOnResume() | ||||
|     } | ||||
| 
 | ||||
|     override fun onPause() { | ||||
|         Timber.tag(tag()).d("---->onPause before call super") | ||||
|         activityState = ActivityState.PAUSE | ||||
|         activityDelegates.callOnPause() | ||||
|         super.onPause() | ||||
|         Timber.tag(tag()).d("---->onPause after call super  ") | ||||
|     } | ||||
| 
 | ||||
|     override fun onStop() { | ||||
|         Timber.tag(tag()).d("---->onStop before call super") | ||||
|         activityState = ActivityState.STOP | ||||
|         activityDelegates.callOnStop() | ||||
|         super.onStop() | ||||
|         Timber.tag(tag()).d("---->onStop after call super") | ||||
|     } | ||||
| 
 | ||||
|     override fun onDestroy() { | ||||
|         Timber.tag(tag()).d("---->onDestroy before call super") | ||||
|         activityState = ActivityState.DESTROY | ||||
|         activityDelegates.callOnDestroy() | ||||
|         super.onDestroy() | ||||
|         Timber.tag(tag()).d("---->onDestroy after call super") | ||||
|     } | ||||
| 
 | ||||
|     override fun onPostCreate(savedInstanceState: Bundle?) { | ||||
|         super.onPostCreate(savedInstanceState) | ||||
|         activityDelegates.callOnPostCreate(savedInstanceState) | ||||
|     } | ||||
| 
 | ||||
|     override fun onSaveInstanceState(outState: Bundle) { | ||||
|         super.onSaveInstanceState(outState) | ||||
|         activityDelegates.callOnSaveInstanceState(outState) | ||||
|     } | ||||
| 
 | ||||
|     override fun onRestoreInstanceState(savedInstanceState: Bundle) { | ||||
|         super.onRestoreInstanceState(savedInstanceState) | ||||
|         activityDelegates.callOnRestoreInstanceState(savedInstanceState) | ||||
|     } | ||||
| 
 | ||||
|     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { | ||||
|         super.onActivityResult(requestCode, resultCode, data) | ||||
|         activityDelegates.callOnActivityResult(requestCode, resultCode, data) | ||||
|     } | ||||
| 
 | ||||
|     override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { | ||||
|         super.onRequestPermissionsResult(requestCode, permissions, grantResults) | ||||
|         activityDelegates.callOnRequestPermissionsResult(requestCode, permissions, grantResults) | ||||
|     } | ||||
| 
 | ||||
|     override fun onResumeFragments() { | ||||
|         super.onResumeFragments() | ||||
|         activityDelegates.callOnResumeFragments() | ||||
|     } | ||||
| 
 | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     // interface impl | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|     @UiThread | ||||
|     final override fun addDelegate(activityDelegate: ActivityDelegate<*>) { | ||||
|         activityDelegates.addActivityDelegate(activityDelegate) | ||||
|     } | ||||
| 
 | ||||
|     @UiThread | ||||
|     final override fun removeDelegate(activityDelegate: ActivityDelegate<*>): Boolean { | ||||
|         return activityDelegates.removeActivityDelegate(activityDelegate) | ||||
|     } | ||||
| 
 | ||||
|     @UiThread | ||||
|     final override fun findDelegate(predicate: Predicate<ActivityDelegate<*>?>?): ActivityDelegate<*>? { | ||||
|         return activityDelegates.findDelegate(predicate) | ||||
|     } | ||||
| 
 | ||||
|     override fun getStatus(): ActivityState { | ||||
|         return activityState | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Before call super.onCreate and setContentView | ||||
|      * | ||||
|      * @param savedInstanceState state | ||||
|      */ | ||||
|     protected open fun initialize(savedInstanceState: Bundle?) {} | ||||
| 
 | ||||
|     /** | ||||
|      * provide a layoutId (int) or layout (View) | ||||
|      * | ||||
|      * @return layoutId | ||||
|      */ | ||||
|     protected abstract fun layout(): Any? | ||||
| 
 | ||||
|     /** | ||||
|      * after setContentView | ||||
|      */ | ||||
|     protected abstract fun setupView(savedInstanceState: Bundle?) | ||||
| 
 | ||||
|     override fun onBackPressed() { | ||||
|         if (BackHandlerHelper.handleBackPress(this)) { | ||||
|             Timber.d("onBackPressed() called but child fragment handle it") | ||||
|         } else { | ||||
|             superOnBackPressed() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected open fun superOnBackPressed() { | ||||
|         super.onBackPressed() | ||||
|     } | ||||
| 
 | ||||
|     override fun isDestroyed(): Boolean { | ||||
|         return if (AndroidVersion.atLeast(17)) { | ||||
|             super.isDestroyed() | ||||
|         } else { | ||||
|             status === ActivityState.DESTROY | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,26 @@ | ||||
| package com.android.base.app.activity; | ||||
| 
 | ||||
| import com.android.base.app.dagger.Injectable; | ||||
| 
 | ||||
| import javax.inject.Inject; | ||||
| 
 | ||||
| import dagger.android.AndroidInjector; | ||||
| import dagger.android.DispatchingAndroidInjector; | ||||
| import dagger.android.HasAndroidInjector; | ||||
| 
 | ||||
| /** | ||||
|  * @author Ztiany | ||||
|  * Email: 1169654504@qq.com | ||||
|  * Date : 2018-11-01 | ||||
|  */ | ||||
| public abstract class InjectorBaseActivity extends BaseActivity implements Injectable, HasAndroidInjector { | ||||
| 
 | ||||
|     @Inject | ||||
|     DispatchingAndroidInjector<Object> androidInjector; | ||||
| 
 | ||||
|     @Override | ||||
|     public AndroidInjector<Object> androidInjector() { | ||||
|         return androidInjector; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,13 @@ | ||||
| package com.android.base.app.activity; | ||||
| 
 | ||||
| /** | ||||
|  * Activity的返回键监听 | ||||
|  */ | ||||
| public interface OnBackPressListener { | ||||
| 
 | ||||
|     /** | ||||
|      * @return true 表示Fragment处理back press,false表示由Activity处理 | ||||
|      */ | ||||
|     boolean onBackPressed(); | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,14 @@ | ||||
| package com.android.base.app.dagger; | ||||
| 
 | ||||
| import java.lang.annotation.Documented; | ||||
| import java.lang.annotation.Retention; | ||||
| import java.lang.annotation.RetentionPolicy; | ||||
| 
 | ||||
| import javax.inject.Scope; | ||||
| 
 | ||||
| 
 | ||||
| @Scope | ||||
| @Retention(RetentionPolicy.RUNTIME) | ||||
| @Documented | ||||
| public @interface ActivityScope { | ||||
| } | ||||
| @ -0,0 +1,22 @@ | ||||
| package com.android.base.app.dagger; | ||||
| 
 | ||||
| import java.lang.annotation.Documented; | ||||
| import java.lang.annotation.Retention; | ||||
| 
 | ||||
| import javax.inject.Qualifier; | ||||
| 
 | ||||
| import static java.lang.annotation.RetentionPolicy.RUNTIME; | ||||
| 
 | ||||
| 
 | ||||
| @Qualifier | ||||
| @Documented | ||||
| @Retention(RUNTIME) | ||||
| public @interface ContextType { | ||||
| 
 | ||||
|     String ACTIVITY = "Activity"; | ||||
|     String CONTEXT = "Context"; | ||||
|     String APPLICATION = "Application"; | ||||
| 
 | ||||
|     String value() default APPLICATION; | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,14 @@ | ||||
| package com.android.base.app.dagger; | ||||
| 
 | ||||
| import java.lang.annotation.Documented; | ||||
| import java.lang.annotation.Retention; | ||||
| import java.lang.annotation.RetentionPolicy; | ||||
| 
 | ||||
| import javax.inject.Scope; | ||||
| 
 | ||||
| 
 | ||||
| @Scope | ||||
| @Retention(RetentionPolicy.RUNTIME) | ||||
| @Documented | ||||
| public @interface FragmentScope { | ||||
| } | ||||
| @ -0,0 +1,18 @@ | ||||
| package com.android.base.app.dagger; | ||||
| 
 | ||||
| import android.view.View; | ||||
| 
 | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import javax.inject.Provider; | ||||
| 
 | ||||
| /** | ||||
|  * @author Ztiany | ||||
|  * Email: ztiany3@gmail.com | ||||
|  * Date : 2017-06-21 14:58 | ||||
|  */ | ||||
| public interface HasViewInjector { | ||||
| 
 | ||||
|     Map<Class<? extends View>, Provider<ViewComponentBuilder>> viewInjectors(); | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,26 @@ | ||||
| package com.android.base.app.dagger; | ||||
| 
 | ||||
| /** | ||||
|  * 当 Activity 实现此接口时,如果需要为内部的 Fragment 提供注入容器,需要实现 HasSupportFragmentInjector,具体如下面代码: | ||||
|  * | ||||
|  * <pre>{@code | ||||
|  * | ||||
|  *       class InjectableActivity implements Injectable, HasSupportFragmentInjector{ | ||||
|  *              @Inject | ||||
|  *              DispatchingAndroidInjector<Fragment> fragmentInjector; | ||||
|  * | ||||
|  *              public AndroidInjector<Fragment> supportFragmentInjector() { | ||||
|  *                         return fragmentInjector; | ||||
|  *              } | ||||
|  *       } | ||||
|  * } | ||||
|  * | ||||
|  * 标记接口,用于标记此类需要被注入依赖。 | ||||
|  * | ||||
|  * @author Ztiany | ||||
|  * Email: ztiany3@gmail.com | ||||
|  * Date : 2018-12-11 12:38 | ||||
|  */ | ||||
| public interface Injectable { | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,14 @@ | ||||
| package com.android.base.app.dagger; | ||||
| 
 | ||||
| import android.view.View; | ||||
| 
 | ||||
| import dagger.MembersInjector; | ||||
| 
 | ||||
| /** | ||||
|  * how to use it? refer https://github.com/Ztiany/notes/blob/master/Android/00-Code/Dagger2AndroidInjection-v2.24/README.md.
 | ||||
|  * | ||||
|  * @param <A> | ||||
|  */ | ||||
| @SuppressWarnings("WeakerAccess") | ||||
| public interface ViewComponent<A extends View> extends MembersInjector<A> { | ||||
| } | ||||
| @ -0,0 +1,15 @@ | ||||
| package com.android.base.app.dagger; | ||||
| 
 | ||||
| import android.view.View; | ||||
| 
 | ||||
| import dagger.BindsInstance; | ||||
| 
 | ||||
| 
 | ||||
| public interface ViewComponentBuilder<T extends View, C extends ViewComponent<T>> { | ||||
| 
 | ||||
|     @BindsInstance | ||||
|     ViewComponentBuilder bindInstance(T t); | ||||
| 
 | ||||
|     C build(); | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,60 @@ | ||||
| package com.android.base.app.dagger; | ||||
| 
 | ||||
| import android.app.Application; | ||||
| import android.content.Context; | ||||
| import android.util.Log; | ||||
| import android.view.View; | ||||
| 
 | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import javax.inject.Provider; | ||||
| 
 | ||||
| /** | ||||
|  * @author Ztiany | ||||
|  * Email: ztiany3@gmail.com | ||||
|  * Date : 2017-06-21 14:55 | ||||
|  */ | ||||
| @SuppressWarnings("unused") | ||||
| public class ViewInjection { | ||||
| 
 | ||||
|     private static final String TAG = ViewInjection.class.getSimpleName(); | ||||
| 
 | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public static void inject(View view) { | ||||
| 
 | ||||
|         if (null == view) { | ||||
|             throw new NullPointerException(); | ||||
|         } | ||||
| 
 | ||||
|         HasViewInjector hasViewInjector = findHasViewInjectors(view); | ||||
| 
 | ||||
|         Log.d(TAG, String.format("An injector for %s was found in %s", view.getClass().getCanonicalName(), hasViewInjector.getClass().getCanonicalName())); | ||||
| 
 | ||||
|         Map<Class<? extends View>, Provider<ViewComponentBuilder>> viewInjectors = hasViewInjector.viewInjectors(); | ||||
|         Provider<ViewComponentBuilder> provider = viewInjectors.get(view.getClass()); | ||||
| 
 | ||||
|         if (provider != null) { | ||||
|             ViewComponentBuilder viewComponentBuilder = provider.get(); | ||||
|             viewComponentBuilder | ||||
|                     .bindInstance(view) | ||||
|                     .build() | ||||
|                     .injectMembers(view); | ||||
|         } else { | ||||
|             throw new NullPointerException("ViewInjection  fail "); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private static HasViewInjector findHasViewInjectors(View view) { | ||||
|         Context context = view.getContext(); | ||||
|         if (context instanceof HasViewInjector) { | ||||
|             return (HasViewInjector) context; | ||||
|         } | ||||
|         Application application = (Application) context.getApplicationContext(); | ||||
|         if (application instanceof HasViewInjector) { | ||||
|             return (HasViewInjector) application; | ||||
|         } | ||||
|         throw new IllegalArgumentException(String.format("No injector was found for %s", view.getClass().getCanonicalName())); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,18 @@ | ||||
| package com.android.base.app.dagger; | ||||
| 
 | ||||
| import android.view.View; | ||||
| 
 | ||||
| import java.lang.annotation.Target; | ||||
| 
 | ||||
| import dagger.MapKey; | ||||
| import dagger.internal.Beta; | ||||
| 
 | ||||
| import static java.lang.annotation.ElementType.METHOD; | ||||
| 
 | ||||
| @Beta | ||||
| @MapKey | ||||
| @Target(METHOD) | ||||
| @SuppressWarnings("unused") | ||||
| public @interface ViewKey { | ||||
|     Class<? extends View> value(); | ||||
| } | ||||
| @ -0,0 +1,48 @@ | ||||
| package com.android.base.app.dagger; | ||||
| 
 | ||||
| 
 | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import javax.inject.Inject; | ||||
| import javax.inject.Provider; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.lifecycle.ViewModel; | ||||
| import androidx.lifecycle.ViewModelProvider; | ||||
| 
 | ||||
| /** | ||||
|  * {@link androidx.lifecycle.AbstractSavedStateViewModelFactory} is not supported. | ||||
|  * | ||||
|  * @author Ztiany | ||||
|  * Email: ztiany3@gmail.com | ||||
|  * Date : 2018-11-05 14:31 | ||||
|  */ | ||||
| public class ViewModelFactory implements ViewModelProvider.Factory { | ||||
| 
 | ||||
|     private final Map<Class<? extends ViewModel>, Provider<ViewModel>> mCreatorMap; | ||||
| 
 | ||||
|     @Inject | ||||
|     ViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> classProviderMap) { | ||||
|         mCreatorMap = classProviderMap; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { | ||||
|         Provider<ViewModel> viewModelProvider = mCreatorMap.get(modelClass); | ||||
|         if (viewModelProvider == null) { | ||||
|             for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : mCreatorMap.entrySet()) { | ||||
|                 if (modelClass.isAssignableFrom(entry.getKey())) { | ||||
|                     viewModelProvider = entry.getValue(); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if (viewModelProvider != null) { | ||||
|             return (T) viewModelProvider.get(); | ||||
|         } | ||||
|         throw new NullPointerException("can not find provider for " + modelClass); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,19 @@ | ||||
| package com.android.base.app.dagger; | ||||
| 
 | ||||
| 
 | ||||
| import androidx.lifecycle.ViewModel; | ||||
| import dagger.MapKey; | ||||
| import kotlin.annotation.AnnotationTarget; | ||||
| import kotlin.annotation.MustBeDocumented; | ||||
| import kotlin.annotation.Retention; | ||||
| import kotlin.annotation.Target; | ||||
| 
 | ||||
| @MustBeDocumented | ||||
| @Target(allowedTargets = AnnotationTarget.FUNCTION) | ||||
| @Retention() | ||||
| @MapKey | ||||
| public @interface ViewModelKey { | ||||
| 
 | ||||
|     Class<? extends ViewModel> value(); | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,21 @@ | ||||
| package com.android.base.app.dagger; | ||||
| 
 | ||||
| import androidx.lifecycle.ViewModelProvider; | ||||
| import dagger.Binds; | ||||
| import dagger.Module; | ||||
| 
 | ||||
| /** | ||||
|  * {@link androidx.lifecycle.AbstractSavedStateViewModelFactory} is not supported. | ||||
|  * | ||||
|  * @author Ztiany | ||||
|  * Email: ztiany3@gmail.com | ||||
|  * Date : 2018-11-05 16:20 | ||||
|  */ | ||||
| @Module | ||||
| public abstract class ViewModelModule { | ||||
| 
 | ||||
|     @Binds | ||||
|     @ActivityScope | ||||
|     abstract ViewModelProvider.Factory provideViewModelFactory(ViewModelFactory viewModelFactory); | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,19 @@ | ||||
| package com.android.base.app.dagger; | ||||
| 
 | ||||
| import java.lang.annotation.Documented; | ||||
| import java.lang.annotation.Retention; | ||||
| 
 | ||||
| import javax.inject.Scope; | ||||
| 
 | ||||
| import static java.lang.annotation.RetentionPolicy.RUNTIME; | ||||
| 
 | ||||
| /** | ||||
|  * @author Ztiany | ||||
|  *         Email: ztiany3@gmail.com | ||||
|  *         Date : 2017-05-23 09:59 | ||||
|  */ | ||||
| @Scope | ||||
| @Documented | ||||
| @Retention(RUNTIME) | ||||
| public @interface ViewScope { | ||||
| } | ||||
| @ -0,0 +1,277 @@ | ||||
| package com.android.base.app.fragment | ||||
| 
 | ||||
| import android.content.Context | ||||
| import android.content.Intent | ||||
| import android.os.Bundle | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import androidx.annotation.StringRes | ||||
| import androidx.annotation.UiThread | ||||
| import androidx.appcompat.app.AppCompatDialogFragment | ||||
| import com.android.base.app.Sword | ||||
| import com.android.base.app.activity.BackHandlerHelper | ||||
| import com.android.base.app.activity.OnBackPressListener | ||||
| import com.android.base.app.fragment.delegates.FragmentDelegate | ||||
| import com.android.base.app.fragment.delegates.FragmentDelegateOwner | ||||
| import com.android.base.app.ui.LoadingView | ||||
| import com.android.base.rx.autodispose.AutoDisposeLifecycleOwnerEx | ||||
| import com.github.dmstocking.optional.java.util.function.Predicate | ||||
| import timber.log.Timber | ||||
| 
 | ||||
| /** | ||||
|  * @author Ztiany | ||||
|  * date :   2016-03-19 23:09 | ||||
|  * email:    1169654504@qq.com | ||||
|  * @see [BaseFragment] | ||||
|  */ | ||||
| open class BaseDialogFragment : AppCompatDialogFragment(), LoadingView, OnBackPressListener, FragmentDelegateOwner, AutoDisposeLifecycleOwnerEx { | ||||
| 
 | ||||
|     private var loadingView: LoadingView? = null | ||||
| 
 | ||||
|     private var layoutView: View? = null | ||||
| 
 | ||||
|     /** just for cache*/ | ||||
|     private var cachedView: View? = null | ||||
| 
 | ||||
|     @Suppress("LeakingThis") | ||||
|     private val fragmentDelegates = FragmentDelegates(this) | ||||
| 
 | ||||
|     private var recentShowingDialogTime: Long = 0 | ||||
| 
 | ||||
|     private var _state: Bundle? = null | ||||
| 
 | ||||
|     protected val state: Bundle | ||||
|         get() { | ||||
|             var state = _state | ||||
|             if (state == null) { | ||||
|                 state = Bundle() | ||||
|                 _state = state | ||||
|             } | ||||
|             return state | ||||
|         } | ||||
| 
 | ||||
|     private fun tag() = this.javaClass.simpleName | ||||
| 
 | ||||
|     override fun onAttach(context: Context) { | ||||
|         super.onAttach(context) | ||||
|         Timber.tag(tag()).d("onAttach() called with: context = [$context]") | ||||
|         fragmentDelegates.onAttach(context) | ||||
|     } | ||||
| 
 | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
|         super.onCreate(savedInstanceState) | ||||
|         _state = getInstanceState(savedInstanceState) | ||||
|         Timber.tag(tag()).d("-->onCreate  savedInstanceState   =   $savedInstanceState") | ||||
|         fragmentDelegates.onCreate(savedInstanceState) | ||||
|     } | ||||
| 
 | ||||
|     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { | ||||
|         if (cachedView == null) { | ||||
|             val layout = provideLayout() ?: return null | ||||
|             if (layout is Int) { | ||||
|                 return inflater.inflate(layout, container, false).also { cachedView = it } | ||||
|             } | ||||
|             if (layout is View) { | ||||
|                 cachedView = layout | ||||
|                 return layout | ||||
|             } | ||||
|             throw IllegalArgumentException("Here you should provide  a  layout id  or a View") | ||||
|         } | ||||
| 
 | ||||
|         Timber.tag(tag()).d("mCachedView.parent: " + cachedView?.parent) | ||||
| 
 | ||||
|         cachedView?.run { | ||||
|             val viewParent = parent | ||||
|             if (viewParent != null && viewParent is ViewGroup) { | ||||
|                 viewParent.removeView(this) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return cachedView | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 使用此方法提供的布局,将只会被缓存起来,即此方法将只会被调用一次。 | ||||
|      * | ||||
|      * @return provide  a  layout id  or a View | ||||
|      */ | ||||
|     protected open fun provideLayout(): Any? = null | ||||
| 
 | ||||
|     final override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||
|         super.onViewCreated(view, savedInstanceState) | ||||
|         Timber.tag(tag()).d("-->onViewCreated  savedInstanceState = %s", savedInstanceState) | ||||
|         if (layoutView !== view) { | ||||
|             layoutView = view | ||||
|             internalOnViewPrepared(view, savedInstanceState) | ||||
|             onViewPrepared(view, savedInstanceState) | ||||
|         } | ||||
|         fragmentDelegates.onViewCreated(view, savedInstanceState) | ||||
|     } | ||||
| 
 | ||||
|     internal open fun internalOnViewPrepared(view: View, savedInstanceState: Bundle?) {} | ||||
| 
 | ||||
|     /** | ||||
|      * View is prepared, If [androidx.fragment.app.Fragment.onCreateView] return same layout, it will be called once | ||||
|      * | ||||
|      * @param view view of fragment | ||||
|      */ | ||||
|     protected open fun onViewPrepared(view: View, savedInstanceState: Bundle?) {} | ||||
| 
 | ||||
|     override fun onActivityCreated(savedInstanceState: Bundle?) { | ||||
|         super.onActivityCreated(savedInstanceState) | ||||
|         Timber.tag(tag()).d("-->onActivityCreated savedInstanceState   =   $savedInstanceState") | ||||
|         fragmentDelegates.onActivityCreated(savedInstanceState) | ||||
|     } | ||||
| 
 | ||||
|     override fun onStart() { | ||||
|         super.onStart() | ||||
|         Timber.tag(tag()).d("-->onStart") | ||||
|         fragmentDelegates.onStart() | ||||
|     } | ||||
| 
 | ||||
|     override fun onResume() { | ||||
|         super.onResume() | ||||
|         Timber.tag(tag()).d("-->onResume") | ||||
|         fragmentDelegates.onResume() | ||||
|     } | ||||
| 
 | ||||
|     override fun onPause() { | ||||
|         Timber.tag(tag()).d("-->onPause") | ||||
|         fragmentDelegates.onPause() | ||||
|         super.onPause() | ||||
|     } | ||||
| 
 | ||||
|     override fun onStop() { | ||||
|         Timber.tag(tag()).d("-->onStop") | ||||
|         fragmentDelegates.onStop() | ||||
|         super.onStop() | ||||
|     } | ||||
| 
 | ||||
|     override fun onDestroyView() { | ||||
|         Timber.tag(tag()).d("-->onDestroyView") | ||||
|         fragmentDelegates.onDestroyView() | ||||
|         super.onDestroyView() | ||||
|     } | ||||
| 
 | ||||
|     override fun onDestroy() { | ||||
|         Timber.tag(tag()).d("-->onDestroy") | ||||
|         fragmentDelegates.onDestroy() | ||||
|         super.onDestroy() | ||||
|         dismissLoadingDialog() | ||||
|     } | ||||
| 
 | ||||
|     override fun onDetach() { | ||||
|         Timber.tag(tag()).d("-->onDetach") | ||||
|         fragmentDelegates.onDetach() | ||||
|         super.onDetach() | ||||
|     } | ||||
| 
 | ||||
|     override fun onSaveInstanceState(outState: Bundle) { | ||||
|         fragmentDelegates.onSaveInstanceState(outState) | ||||
|         saveInstanceState(outState, _state) | ||||
|         super.onSaveInstanceState(outState) | ||||
|     } | ||||
| 
 | ||||
|     override fun setUserVisibleHint(isVisibleToUser: Boolean) { | ||||
|         super.setUserVisibleHint(isVisibleToUser) | ||||
|         Timber.tag(tag()).d("-->setUserVisibleHint ==$isVisibleToUser") | ||||
|         fragmentDelegates.setUserVisibleHint(isVisibleToUser) | ||||
|     } | ||||
| 
 | ||||
|     override fun onHiddenChanged(hidden: Boolean) { | ||||
|         super.onHiddenChanged(hidden) | ||||
|         Timber.tag(tag()).d("-->onHiddenChanged = $hidden") | ||||
|         fragmentDelegates.onHiddenChanged(hidden) | ||||
|     } | ||||
| 
 | ||||
|     override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { | ||||
|         super.onRequestPermissionsResult(requestCode, permissions, grantResults) | ||||
|         fragmentDelegates.onRequestPermissionsResult(requestCode, permissions, grantResults) | ||||
|     } | ||||
| 
 | ||||
|     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { | ||||
|         super.onActivityResult(requestCode, resultCode, data) | ||||
|         fragmentDelegates.onActivityResult(requestCode, resultCode, data) | ||||
|     } | ||||
| 
 | ||||
|     @UiThread | ||||
|     override fun addDelegate(fragmentDelegate: FragmentDelegate<*>?) { | ||||
|         fragmentDelegates.addDelegate(fragmentDelegate) | ||||
|     } | ||||
| 
 | ||||
|     @UiThread | ||||
|     override fun removeDelegate(fragmentDelegate: FragmentDelegate<*>?): Boolean { | ||||
|         return fragmentDelegates.removeDelegate(fragmentDelegate) | ||||
|     } | ||||
| 
 | ||||
|     override fun findDelegate(predicate: Predicate<FragmentDelegate<*>?>?): FragmentDelegate<*>? { | ||||
|         return fragmentDelegates.findDelegate(predicate) | ||||
|     } | ||||
| 
 | ||||
|     final override fun onBackPressed(): Boolean { | ||||
|         return handleBackPress() || BackHandlerHelper.handleBackPress(this) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Fragment需要自己处理BackPress事件,如果不处理,就交给子Fragment处理。都不处理则由Activity处理 | ||||
|      */ | ||||
|     protected open fun handleBackPress(): Boolean { | ||||
|         return false | ||||
|     } | ||||
| 
 | ||||
|     private fun loadingView(): LoadingView { | ||||
|         val loadingViewImpl = loadingView | ||||
|         return if (loadingViewImpl != null) { | ||||
|             loadingViewImpl | ||||
|         } else { | ||||
|             loadingView = onCreateLoadingView() ?: Sword.loadingViewFactory?.invoke(requireContext()) | ||||
|             loadingView ?: throw NullPointerException("you need to config LoadingViewFactory in Sword or implement onCreateLoadingView.") | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected open fun onCreateLoadingView(): LoadingView? { | ||||
|         return null | ||||
|     } | ||||
| 
 | ||||
|     override fun showLoadingDialog() { | ||||
|         recentShowingDialogTime = System.currentTimeMillis() | ||||
|         loadingView().showLoadingDialog(true) | ||||
|     } | ||||
| 
 | ||||
|     override fun showLoadingDialog(cancelable: Boolean) { | ||||
|         recentShowingDialogTime = System.currentTimeMillis() | ||||
|         loadingView().showLoadingDialog(cancelable) | ||||
|     } | ||||
| 
 | ||||
|     override fun showLoadingDialog(message: CharSequence, cancelable: Boolean) { | ||||
|         recentShowingDialogTime = System.currentTimeMillis() | ||||
|         loadingView().showLoadingDialog(message, cancelable) | ||||
|     } | ||||
| 
 | ||||
|     override fun showLoadingDialog(@StringRes messageId: Int, cancelable: Boolean) { | ||||
|         recentShowingDialogTime = System.currentTimeMillis() | ||||
|         loadingView().showLoadingDialog(messageId, cancelable) | ||||
|     } | ||||
| 
 | ||||
|     override fun dismissLoadingDialog() { | ||||
|         loadingView().dismissLoadingDialog() | ||||
|     } | ||||
| 
 | ||||
|     override fun dismissLoadingDialog(minimumMills: Long, onDismiss: () -> Unit) { | ||||
|         dismissDialog(recentShowingDialogTime, minimumMills, onDismiss) | ||||
|     } | ||||
| 
 | ||||
|     override fun isLoadingDialogShowing(): Boolean { | ||||
|         return loadingView().isLoadingDialogShowing() | ||||
|     } | ||||
| 
 | ||||
|     override fun showMessage(message: CharSequence) { | ||||
|         loadingView().showMessage(message) | ||||
|     } | ||||
| 
 | ||||
|     override fun showMessage(@StringRes messageId: Int) { | ||||
|         loadingView().showMessage(messageId) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,295 @@ | ||||
| package com.android.base.app.fragment | ||||
| 
 | ||||
| import android.content.Context | ||||
| import android.content.Intent | ||||
| import android.os.Bundle | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import android.view.animation.Animation | ||||
| import androidx.annotation.StringRes | ||||
| import androidx.annotation.UiThread | ||||
| import androidx.fragment.app.Fragment | ||||
| import com.android.base.app.Sword | ||||
| import com.android.base.app.activity.BackHandlerHelper | ||||
| import com.android.base.app.activity.OnBackPressListener | ||||
| import com.android.base.app.fragment.animator.FragmentAnimatorHelper | ||||
| import com.android.base.app.fragment.delegates.FragmentDelegate | ||||
| import com.android.base.app.fragment.delegates.FragmentDelegateOwner | ||||
| import com.android.base.app.fragment.tools.FragmentConfig | ||||
| import com.android.base.app.ui.LoadingView | ||||
| import com.android.base.rx.autodispose.AutoDisposeLifecycleOwnerEx | ||||
| import com.github.dmstocking.optional.java.util.function.Predicate | ||||
| import timber.log.Timber | ||||
| 
 | ||||
| /** | ||||
|  *基础 BaseFragment 封装: | ||||
|  * | ||||
|  * 1. RxJava 生命周期绑定。 | ||||
|  * 2. 返回键监听。 | ||||
|  * 3. 显示 LoadingDialog 和 Message。 | ||||
|  * 4. 可以添加生命周期代理。 | ||||
|  * | ||||
|  * @author Ztiany | ||||
|  * date :   2016-03-19 23:09 | ||||
|  * email:    1169654504@qq.com | ||||
|  */ | ||||
| open class BaseFragment : Fragment(), LoadingView, OnBackPressListener, FragmentDelegateOwner, AutoDisposeLifecycleOwnerEx { | ||||
| 
 | ||||
|     private var loadingView: LoadingView? = null | ||||
| 
 | ||||
|     private var layoutView: View? = null | ||||
| 
 | ||||
|     /** just for cache*/ | ||||
|     private var cachedView: View? = null | ||||
| 
 | ||||
|     private var fragmentAnimatorHelper: FragmentAnimatorHelper? = null | ||||
| 
 | ||||
|     @Suppress("LeakingThis") | ||||
|     private val fragmentDelegates = FragmentDelegates(this) | ||||
| 
 | ||||
|     private var recentShowingDialogTime: Long = 0 | ||||
| 
 | ||||
|     private var _state: Bundle? = null | ||||
| 
 | ||||
|     protected val state: Bundle | ||||
|         get() { | ||||
|             var state = _state | ||||
|             if (state == null) { | ||||
|                 state = Bundle() | ||||
|                 _state = state | ||||
|             } | ||||
|             return state | ||||
|         } | ||||
| 
 | ||||
|     private fun tag() = this.javaClass.simpleName | ||||
| 
 | ||||
|     override fun onAttach(context: Context) { | ||||
|         super.onAttach(context) | ||||
|         Timber.tag(tag()).d("onAttach() called with: context = [$context]") | ||||
|         fragmentDelegates.onAttach(context) | ||||
|     } | ||||
| 
 | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
|         super.onCreate(savedInstanceState) | ||||
|         _state = getInstanceState(savedInstanceState) | ||||
|         Timber.tag(tag()).d("-->onCreate  savedInstanceState   =   $savedInstanceState") | ||||
|         fragmentDelegates.onCreate(savedInstanceState) | ||||
|     } | ||||
| 
 | ||||
|     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { | ||||
|         if (cachedView == null) { | ||||
|             val layout = provideLayout() ?: return null | ||||
|             if (layout is Int) { | ||||
|                 return inflater.inflate(layout, container, false).also { cachedView = it } | ||||
|             } | ||||
|             if (layout is View) { | ||||
|                 cachedView = layout | ||||
|                 return layout | ||||
|             } | ||||
|             throw IllegalArgumentException("Here you should provide  a  layout id  or a View") | ||||
|         } | ||||
| 
 | ||||
|         Timber.tag(tag()).d("mCachedView.parent: " + cachedView?.parent) | ||||
| 
 | ||||
|         cachedView?.run { | ||||
|             val viewParent = parent | ||||
|             if (viewParent != null && viewParent is ViewGroup) { | ||||
|                 viewParent.removeView(this) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return cachedView | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 使用此方法提供的布局,将只会被缓存起来,即此方法将只会被调用一次。 | ||||
|      * | ||||
|      * @return provide  a  layout id  or a View | ||||
|      */ | ||||
|     protected open fun provideLayout(): Any? = null | ||||
| 
 | ||||
|     final override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||
|         super.onViewCreated(view, savedInstanceState) | ||||
|         Timber.tag(tag()).d("-->onViewCreated  savedInstanceState = %s", savedInstanceState) | ||||
|         if (layoutView !== view) { | ||||
|             layoutView = view | ||||
|             internalOnViewPrepared(view, savedInstanceState) | ||||
|             onViewPrepared(view, savedInstanceState) | ||||
|         } | ||||
|         fragmentDelegates.onViewCreated(view, savedInstanceState) | ||||
|     } | ||||
| 
 | ||||
|     internal open fun internalOnViewPrepared(view: View, savedInstanceState: Bundle?) {} | ||||
| 
 | ||||
|     /** | ||||
|      * View is prepared, If [androidx.fragment.app.Fragment.onCreateView] return same layout, it will be called once | ||||
|      * | ||||
|      * @param view view of fragment | ||||
|      */ | ||||
|     protected open fun onViewPrepared(view: View, savedInstanceState: Bundle?) {} | ||||
| 
 | ||||
|     override fun onActivityCreated(savedInstanceState: Bundle?) { | ||||
|         super.onActivityCreated(savedInstanceState) | ||||
|         Timber.tag(tag()).d("-->onActivityCreated savedInstanceState   =   $savedInstanceState") | ||||
|         fragmentDelegates.onActivityCreated(savedInstanceState) | ||||
|     } | ||||
| 
 | ||||
|     override fun onStart() { | ||||
|         super.onStart() | ||||
|         Timber.tag(tag()).d("-->onStart") | ||||
|         fragmentDelegates.onStart() | ||||
|     } | ||||
| 
 | ||||
|     override fun onResume() { | ||||
|         super.onResume() | ||||
|         Timber.tag(tag()).d("-->onResume") | ||||
|         fragmentDelegates.onResume() | ||||
|     } | ||||
| 
 | ||||
|     override fun onPause() { | ||||
|         Timber.tag(tag()).d("-->onPause") | ||||
|         fragmentDelegates.onPause() | ||||
|         super.onPause() | ||||
|     } | ||||
| 
 | ||||
|     override fun onStop() { | ||||
|         Timber.tag(tag()).d("-->onStop") | ||||
|         fragmentDelegates.onStop() | ||||
|         super.onStop() | ||||
|     } | ||||
| 
 | ||||
|     override fun onDestroyView() { | ||||
|         Timber.tag(tag()).d("-->onDestroyView") | ||||
|         fragmentDelegates.onDestroyView() | ||||
|         super.onDestroyView() | ||||
|     } | ||||
| 
 | ||||
|     override fun onDestroy() { | ||||
|         Timber.tag(tag()).d("-->onDestroy") | ||||
|         fragmentDelegates.onDestroy() | ||||
|         super.onDestroy() | ||||
|         dismissLoadingDialog() | ||||
|     } | ||||
| 
 | ||||
|     override fun onDetach() { | ||||
|         Timber.tag(tag()).d("-->onDetach") | ||||
|         fragmentDelegates.onDetach() | ||||
|         super.onDetach() | ||||
|     } | ||||
| 
 | ||||
|     override fun onSaveInstanceState(outState: Bundle) { | ||||
|         fragmentDelegates.onSaveInstanceState(outState) | ||||
|         saveInstanceState(outState, _state) | ||||
|         super.onSaveInstanceState(outState) | ||||
|     } | ||||
| 
 | ||||
|     override fun setUserVisibleHint(isVisibleToUser: Boolean) { | ||||
|         super.setUserVisibleHint(isVisibleToUser) | ||||
|         Timber.tag(tag()).d("-->setUserVisibleHint ==$isVisibleToUser") | ||||
|         fragmentDelegates.setUserVisibleHint(isVisibleToUser) | ||||
|     } | ||||
| 
 | ||||
|     override fun onHiddenChanged(hidden: Boolean) { | ||||
|         super.onHiddenChanged(hidden) | ||||
|         Timber.tag(tag()).d("-->onHiddenChanged = $hidden") | ||||
|         fragmentDelegates.onHiddenChanged(hidden) | ||||
|     } | ||||
| 
 | ||||
|     override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { | ||||
|         super.onRequestPermissionsResult(requestCode, permissions, grantResults) | ||||
|         fragmentDelegates.onRequestPermissionsResult(requestCode, permissions, grantResults) | ||||
|     } | ||||
| 
 | ||||
|     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { | ||||
|         super.onActivityResult(requestCode, resultCode, data) | ||||
|         fragmentDelegates.onActivityResult(requestCode, resultCode, data) | ||||
|     } | ||||
| 
 | ||||
|     @UiThread | ||||
|     override fun addDelegate(fragmentDelegate: FragmentDelegate<*>?) { | ||||
|         fragmentDelegates.addDelegate(fragmentDelegate) | ||||
|     } | ||||
| 
 | ||||
|     @UiThread | ||||
|     override fun removeDelegate(fragmentDelegate: FragmentDelegate<*>?): Boolean { | ||||
|         return fragmentDelegates.removeDelegate(fragmentDelegate) | ||||
|     } | ||||
| 
 | ||||
|     override fun findDelegate(predicate: Predicate<FragmentDelegate<*>?>?): FragmentDelegate<*>? { | ||||
|         return fragmentDelegates.findDelegate(predicate) | ||||
|     } | ||||
| 
 | ||||
|     final override fun onBackPressed(): Boolean { | ||||
|         return handleBackPress() || BackHandlerHelper.handleBackPress(this) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Fragment需要自己处理BackPress事件,如果不处理,就交给子Fragment处理。都不处理则由Activity处理 | ||||
|      */ | ||||
|     protected open fun handleBackPress(): Boolean { | ||||
|         return false | ||||
|     } | ||||
| 
 | ||||
|     private fun loadingView(): LoadingView { | ||||
|         val loadingViewImpl = loadingView | ||||
|         return if (loadingViewImpl != null) { | ||||
|             loadingViewImpl | ||||
|         } else { | ||||
|             loadingView = onCreateLoadingView() ?: Sword.loadingViewFactory?.invoke(requireContext()) | ||||
|             loadingView ?: throw NullPointerException("you need to config LoadingViewFactory in Sword or implement onCreateLoadingView.") | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected open fun onCreateLoadingView(): LoadingView? { | ||||
|         return null | ||||
|     } | ||||
| 
 | ||||
|     override fun showLoadingDialog() { | ||||
|         recentShowingDialogTime = System.currentTimeMillis() | ||||
|         loadingView().showLoadingDialog(true) | ||||
|     } | ||||
| 
 | ||||
|     override fun showLoadingDialog(cancelable: Boolean) { | ||||
|         recentShowingDialogTime = System.currentTimeMillis() | ||||
|         loadingView().showLoadingDialog(cancelable) | ||||
|     } | ||||
| 
 | ||||
|     override fun showLoadingDialog(message: CharSequence, cancelable: Boolean) { | ||||
|         recentShowingDialogTime = System.currentTimeMillis() | ||||
|         loadingView().showLoadingDialog(message, cancelable) | ||||
|     } | ||||
| 
 | ||||
|     override fun showLoadingDialog(@StringRes messageId: Int, cancelable: Boolean) { | ||||
|         recentShowingDialogTime = System.currentTimeMillis() | ||||
|         loadingView().showLoadingDialog(messageId, cancelable) | ||||
|     } | ||||
| 
 | ||||
|     override fun dismissLoadingDialog() { | ||||
|         loadingView().dismissLoadingDialog() | ||||
|     } | ||||
| 
 | ||||
|     override fun dismissLoadingDialog(minimumMills: Long, onDismiss: () -> Unit) { | ||||
|         dismissDialog(recentShowingDialogTime, minimumMills, onDismiss) | ||||
|     } | ||||
| 
 | ||||
|     override fun isLoadingDialogShowing(): Boolean { | ||||
|         return loadingView().isLoadingDialogShowing() | ||||
|     } | ||||
| 
 | ||||
|     override fun showMessage(message: CharSequence) { | ||||
|         loadingView().showMessage(message) | ||||
|     } | ||||
| 
 | ||||
|     override fun showMessage(@StringRes messageId: Int) { | ||||
|         loadingView().showMessage(messageId) | ||||
|     } | ||||
| 
 | ||||
|     override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? { | ||||
|         if (fragmentAnimatorHelper == null) { | ||||
|             fragmentAnimatorHelper = FragmentAnimatorHelper(requireContext(), FragmentConfig.defaultFragmentAnimator()) | ||||
|         } | ||||
|         return fragmentAnimatorHelper?.onCreateAnimation(transit, enter) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,89 @@ | ||||
| package com.android.base.app.fragment | ||||
| 
 | ||||
| import android.os.Bundle | ||||
| import androidx.recyclerview.widget.RecyclerView.Adapter | ||||
| import com.android.base.adapter.DataManager | ||||
| import com.android.base.app.ui.AutoPageNumber | ||||
| import com.android.base.app.ui.PageNumber | ||||
| import com.android.base.app.ui.RefreshListLayout | ||||
| import com.ztiany.loadmore.adapter.ILoadMore | ||||
| import com.ztiany.loadmore.adapter.OnLoadMoreListener | ||||
| import com.ztiany.loadmore.adapter.WrapperAdapter | ||||
| 
 | ||||
| /** | ||||
|  * @author Ztiany | ||||
|  * date :   2016-03-19 23:09 | ||||
|  * email:    1169654504@qq.com | ||||
|  * @see [BaseListFragment] | ||||
|  */ | ||||
| abstract class BaseListDialogFragment<T> : BaseStateDialogFragment(), RefreshListLayout<T> { | ||||
| 
 | ||||
|     /**加载更多*/ | ||||
|     private var loadMore: ILoadMore? = null | ||||
| 
 | ||||
|     /**列表数据管理*/ | ||||
|     private lateinit var dataManager: DataManager<T> | ||||
| 
 | ||||
|     /**分页页码*/ | ||||
|     private var pageNumber: PageNumber? = null | ||||
| 
 | ||||
|     override fun onActivityCreated(savedInstanceState: Bundle?) { | ||||
|         super.onActivityCreated(savedInstanceState) | ||||
|         if (!::dataManager.isInitialized) { | ||||
|             throw NullPointerException("you need set DataManager") | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected fun setDataManager(dataManager: DataManager<T>) { | ||||
|         this.dataManager = dataManager | ||||
|     } | ||||
| 
 | ||||
|     protected fun setupLoadMore(recyclerAdapter: Adapter<*>, pageNumber: PageNumber = AutoPageNumber(this, dataManager)): Adapter<*> { | ||||
|         this.pageNumber = pageNumber | ||||
| 
 | ||||
|         return WrapperAdapter.wrap(recyclerAdapter).apply { | ||||
|             setOnLoadMoreListener(object : OnLoadMoreListener { | ||||
|                 override fun onLoadMore() { | ||||
|                     this@BaseListDialogFragment.onLoadMore() | ||||
|                 } | ||||
| 
 | ||||
|                 override fun canLoadMore(): Boolean { | ||||
|                     return !isRefreshing | ||||
|                 } | ||||
|             }) | ||||
|             loadMore = this | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /**call by [.onRefresh] or [.onLoadMore], you can get current loading type from [.isRefreshing] or [.isLoadingMore].*/ | ||||
|     protected open fun onStartLoad() {} | ||||
| 
 | ||||
|     override fun onRefresh() = onStartLoad() | ||||
| 
 | ||||
|     protected fun onLoadMore() = onStartLoad() | ||||
| 
 | ||||
|     override fun canRefresh() = !isLoadingMore | ||||
| 
 | ||||
|     override fun replaceData(data: List<T>) = dataManager.replaceAll(data) | ||||
| 
 | ||||
|     override fun addData(data: List<T>) = dataManager.addItems(data) | ||||
| 
 | ||||
|     override fun isEmpty(): Boolean = dataManager.isEmpty | ||||
| 
 | ||||
|     override fun isLoadingMore(): Boolean = loadMore != null && loadMore?.isLoadingMore ?: false | ||||
| 
 | ||||
|     override fun getPager(): PageNumber = pageNumber | ||||
|             ?: throw NullPointerException("you need to call setupLoadMore to init pageNumber") | ||||
| 
 | ||||
|     fun loadMoreController(): ILoadMore = loadMore | ||||
|             ?: throw NullPointerException("you need to call setupLoadMore to init loadMoreController") | ||||
| 
 | ||||
|     override fun loadMoreCompleted(hasMore: Boolean) { | ||||
|         loadMore?.loadCompleted(hasMore) | ||||
|     } | ||||
| 
 | ||||
|     override fun loadMoreFailed() { | ||||
|         loadMore?.loadFail() | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,89 @@ | ||||
| package com.android.base.app.fragment | ||||
| 
 | ||||
| import android.os.Bundle | ||||
| import androidx.recyclerview.widget.RecyclerView.Adapter | ||||
| import com.android.base.adapter.DataManager | ||||
| import com.android.base.app.ui.AutoPageNumber | ||||
| import com.android.base.app.ui.PageNumber | ||||
| import com.android.base.app.ui.RefreshListLayout | ||||
| import com.ztiany.loadmore.adapter.ILoadMore | ||||
| import com.ztiany.loadmore.adapter.OnLoadMoreListener | ||||
| import com.ztiany.loadmore.adapter.WrapperAdapter | ||||
| 
 | ||||
| /** | ||||
|  * 通用的基于 RecyclerView 的列表界面,支持下拉刷新和加载更多。 | ||||
|  * | ||||
|  * @param <T> 当前列表使用的数据类型 | ||||
|  * @author Ztiany | ||||
|  * date :   2016-03-19 23:09 | ||||
|  * email:    1169654504@qq.com | ||||
|  */ | ||||
| abstract class BaseListFragment<T> : BaseStateFragment(), RefreshListLayout<T> { | ||||
| 
 | ||||
|     /**加载更多*/ | ||||
|     private var loadMore: ILoadMore? = null | ||||
| 
 | ||||
|     /**列表数据管理*/ | ||||
|     private lateinit var dataManager: DataManager<T> | ||||
| 
 | ||||
|     /**分页页码*/ | ||||
|     private var pageNumber: PageNumber? = null | ||||
| 
 | ||||
|     override fun onActivityCreated(savedInstanceState: Bundle?) { | ||||
|         super.onActivityCreated(savedInstanceState) | ||||
|         if (!::dataManager.isInitialized) { | ||||
|             throw NullPointerException("you need set DataManager") | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected fun setDataManager(dataManager: DataManager<T>) { | ||||
|         this.dataManager = dataManager | ||||
|     } | ||||
| 
 | ||||
|     protected fun setupLoadMore(recyclerAdapter: Adapter<*>, pageNumber: PageNumber = AutoPageNumber(this, dataManager)): Adapter<*> { | ||||
|         this.pageNumber = pageNumber | ||||
| 
 | ||||
|         return WrapperAdapter.wrap(recyclerAdapter).apply { | ||||
|             setOnLoadMoreListener(object : OnLoadMoreListener { | ||||
|                 override fun onLoadMore() { | ||||
|                     this@BaseListFragment.onLoadMore() | ||||
|                 } | ||||
| 
 | ||||
|                 override fun canLoadMore(): Boolean { | ||||
|                     return !isRefreshing | ||||
|                 } | ||||
|             }) | ||||
|             loadMore = this | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /**call by [.onRefresh] or [.onLoadMore], you can get current loading type from [.isRefreshing] or [.isLoadingMore].*/ | ||||
|     protected open fun onStartLoad() {} | ||||
| 
 | ||||
|     override fun onRefresh() = onStartLoad() | ||||
| 
 | ||||
|     protected fun onLoadMore() = onStartLoad() | ||||
| 
 | ||||
|     override fun canRefresh() = !isLoadingMore | ||||
| 
 | ||||
|     override fun replaceData(data: List<T>)=  dataManager.replaceAll(data) | ||||
| 
 | ||||
|     override fun addData(data: List<T>) =   dataManager.addItems(data) | ||||
| 
 | ||||
|     override fun isEmpty(): Boolean = dataManager.isEmpty | ||||
| 
 | ||||
|     override fun isLoadingMore(): Boolean = loadMore != null && loadMore?.isLoadingMore ?: false | ||||
| 
 | ||||
|     override fun getPager(): PageNumber = pageNumber ?: throw NullPointerException("you need to call setupLoadMore to init pageNumber") | ||||
| 
 | ||||
|     fun loadMoreController(): ILoadMore = loadMore ?: throw NullPointerException("you need to call setupLoadMore to init loadMoreController") | ||||
| 
 | ||||
|     override fun loadMoreCompleted(hasMore: Boolean) { | ||||
|         loadMore?.loadCompleted(hasMore) | ||||
|     } | ||||
| 
 | ||||
|     override fun loadMoreFailed() { | ||||
|         loadMore?.loadFail() | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,119 @@ | ||||
| package com.android.base.app.fragment | ||||
| 
 | ||||
| import android.os.Bundle | ||||
| import android.view.View | ||||
| import com.android.base.adapter.DataManager | ||||
| import com.android.base.app.ui.AutoPageNumber | ||||
| import com.android.base.app.ui.RefreshListLayout | ||||
| import com.android.base.app.ui.StateLayoutConfig | ||||
| 
 | ||||
| /** | ||||
|  * 区别于 [BaseListFragment] 只能支持 RecyclerView。[BaseListFragment] 采用包装 [androidx.recyclerview.widget.RecyclerView.Adapter] 的方式, | ||||
|  * 在底部添加 load more view 的 item,来实现加载更多。[BaseListV2DialogFragment] 没有采用此种方式,所以你使用的 RefreshView 应该是支持这下来刷新和加载更多功能的。 | ||||
|  * | ||||
|  * 在调用 [BaseListV2DialogFragment] 的 [onActivityCreated] 之前,你应该设置好 [dataManager]。 | ||||
|  * | ||||
|  *@author Ztiany | ||||
|  *      Email: ztiany3@gmail.com | ||||
|  *      Date : 2019-03-26 15:06 | ||||
|  */ | ||||
| abstract class BaseListV2DialogFragment<T> : BaseFragment(), RefreshListLayout<T> { | ||||
| 
 | ||||
|     private lateinit var stateLayout: RefreshLoadMoreStateLayoutImpl | ||||
| 
 | ||||
|     protected open lateinit var dataManager: DataManager<T> | ||||
| 
 | ||||
|     override fun internalOnViewPrepared(view: View, savedInstanceState: Bundle?) { | ||||
|         stateLayout = RefreshLoadMoreStateLayoutImpl.init(view) | ||||
|         stateLayout.refreshView.setRefreshHandler { | ||||
|             onRefresh() | ||||
|         } | ||||
|         stateLayout.refreshView.setLoadMoreHandler { | ||||
|             onLoadMore() | ||||
|         } | ||||
|         stateLayout.setStateRetryListener(this::onRetry) | ||||
|     } | ||||
| 
 | ||||
|     protected open fun onRetry(state: Int) { | ||||
|         if (!isRefreshing) { | ||||
|             autoRefresh() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected open fun onRefresh() = onStartLoad() | ||||
| 
 | ||||
|     protected open fun onLoadMore() = onStartLoad() | ||||
| 
 | ||||
|     /** call by [onRefresh] or [onLoadMore], you can get current loading type from [isRefreshing] or [isLoadingMore]. */ | ||||
|     protected open fun onStartLoad() {} | ||||
| 
 | ||||
|     override fun onActivityCreated(savedInstanceState: Bundle?) { | ||||
|         super.onActivityCreated(savedInstanceState) | ||||
|         if (!::dataManager.isInitialized) { | ||||
|             throw NullPointerException("you need set DataManager") | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun onDestroyView() { | ||||
|         super.onDestroyView() | ||||
|         refreshCompleted() | ||||
|     } | ||||
| 
 | ||||
|     override fun replaceData(data: MutableList<T>?) { | ||||
|         dataManager.replaceAll(data) | ||||
|     } | ||||
| 
 | ||||
|     override fun addData(data: MutableList<T>?) { | ||||
|         dataManager.addItems(data) | ||||
|     } | ||||
| 
 | ||||
|     fun setRefreshEnable(enable: Boolean) = stateLayout.refreshView.setRefreshEnable(enable) | ||||
| 
 | ||||
|     fun setLoadMoreEnable(enable: Boolean) = stateLayout.refreshView.setLoadMoreEnable(enable) | ||||
| 
 | ||||
|     override fun getPager() = AutoPageNumber(this, dataManager) | ||||
| 
 | ||||
|     override fun isEmpty() = dataManager.isEmpty | ||||
| 
 | ||||
|     override fun loadMoreCompleted(hasMore: Boolean) = stateLayout.refreshView.loadMoreCompleted(hasMore) | ||||
| 
 | ||||
|     override fun loadMoreFailed() = stateLayout.refreshView.loadMoreFailed() | ||||
| 
 | ||||
|     override fun isRefreshing() = stateLayout.refreshView.isRefreshing | ||||
| 
 | ||||
|     override fun showContentLayout() = stateLayout.showContentLayout() | ||||
| 
 | ||||
|     override fun showLoadingLayout() = stateLayout.showLoadingLayout() | ||||
| 
 | ||||
|     override fun refreshCompleted() = stateLayout.refreshView.refreshCompleted() | ||||
| 
 | ||||
|     override fun showEmptyLayout() = stateLayout.showEmptyLayout() | ||||
| 
 | ||||
|     override fun showErrorLayout() = stateLayout.showErrorLayout() | ||||
| 
 | ||||
|     override fun showRequesting() = stateLayout.showRequesting() | ||||
| 
 | ||||
|     override fun showBlank() = stateLayout.showBlank() | ||||
| 
 | ||||
|     override fun getStateLayoutConfig() = stateLayout.stateLayoutConfig | ||||
| 
 | ||||
|     override fun autoRefresh() = stateLayout.refreshView.autoRefresh() | ||||
| 
 | ||||
|     override fun showNetErrorLayout() = stateLayout.showNetErrorLayout() | ||||
| 
 | ||||
|     override fun showServerErrorLayout() = stateLayout.showServerErrorLayout() | ||||
| 
 | ||||
|     override fun isLoadingMore() = stateLayout.refreshView.isLoadingMore | ||||
| 
 | ||||
|     override fun currentStatus() = stateLayout.currentStatus() | ||||
| 
 | ||||
|     companion object { | ||||
|         const val CONTENT = StateLayoutConfig.CONTENT | ||||
|         const val LOADING = StateLayoutConfig.LOADING | ||||
|         const val ERROR = StateLayoutConfig.ERROR | ||||
|         const val EMPTY = StateLayoutConfig.EMPTY | ||||
|         const val NET_ERROR = StateLayoutConfig.NET_ERROR | ||||
|         const val SERVER_ERROR = StateLayoutConfig.SERVER_ERROR | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,115 @@ | ||||
| package com.android.base.app.fragment | ||||
| 
 | ||||
| import android.os.Bundle | ||||
| import android.view.View | ||||
| import com.android.base.adapter.DataManager | ||||
| import com.android.base.app.ui.AutoPageNumber | ||||
| import com.android.base.app.ui.RefreshListLayout | ||||
| import com.android.base.app.ui.StateLayoutConfig | ||||
| 
 | ||||
| /** | ||||
|  *@author Ztiany | ||||
|  *      Email: ztiany3@gmail.com | ||||
|  *      Date : 2019-03-26 15:06 | ||||
|  *@see [BaseListV2Fragment] | ||||
|  */ | ||||
| abstract class BaseListV2Fragment<T> : BaseDialogFragment(), RefreshListLayout<T> { | ||||
| 
 | ||||
|     private lateinit var stateLayout: RefreshLoadMoreStateLayoutImpl | ||||
| 
 | ||||
|     protected open lateinit var dataManager: DataManager<T> | ||||
| 
 | ||||
|     override fun internalOnViewPrepared(view: View, savedInstanceState: Bundle?) { | ||||
|         stateLayout = RefreshLoadMoreStateLayoutImpl.init(view) | ||||
|         stateLayout.refreshView.setRefreshHandler { | ||||
|             onRefresh() | ||||
|         } | ||||
|         stateLayout.refreshView.setLoadMoreHandler { | ||||
|             onLoadMore() | ||||
|         } | ||||
|         stateLayout.setStateRetryListener(this::onRetry) | ||||
|     } | ||||
| 
 | ||||
|     protected open fun onRetry(state: Int) { | ||||
|         if (!isRefreshing) { | ||||
|             autoRefresh() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected open fun onRefresh() = onStartLoad() | ||||
| 
 | ||||
|     protected open fun onLoadMore() = onStartLoad() | ||||
| 
 | ||||
|     /** call by [onRefresh] or [onLoadMore], you can get current loading type from [isRefreshing] or [isLoadingMore]. */ | ||||
|     protected open fun onStartLoad() {} | ||||
| 
 | ||||
|     override fun onActivityCreated(savedInstanceState: Bundle?) { | ||||
|         super.onActivityCreated(savedInstanceState) | ||||
|         if (!::dataManager.isInitialized) { | ||||
|             throw NullPointerException("you need set DataManager") | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun onDestroyView() { | ||||
|         super.onDestroyView() | ||||
|         refreshCompleted() | ||||
|     } | ||||
| 
 | ||||
|     override fun replaceData(data: MutableList<T>?) { | ||||
|         dataManager.replaceAll(data) | ||||
|     } | ||||
| 
 | ||||
|     override fun addData(data: MutableList<T>?) { | ||||
|         dataManager.addItems(data) | ||||
|     } | ||||
| 
 | ||||
|     fun setRefreshEnable(enable: Boolean) = stateLayout.refreshView.setRefreshEnable(enable) | ||||
| 
 | ||||
|     fun setLoadMoreEnable(enable: Boolean) = stateLayout.refreshView.setLoadMoreEnable(enable) | ||||
| 
 | ||||
|     override fun getPager() = AutoPageNumber(this, dataManager) | ||||
| 
 | ||||
|     override fun isEmpty() = dataManager.isEmpty | ||||
| 
 | ||||
|     override fun loadMoreCompleted(hasMore: Boolean) = stateLayout.refreshView.loadMoreCompleted(hasMore) | ||||
| 
 | ||||
|     override fun loadMoreFailed() = stateLayout.refreshView.loadMoreFailed() | ||||
| 
 | ||||
|     override fun isRefreshing() = stateLayout.refreshView.isRefreshing | ||||
| 
 | ||||
|     override fun showContentLayout() = stateLayout.showContentLayout() | ||||
| 
 | ||||
|     override fun showLoadingLayout() = stateLayout.showLoadingLayout() | ||||
| 
 | ||||
|     override fun refreshCompleted() = stateLayout.refreshView.refreshCompleted() | ||||
| 
 | ||||
|     override fun showEmptyLayout() = stateLayout.showEmptyLayout() | ||||
| 
 | ||||
|     override fun showErrorLayout() = stateLayout.showErrorLayout() | ||||
| 
 | ||||
|     override fun showRequesting() = stateLayout.showRequesting() | ||||
| 
 | ||||
|     override fun showBlank() = stateLayout.showBlank() | ||||
| 
 | ||||
|     override fun getStateLayoutConfig() = stateLayout.stateLayoutConfig | ||||
| 
 | ||||
|     override fun autoRefresh() = stateLayout.refreshView.autoRefresh() | ||||
| 
 | ||||
|     override fun showNetErrorLayout() = stateLayout.showNetErrorLayout() | ||||
| 
 | ||||
|     override fun showServerErrorLayout() = stateLayout.showServerErrorLayout() | ||||
| 
 | ||||
|     override fun isLoadingMore() = stateLayout.refreshView.isLoadingMore | ||||
| 
 | ||||
|     override fun currentStatus() = stateLayout.currentStatus() | ||||
| 
 | ||||
|     companion object { | ||||
|         const val CONTENT = StateLayoutConfig.CONTENT | ||||
|         const val LOADING = StateLayoutConfig.LOADING | ||||
|         const val ERROR = StateLayoutConfig.ERROR | ||||
|         const val EMPTY = StateLayoutConfig.EMPTY | ||||
|         const val NET_ERROR = StateLayoutConfig.NET_ERROR | ||||
|         const val SERVER_ERROR = StateLayoutConfig.SERVER_ERROR | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,94 @@ | ||||
| package com.android.base.app.fragment | ||||
| 
 | ||||
| import android.os.Bundle | ||||
| import android.view.View | ||||
| import com.android.base.app.ui.RefreshStateLayout | ||||
| import com.android.base.app.ui.RefreshView | ||||
| import com.android.base.app.ui.StateLayoutConfig | ||||
| import com.android.base.utils.common.ifNonNull | ||||
| import com.android.base.utils.common.otherwise | ||||
| 
 | ||||
| /** | ||||
|  * @author Ztiany | ||||
|  * date :   2016-03-19 23:09 | ||||
|  * email:    1169654504@qq.com | ||||
|  * @see BaseStateFragment | ||||
|  */ | ||||
| abstract class BaseStateDialogFragment : BaseDialogFragment(), RefreshStateLayout { | ||||
| 
 | ||||
|     private lateinit var stateLayout: RefreshableStateLayoutImpl | ||||
| 
 | ||||
|     override fun internalOnViewPrepared(view: View, savedInstanceState: Bundle?) { | ||||
|         stateLayout = RefreshableStateLayoutImpl.init(view) | ||||
|         stateLayout.setRefreshHandler(object : RefreshView.RefreshHandler() { | ||||
|             override fun onRefresh() { | ||||
|                 this@BaseStateDialogFragment.onRefresh() | ||||
|             } | ||||
| 
 | ||||
|             override fun canRefresh(): Boolean { | ||||
|                 return this@BaseStateDialogFragment.canRefresh() | ||||
|             } | ||||
|         }) | ||||
|         stateLayout.setStateRetryListenerUnchecked { state -> onRetry(state) } | ||||
|     } | ||||
| 
 | ||||
|     override fun onDestroyView() { | ||||
|         super.onDestroyView() | ||||
|         refreshCompleted() | ||||
|     } | ||||
| 
 | ||||
|     internal open fun canRefresh() = true | ||||
| 
 | ||||
|     private val refreshView: RefreshView? | ||||
|         get() = stateLayout.refreshView | ||||
| 
 | ||||
|     protected open fun onRetry(@StateLayoutConfig.RetryableState state: Int) { | ||||
|         refreshView.ifNonNull { | ||||
|             if (!this.isRefreshing) { | ||||
|                 autoRefresh() | ||||
|             } | ||||
|         } otherwise { | ||||
|             onRefresh() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected open fun onRefresh() {} | ||||
| 
 | ||||
|     fun setRefreshEnable(enable: Boolean) = refreshView?.setRefreshEnable(enable) | ||||
| 
 | ||||
|     override fun getStateLayoutConfig(): StateLayoutConfig = stateLayout.stateLayoutConfig | ||||
| 
 | ||||
|     override fun isRefreshing() = stateLayout.isRefreshing | ||||
| 
 | ||||
|     override fun refreshCompleted() = stateLayout.refreshCompleted() | ||||
| 
 | ||||
|     override fun autoRefresh() = stateLayout.autoRefresh() | ||||
| 
 | ||||
|     override fun showContentLayout() = stateLayout.showContentLayout() | ||||
| 
 | ||||
|     override fun showLoadingLayout() = stateLayout.showLoadingLayout() | ||||
| 
 | ||||
|     override fun showEmptyLayout() = stateLayout.showEmptyLayout() | ||||
| 
 | ||||
|     override fun showErrorLayout() = stateLayout.showErrorLayout() | ||||
| 
 | ||||
|     override fun showRequesting() = stateLayout.showRequesting() | ||||
| 
 | ||||
|     override fun showBlank() = stateLayout.showBlank() | ||||
| 
 | ||||
|     override fun showNetErrorLayout() = stateLayout.showNetErrorLayout() | ||||
| 
 | ||||
|     override fun showServerErrorLayout() = stateLayout.showServerErrorLayout() | ||||
| 
 | ||||
|     override fun currentStatus() = stateLayout.currentStatus() | ||||
| 
 | ||||
|     companion object { | ||||
|         const val CONTENT = StateLayoutConfig.CONTENT | ||||
|         const val LOADING = StateLayoutConfig.LOADING | ||||
|         const val ERROR = StateLayoutConfig.ERROR | ||||
|         const val EMPTY = StateLayoutConfig.EMPTY | ||||
|         const val NET_ERROR = StateLayoutConfig.NET_ERROR | ||||
|         const val SERVER_ERROR = StateLayoutConfig.SERVER_ERROR | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,101 @@ | ||||
| package com.android.base.app.fragment | ||||
| 
 | ||||
| import android.os.Bundle | ||||
| import android.view.View | ||||
| import com.android.base.R | ||||
| import com.android.base.app.ui.RefreshStateLayout | ||||
| import com.android.base.app.ui.RefreshView | ||||
| import com.android.base.app.ui.RefreshView.RefreshHandler | ||||
| import com.android.base.app.ui.StateLayoutConfig | ||||
| import com.android.base.app.ui.StateLayoutConfig.RetryableState | ||||
| import com.android.base.utils.common.ifNonNull | ||||
| import com.android.base.utils.common.otherwise | ||||
| 
 | ||||
| /** | ||||
|  * 1. 支持显示{CONTENT, LOADING, ERROR, EMPTY}等状态布局、支持下拉刷新 | ||||
|  * 2. 使用的布局中必须有一个id = [R.id.base_state_layout] 的 Layout,确保 Layout 实现了[com.android.base.app.ui.StateLayout] | ||||
|  * 3. [RefreshView] (下拉刷新)的 id 必须设置为 :[R.id.base_refresh_layout],没有添加则表示不需要下拉刷新功能 | ||||
|  * 4. 默认所有重试和下拉刷新都会调用 [onRefresh],子类可以修改该行为 | ||||
|  * | ||||
|  * @author Ztiany | ||||
|  * date :   2016-03-19 23:09 | ||||
|  * email:    1169654504@qq.com | ||||
|  */ | ||||
| abstract class BaseStateFragment : BaseFragment(), RefreshStateLayout { | ||||
| 
 | ||||
|     private lateinit var stateLayout: RefreshableStateLayoutImpl | ||||
| 
 | ||||
|     override fun internalOnViewPrepared(view: View, savedInstanceState: Bundle?) { | ||||
|         stateLayout = RefreshableStateLayoutImpl.init(view) | ||||
|         stateLayout.setRefreshHandler(object : RefreshHandler() { | ||||
|             override fun onRefresh() { | ||||
|                 this@BaseStateFragment.onRefresh() | ||||
|             } | ||||
| 
 | ||||
|             override fun canRefresh(): Boolean { | ||||
|                 return this@BaseStateFragment.canRefresh() | ||||
|             } | ||||
|         }) | ||||
|         stateLayout.setStateRetryListenerUnchecked { state -> onRetry(state) } | ||||
|     } | ||||
| 
 | ||||
|     override fun onDestroyView() { | ||||
|         super.onDestroyView() | ||||
|         refreshCompleted() | ||||
|     } | ||||
| 
 | ||||
|     internal open fun canRefresh() = true | ||||
| 
 | ||||
|     private val refreshView: RefreshView? | ||||
|         get() = stateLayout.refreshView | ||||
| 
 | ||||
|     protected open fun onRetry(@RetryableState state: Int) { | ||||
|         refreshView.ifNonNull { | ||||
|             if (!this.isRefreshing) { | ||||
|                 autoRefresh() | ||||
|             } | ||||
|         } otherwise { | ||||
|             onRefresh() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected open fun onRefresh() {} | ||||
| 
 | ||||
|     fun setRefreshEnable(enable: Boolean) = refreshView?.setRefreshEnable(enable) | ||||
| 
 | ||||
|     override fun getStateLayoutConfig(): StateLayoutConfig = stateLayout.stateLayoutConfig | ||||
| 
 | ||||
|     override fun isRefreshing() = stateLayout.isRefreshing | ||||
| 
 | ||||
|     override fun refreshCompleted() = stateLayout.refreshCompleted() | ||||
| 
 | ||||
|     override fun autoRefresh() = stateLayout.autoRefresh() | ||||
| 
 | ||||
|     override fun showContentLayout() = stateLayout.showContentLayout() | ||||
| 
 | ||||
|     override fun showLoadingLayout() = stateLayout.showLoadingLayout() | ||||
| 
 | ||||
|     override fun showEmptyLayout() = stateLayout.showEmptyLayout() | ||||
| 
 | ||||
|     override fun showErrorLayout() = stateLayout.showErrorLayout() | ||||
| 
 | ||||
|     override fun showRequesting() = stateLayout.showRequesting() | ||||
| 
 | ||||
|     override fun showBlank() = stateLayout.showBlank() | ||||
| 
 | ||||
|     override fun showNetErrorLayout() = stateLayout.showNetErrorLayout() | ||||
| 
 | ||||
|     override fun showServerErrorLayout() = stateLayout.showServerErrorLayout() | ||||
| 
 | ||||
|     override fun currentStatus() = stateLayout.currentStatus() | ||||
| 
 | ||||
|     companion object { | ||||
|         const val CONTENT = StateLayoutConfig.CONTENT | ||||
|         const val LOADING = StateLayoutConfig.LOADING | ||||
|         const val ERROR = StateLayoutConfig.ERROR | ||||
|         const val EMPTY = StateLayoutConfig.EMPTY | ||||
|         const val NET_ERROR = StateLayoutConfig.NET_ERROR | ||||
|         const val SERVER_ERROR = StateLayoutConfig.SERVER_ERROR | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,169 @@ | ||||
| package com.android.base.app.fragment; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.os.Bundle; | ||||
| import android.view.View; | ||||
| 
 | ||||
| import com.android.base.app.fragment.delegates.FragmentDelegate; | ||||
| import com.android.base.app.fragment.delegates.FragmentDelegateOwner; | ||||
| import com.github.dmstocking.optional.java.util.function.Predicate; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.annotation.UiThread; | ||||
| import androidx.fragment.app.Fragment; | ||||
| 
 | ||||
| 
 | ||||
| @UiThread | ||||
| final class FragmentDelegates implements FragmentDelegate<Fragment>, FragmentDelegateOwner { | ||||
| 
 | ||||
|     private final Fragment mDelegateOwner; | ||||
|     private List<FragmentDelegate> mDelegates = new ArrayList<>(4); | ||||
| 
 | ||||
|     <T extends Fragment & FragmentDelegateOwner> FragmentDelegates(T delegateOwner) { | ||||
|         mDelegateOwner = delegateOwner; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onAttach(Context context) { | ||||
|         for (FragmentDelegate fragmentDelegate : mDelegates) { | ||||
|             fragmentDelegate.onAttach(context); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onCreate(@Nullable Bundle savedInstanceState) { | ||||
|         for (FragmentDelegate fragmentDelegate : mDelegates) { | ||||
|             fragmentDelegate.onCreate(savedInstanceState); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { | ||||
|         for (FragmentDelegate fragmentDelegate : mDelegates) { | ||||
|             fragmentDelegate.onViewCreated(view, savedInstanceState); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onActivityCreated(@Nullable Bundle savedInstanceState) { | ||||
|         for (FragmentDelegate fragmentDelegate : mDelegates) { | ||||
|             fragmentDelegate.onActivityCreated(savedInstanceState); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onStart() { | ||||
|         for (FragmentDelegate fragmentDelegate : mDelegates) { | ||||
|             fragmentDelegate.onStart(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onResume() { | ||||
|         for (FragmentDelegate fragmentDelegate : mDelegates) { | ||||
|             fragmentDelegate.onResume(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onPause() { | ||||
|         for (FragmentDelegate fragmentDelegate : mDelegates) { | ||||
|             fragmentDelegate.onPause(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onStop() { | ||||
|         for (FragmentDelegate fragmentDelegate : mDelegates) { | ||||
|             fragmentDelegate.onStop(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         for (FragmentDelegate fragmentDelegate : mDelegates) { | ||||
|             fragmentDelegate.onDestroy(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onDestroyView() { | ||||
|         for (FragmentDelegate fragmentDelegate : mDelegates) { | ||||
|             fragmentDelegate.onDestroyView(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onDetach() { | ||||
|         for (FragmentDelegate fragmentDelegate : mDelegates) { | ||||
|             fragmentDelegate.onDetach(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void setUserVisibleHint(boolean isVisibleToUser) { | ||||
|         for (FragmentDelegate fragmentDelegate : mDelegates) { | ||||
|             fragmentDelegate.setUserVisibleHint(isVisibleToUser); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onSaveInstanceState(Bundle savedInstanceState) { | ||||
|         for (FragmentDelegate fragmentDelegate : mDelegates) { | ||||
|             fragmentDelegate.onSaveInstanceState(savedInstanceState); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onHiddenChanged(boolean hidden) { | ||||
|         for (FragmentDelegate fragmentDelegate : mDelegates) { | ||||
|             fragmentDelegate.onHiddenChanged(hidden); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onActivityResult(int requestCode, int resultCode, Intent data) { | ||||
|         for (FragmentDelegate fragmentDelegate : mDelegates) { | ||||
|             fragmentDelegate.onActivityResult(requestCode, resultCode, data); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { | ||||
|         for (FragmentDelegate fragmentDelegate : mDelegates) { | ||||
|             fragmentDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public void addDelegate(FragmentDelegate fragmentDelegate) { | ||||
|         mDelegates.add(fragmentDelegate); | ||||
|         fragmentDelegate.onAttachToFragment(mDelegateOwner); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean removeDelegate(FragmentDelegate fragmentDelegate) { | ||||
|         boolean remove = mDelegates.remove(fragmentDelegate); | ||||
|         if (remove) { | ||||
|             fragmentDelegate.onDetachFromFragment(); | ||||
|         } | ||||
|         return remove; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public FragmentDelegate findDelegate(Predicate<FragmentDelegate> predicate) { | ||||
|         for (FragmentDelegate delegate : mDelegates) { | ||||
|             if (predicate.test(delegate)) { | ||||
|                 return delegate; | ||||
|             } | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,85 @@ | ||||
| package com.android.base.app.fragment | ||||
| 
 | ||||
| import android.graphics.drawable.Drawable | ||||
| import android.view.View | ||||
| import com.android.base.app.ui.* | ||||
| import com.android.base.widget.StateProcessor | ||||
| 
 | ||||
| internal class RefreshLoadMoreStateLayoutImpl private constructor(layout: View) : StateLayout, StateLayoutConfig { | ||||
| 
 | ||||
|     companion object { | ||||
|         fun init(view: View): RefreshLoadMoreStateLayoutImpl { | ||||
|             return RefreshLoadMoreStateLayoutImpl(view) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private var _multiStateView: StateLayout? = layout.findViewById<View>(CommonId.STATE_ID) as? StateLayout | ||||
|     private var _refreshView: RefreshLoadMoreView | ||||
| 
 | ||||
|     val refreshView: RefreshLoadMoreView | ||||
|         get() = _refreshView | ||||
| 
 | ||||
|     init { | ||||
|         val refreshLayout = layout.findViewById<View>(CommonId.REFRESH_ID) | ||||
|                 ?: throw NullPointerException("You need to provide a refreshable layout with id R.id.base_refresh_layout in your xml.") | ||||
| 
 | ||||
|         _refreshView = RefreshLoadViewFactory.createRefreshView(refreshLayout) | ||||
|     } | ||||
| 
 | ||||
|     override fun showLoadingLayout() = checkMultiStateView().showLoadingLayout() | ||||
|     override fun showContentLayout() = checkMultiStateView().showContentLayout() | ||||
|     override fun showEmptyLayout() = checkMultiStateView().showEmptyLayout() | ||||
|     override fun showErrorLayout() = checkMultiStateView().showErrorLayout() | ||||
|     override fun showRequesting() = checkMultiStateView().showRequesting() | ||||
|     override fun showBlank() = checkMultiStateView().showBlank() | ||||
|     override fun showNetErrorLayout() = checkMultiStateView().showNetErrorLayout() | ||||
|     override fun showServerErrorLayout() = checkMultiStateView().showServerErrorLayout() | ||||
|     override fun currentStatus() = checkMultiStateView().currentStatus() | ||||
| 
 | ||||
|     override fun getStateLayoutConfig(): StateLayoutConfig = checkMultiStateView().stateLayoutConfig | ||||
| 
 | ||||
|     override fun setStateMessage(state: Int, message: CharSequence?): StateLayoutConfig { | ||||
|         checkMultiStateView().stateLayoutConfig.setStateMessage(state, message) | ||||
|         return checkMultiStateView().stateLayoutConfig | ||||
|     } | ||||
| 
 | ||||
|     override fun setStateIcon(state: Int, drawable: Drawable?): StateLayoutConfig { | ||||
|         checkMultiStateView().stateLayoutConfig.setStateIcon(state, drawable) | ||||
|         return checkMultiStateView().stateLayoutConfig | ||||
|     } | ||||
| 
 | ||||
|     override fun setStateIcon(state: Int, drawableId: Int): StateLayoutConfig { | ||||
|         checkMultiStateView().stateLayoutConfig.setStateIcon(state, drawableId) | ||||
|         return checkMultiStateView().stateLayoutConfig | ||||
|     } | ||||
| 
 | ||||
|     override fun setStateAction(state: Int, actionText: CharSequence?): StateLayoutConfig { | ||||
|         checkMultiStateView().stateLayoutConfig.setStateAction(state, actionText) | ||||
|         return checkMultiStateView().stateLayoutConfig | ||||
|     } | ||||
| 
 | ||||
|     override fun setStateRetryListener(retryActionListener: OnRetryActionListener?): StateLayoutConfig { | ||||
|         checkMultiStateView().stateLayoutConfig.setStateRetryListener(retryActionListener) | ||||
|         return checkMultiStateView().stateLayoutConfig | ||||
|     } | ||||
| 
 | ||||
|     override fun disableOperationWhenRequesting(disable: Boolean): StateLayoutConfig { | ||||
|         checkMultiStateView().stateLayoutConfig.disableOperationWhenRequesting(disable) | ||||
|         return checkMultiStateView().stateLayoutConfig | ||||
|     } | ||||
| 
 | ||||
|     override fun getProcessor(): StateProcessor { | ||||
|         return checkMultiStateView().stateLayoutConfig.processor | ||||
|     } | ||||
| 
 | ||||
|     override fun setMessageGravity(state: Int, gravity: Int): StateLayoutConfig { | ||||
|         checkMultiStateView().stateLayoutConfig.setMessageGravity(state, gravity) | ||||
|         return this | ||||
|     } | ||||
| 
 | ||||
|     private fun checkMultiStateView(): StateLayout { | ||||
|         return _multiStateView | ||||
|                 ?: throw IllegalStateException("Calling this function requires defining a view that implements StateLayout in the Layout") | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,207 @@ | ||||
| package com.android.base.app.fragment; | ||||
| 
 | ||||
| import android.graphics.drawable.Drawable; | ||||
| import android.view.View; | ||||
| 
 | ||||
| import com.android.base.app.ui.OnRetryActionListener; | ||||
| import com.android.base.app.ui.RefreshStateLayout; | ||||
| import com.android.base.app.ui.RefreshView; | ||||
| import com.android.base.app.ui.RefreshViewFactory; | ||||
| import com.android.base.app.ui.StateLayout; | ||||
| import com.android.base.app.ui.StateLayoutConfig; | ||||
| import com.android.base.widget.StateProcessor; | ||||
| 
 | ||||
| import androidx.annotation.DrawableRes; | ||||
| 
 | ||||
| import static com.android.base.app.ui.CommonId.REFRESH_ID; | ||||
| import static com.android.base.app.ui.CommonId.STATE_ID; | ||||
| 
 | ||||
| /** | ||||
|  * @author Ztiany | ||||
|  */ | ||||
| final class RefreshableStateLayoutImpl implements RefreshStateLayout, StateLayoutConfig { | ||||
| 
 | ||||
|     private StateLayout mMultiStateView; | ||||
|     private RefreshView mRefreshView; | ||||
|     private RefreshView.RefreshHandler mRefreshHandler; | ||||
| 
 | ||||
|     static RefreshableStateLayoutImpl init(View layoutView) { | ||||
|         return new RefreshableStateLayoutImpl(layoutView); | ||||
|     } | ||||
| 
 | ||||
|     private RefreshableStateLayoutImpl(View layoutView) { | ||||
|         setupBaseUiLogic(layoutView); | ||||
|         setupRefreshLogic(layoutView); | ||||
|     } | ||||
| 
 | ||||
|     RefreshView getRefreshView() { | ||||
|         return mRefreshView; | ||||
|     } | ||||
| 
 | ||||
|     void setRefreshHandler(RefreshView.RefreshHandler refreshHandler) { | ||||
|         mRefreshHandler = refreshHandler; | ||||
|     } | ||||
| 
 | ||||
|     void setStateRetryListenerUnchecked(OnRetryActionListener retryActionListener) { | ||||
|         if (mMultiStateView != null) { | ||||
|             setStateRetryListener(retryActionListener); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("all") | ||||
|     private void setupBaseUiLogic(View layoutView) { | ||||
|         mMultiStateView = (StateLayout) layoutView.findViewById(STATE_ID); | ||||
|     } | ||||
| 
 | ||||
|     private void setupRefreshLogic(View layoutView) { | ||||
|         View refreshLayout = layoutView.findViewById(REFRESH_ID); | ||||
|         if (refreshLayout == null) { | ||||
|             return; | ||||
|         } | ||||
|         mRefreshView = RefreshViewFactory.createRefreshView(refreshLayout); | ||||
|         mRefreshView.setRefreshHandler(new RefreshView.RefreshHandler() { | ||||
|             @Override | ||||
|             public boolean canRefresh() { | ||||
|                 return mRefreshHandler.canRefresh(); | ||||
|             } | ||||
| 
 | ||||
|             @Override | ||||
|             public void onRefresh() { | ||||
|                 mRefreshHandler.onRefresh(); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public final void autoRefresh() { | ||||
|         if (mRefreshView != null) { | ||||
|             mRefreshView.autoRefresh(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void showLoadingLayout() { | ||||
|         checkMultiStateView().showLoadingLayout(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void showContentLayout() { | ||||
|         refreshCompleted(); | ||||
|         checkMultiStateView().showContentLayout(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void showEmptyLayout() { | ||||
|         refreshCompleted(); | ||||
|         checkMultiStateView().showEmptyLayout(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void showErrorLayout() { | ||||
|         refreshCompleted(); | ||||
|         checkMultiStateView().showErrorLayout(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void showRequesting() { | ||||
|         checkMultiStateView().showRequesting(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void showBlank() { | ||||
|         checkMultiStateView().showBlank(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void showNetErrorLayout() { | ||||
|         refreshCompleted(); | ||||
|         checkMultiStateView().showNetErrorLayout(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void showServerErrorLayout() { | ||||
|         refreshCompleted(); | ||||
|         checkMultiStateView().showServerErrorLayout(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public StateLayoutConfig getStateLayoutConfig() { | ||||
|         checkMultiStateView(); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int currentStatus() { | ||||
|         return mMultiStateView.currentStatus(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void refreshCompleted() { | ||||
|         if (mRefreshView != null) { | ||||
|             mRefreshView.refreshCompleted(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean isRefreshing() { | ||||
|         if (mRefreshView != null) { | ||||
|             return mRefreshView.isRefreshing(); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public StateLayoutConfig setStateMessage(@RetryableState int state, CharSequence message) { | ||||
|         checkMultiStateView().getStateLayoutConfig().setStateMessage(state, message); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public StateLayoutConfig setMessageGravity(int state, int gravity) { | ||||
|         checkMultiStateView().getStateLayoutConfig().setMessageGravity(state, gravity); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public StateLayoutConfig setStateIcon(@RetryableState int state, Drawable drawable) { | ||||
|         checkMultiStateView().getStateLayoutConfig().setStateIcon(state, drawable); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public StateLayoutConfig setStateIcon(@RetryableState int state, @DrawableRes int drawableId) { | ||||
|         checkMultiStateView().getStateLayoutConfig().setStateIcon(state, drawableId); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public StateLayoutConfig setStateAction(@RetryableState int state, CharSequence actionText) { | ||||
|         checkMultiStateView().getStateLayoutConfig().setStateAction(state, actionText); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public StateLayoutConfig setStateRetryListener(OnRetryActionListener retryActionListener) { | ||||
|         checkMultiStateView().getStateLayoutConfig().setStateRetryListener(retryActionListener); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public StateLayoutConfig disableOperationWhenRequesting(boolean disable) { | ||||
|         checkMultiStateView().getStateLayoutConfig().disableOperationWhenRequesting(disable); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public StateProcessor getProcessor() { | ||||
|         return checkMultiStateView().getStateLayoutConfig().getProcessor(); | ||||
|     } | ||||
| 
 | ||||
|     private StateLayout checkMultiStateView() { | ||||
|         if (mMultiStateView == null) { | ||||
|             throw new IllegalStateException("Calling this function requires defining a view that implements StateLayout in the Layout"); | ||||
|         } | ||||
|         return mMultiStateView; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,103 @@ | ||||
| package com.android.base.app.fragment.animator | ||||
| 
 | ||||
| import android.view.animation.* | ||||
| import com.android.base.R | ||||
| import com.android.base.utils.BaseUtils | ||||
| 
 | ||||
| interface FragmentAnimator { | ||||
|     fun makeOpenEnter(): Animation? | ||||
|     fun makeOpenExit(): Animation? | ||||
|     fun makeCloseEnter(): Animation? | ||||
|     fun makeCloseExit(): Animation? | ||||
| } | ||||
| 
 | ||||
| class DefaultVerticalAnimator : FragmentAnimator { | ||||
|     override fun makeOpenEnter(): Animation? { | ||||
|         return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.v_fragment_open_enter) | ||||
|     } | ||||
| 
 | ||||
|     override fun makeOpenExit(): Animation? { | ||||
|         return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.v_fragment_open_exit) | ||||
|     } | ||||
| 
 | ||||
|     override fun makeCloseEnter(): Animation? { | ||||
|         return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.v_fragment_close_enter) | ||||
|     } | ||||
| 
 | ||||
|     override fun makeCloseExit(): Animation? { | ||||
|         return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.v_fragment_close_exit) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| class DefaultHorizontalAnimator : FragmentAnimator { | ||||
|     override fun makeOpenEnter(): Animation? { | ||||
|         return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.h_fragment_open_enter) | ||||
|     } | ||||
| 
 | ||||
|     override fun makeOpenExit(): Animation? { | ||||
|         return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.h_fragment_open_exit) | ||||
|     } | ||||
| 
 | ||||
|     override fun makeCloseEnter(): Animation? { | ||||
|         return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.h_fragment_close_enter) | ||||
|     } | ||||
| 
 | ||||
|     override fun makeCloseExit(): Animation? { | ||||
|         return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.h_fragment_close_exit) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| class DefaultNoAnimator : FragmentAnimator { | ||||
|     override fun makeOpenEnter(): Animation? { | ||||
|         return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.base_no_anim) | ||||
|     } | ||||
| 
 | ||||
|     override fun makeOpenExit(): Animation? { | ||||
|         return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.base_no_anim) | ||||
|     } | ||||
| 
 | ||||
|     override fun makeCloseEnter(): Animation? { | ||||
|         return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.base_no_anim) | ||||
|     } | ||||
| 
 | ||||
|     override fun makeCloseExit(): Animation? { | ||||
|         return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.base_no_anim) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| class DefaultScaleAnimator : FragmentAnimator { | ||||
|     override fun makeOpenEnter(): Animation? { | ||||
|         return makeOpenCloseAnimation(1.125f, 1.0f, 0f, 1f) | ||||
|     } | ||||
| 
 | ||||
|     override fun makeOpenExit(): Animation? { | ||||
|         return makeOpenCloseAnimation(1.0f, .975f, 1f, 0f) | ||||
|     } | ||||
| 
 | ||||
|     override fun makeCloseEnter(): Animation? { | ||||
|         return makeOpenCloseAnimation(.975f, 1.0f, 0f, 1f) | ||||
|     } | ||||
| 
 | ||||
|     override fun makeCloseExit(): Animation? { | ||||
|         return makeOpenCloseAnimation(1.0f, 1.075f, 1f, 0f) | ||||
|     } | ||||
| 
 | ||||
|     companion object { | ||||
|         private val DECELERATE_QUINT: Interpolator = DecelerateInterpolator(2.5f) | ||||
|         private val DECELERATE_CUBIC: Interpolator = DecelerateInterpolator(1.5f) | ||||
|         private const val ANIM_DUR = 220 | ||||
|         private fun makeOpenCloseAnimation(startScale: Float, endScale: Float, startAlpha: Float, endAlpha: Float): Animation { | ||||
|             val set = AnimationSet(false) | ||||
|             val scale = ScaleAnimation(startScale, endScale, startScale, endScale, Animation.RELATIVE_TO_SELF, .5f, Animation.RELATIVE_TO_SELF, .5f) | ||||
|             scale.interpolator = DECELERATE_QUINT | ||||
|             scale.duration = ANIM_DUR.toLong() | ||||
|             set.addAnimation(scale) | ||||
|             val alpha = AlphaAnimation(startAlpha, endAlpha) | ||||
|             alpha.interpolator = DECELERATE_CUBIC | ||||
|             alpha.duration = ANIM_DUR.toLong() | ||||
|             set.addAnimation(alpha) | ||||
|             return set | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,103 @@ | ||||
| package com.android.base.app.fragment.animator; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.view.animation.Animation; | ||||
| import android.view.animation.AnimationUtils; | ||||
| 
 | ||||
| import com.android.base.R; | ||||
| 
 | ||||
| import androidx.fragment.app.FragmentTransaction; | ||||
| 
 | ||||
| public final class FragmentAnimatorHelper { | ||||
| 
 | ||||
|     private Context context; | ||||
|     private FragmentAnimator fragmentAnimator; | ||||
| 
 | ||||
|     public FragmentAnimatorHelper(Context context, FragmentAnimator fragmentAnimator) { | ||||
|         this.context = context; | ||||
|         this.fragmentAnimator = fragmentAnimator; | ||||
|     } | ||||
| 
 | ||||
|     public void changeAnimation(FragmentAnimator fragmentAnimator) { | ||||
|         this.fragmentAnimator = fragmentAnimator; | ||||
|     } | ||||
| 
 | ||||
|     public Animation getNoneAnim() { | ||||
|         return AnimationUtils.loadAnimation(context, R.anim.base_no_anim); | ||||
|     } | ||||
| 
 | ||||
|     public Animation getNoneAnimFixed() { | ||||
|         return new Animation() { | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     private Animation initEnterAnim() { | ||||
|         if (fragmentAnimator == null) { | ||||
|             return AnimationUtils.loadAnimation(context, R.anim.base_no_anim); | ||||
|         } else { | ||||
|             Animation animation = fragmentAnimator.makeOpenEnter(); | ||||
|             if (animation == null) { | ||||
|                 return AnimationUtils.loadAnimation(context, R.anim.base_no_anim); | ||||
|             } else { | ||||
|                 return animation; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private Animation initExitAnim() { | ||||
|         if (fragmentAnimator == null) { | ||||
|             return AnimationUtils.loadAnimation(context, R.anim.base_no_anim); | ||||
|         } else { | ||||
|             Animation animation = fragmentAnimator.makeOpenExit(); | ||||
|             if (animation == null) { | ||||
|                 return AnimationUtils.loadAnimation(context, R.anim.base_no_anim); | ||||
|             } else { | ||||
|                 return animation; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private Animation initPopEnterAnim() { | ||||
|         if (fragmentAnimator == null) { | ||||
|             return AnimationUtils.loadAnimation(context, R.anim.base_no_anim); | ||||
|         } else { | ||||
|             Animation animation = fragmentAnimator.makeCloseEnter(); | ||||
|             if (animation == null) { | ||||
|                 return AnimationUtils.loadAnimation(context, R.anim.base_no_anim); | ||||
|             } else { | ||||
|                 return animation; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private Animation initPopExitAnim() { | ||||
|         if (fragmentAnimator == null) { | ||||
|             return AnimationUtils.loadAnimation(context, R.anim.base_no_anim); | ||||
|         } else { | ||||
|             Animation animation = fragmentAnimator.makeCloseExit(); | ||||
|             if (animation == null) { | ||||
|                 return AnimationUtils.loadAnimation(context, R.anim.base_no_anim); | ||||
|             } else { | ||||
|                 return animation; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public Animation onCreateAnimation(int transit, boolean enter) { | ||||
|         if (transit == FragmentTransaction.TRANSIT_FRAGMENT_OPEN) { | ||||
|             if (enter) { | ||||
|                 return initEnterAnim(); | ||||
|             } else { | ||||
|                 return initExitAnim(); | ||||
|             } | ||||
|         } else if (transit == FragmentTransaction.TRANSIT_FRAGMENT_CLOSE) { | ||||
|             if (enter) { | ||||
|                 return initPopEnterAnim(); | ||||
|             } else { | ||||
|                 return initPopExitAnim(); | ||||
|             } | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,74 @@ | ||||
| package com.android.base.app.fragment.delegates; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.os.Bundle; | ||||
| import android.view.View; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.fragment.app.Fragment; | ||||
| 
 | ||||
| public interface FragmentDelegate<T extends Fragment> { | ||||
| 
 | ||||
|     /** | ||||
|      * 当该Delegate被添加到Fragment中 | ||||
|      */ | ||||
|     default void onAttachToFragment(T fragment) { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 调用此方法时,清除Fragment的引用 | ||||
|      */ | ||||
|     default void onDetachFromFragment() { | ||||
|     } | ||||
| 
 | ||||
|     default void onAttach(Context context) { | ||||
|     } | ||||
| 
 | ||||
|     default void onCreate(@Nullable Bundle savedInstanceState) { | ||||
|     } | ||||
| 
 | ||||
|     default void onActivityCreated(@Nullable Bundle savedInstanceState) { | ||||
|     } | ||||
| 
 | ||||
|     default void onSaveInstanceState(Bundle savedInstanceState) { | ||||
|     } | ||||
| 
 | ||||
|     default void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { | ||||
|     } | ||||
| 
 | ||||
|     default void onStart() { | ||||
|     } | ||||
| 
 | ||||
|     default void onResume() { | ||||
|     } | ||||
| 
 | ||||
|     default void onPause() { | ||||
|     } | ||||
| 
 | ||||
|     default void onStop() { | ||||
|     } | ||||
| 
 | ||||
|     default void onDestroy() { | ||||
|     } | ||||
| 
 | ||||
|     default void onDestroyView() { | ||||
|     } | ||||
| 
 | ||||
|     default void onDetach() { | ||||
|     } | ||||
| 
 | ||||
|     default void setUserVisibleHint(boolean isVisibleToUser) { | ||||
|     } | ||||
| 
 | ||||
|     default void onHiddenChanged(boolean hidden) { | ||||
|     } | ||||
| 
 | ||||
|     default void onActivityResult(int requestCode, int resultCode, Intent data) { | ||||
|     } | ||||
| 
 | ||||
|     default void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,17 @@ | ||||
| package com.android.base.app.fragment.delegates; | ||||
| 
 | ||||
| 
 | ||||
| import com.github.dmstocking.optional.java.util.function.Predicate; | ||||
| 
 | ||||
| import androidx.annotation.UiThread; | ||||
| 
 | ||||
| @UiThread | ||||
| public interface FragmentDelegateOwner { | ||||
| 
 | ||||
|     void addDelegate(FragmentDelegate fragmentDelegate); | ||||
| 
 | ||||
|     boolean removeDelegate(FragmentDelegate fragmentDelegate); | ||||
| 
 | ||||
|     FragmentDelegate findDelegate(Predicate<FragmentDelegate> predicate); | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,96 @@ | ||||
| package com.android.base.app.fragment.delegates | ||||
| 
 | ||||
| import android.os.Bundle | ||||
| import android.view.View | ||||
| import androidx.fragment.app.Fragment | ||||
| 
 | ||||
| /** | ||||
|  * 用于在ViewPager中实现懒加载的Fragment: | ||||
|  * | ||||
|  * - changed-1: Android Support 24 把 setUserVisibleHint 方法放到了Attach 之前调用了,所以请在在构造代码块中设置 LazyDelegate。 | ||||
|  * | ||||
|  * @author Ztiany | ||||
|  * Date : Date : 2016-05-06 15:02 | ||||
|  * Email: 1169654504@qq.com | ||||
|  */ | ||||
| open class LazyDelegate private constructor() : FragmentDelegate<Fragment?> { | ||||
| 
 | ||||
|     /** View是否准备好,如果不需要绑定view数据,只是加载网络数据,那么该字段可以去掉 */ | ||||
|     private var isViewPrepared = false | ||||
| 
 | ||||
|     /** 滑动过来后,View是否可见 */ | ||||
|     private var isViewVisible = false | ||||
| 
 | ||||
|     private var preparedListener: (() -> Unit)? = null | ||||
| 
 | ||||
|     /** | ||||
|      * 在这里实现Fragment数据的缓加载. | ||||
|      * | ||||
|      * @param isVisibleToUser true 表用户可见,false 表不可见 | ||||
|      */ | ||||
|     override fun setUserVisibleHint(isVisibleToUser: Boolean) { | ||||
|         if (isVisibleToUser) { | ||||
|             isViewVisible = true | ||||
|             onVisible() | ||||
|         } else { | ||||
|             isViewVisible = false | ||||
|             onInvisible() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||
|         isViewPrepared = true | ||||
|     } | ||||
| 
 | ||||
|     override fun onActivityCreated(savedInstanceState: Bundle?) { | ||||
|         lazyLoad() | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 滑动过来后,界面可见时执行 | ||||
|      */ | ||||
|     protected fun onVisible() { | ||||
|         lazyLoad() | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 滑动过来后,界面不可见时执行 | ||||
|      */ | ||||
|     protected fun onInvisible() {} | ||||
| 
 | ||||
|     private fun lazyLoad() { | ||||
|         if (isViewPrepared && isViewVisible) { | ||||
|             notifyLazyLoad() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 懒加载数据,并在此绑定View数据 | ||||
|      */ | ||||
|     private fun notifyLazyLoad() { | ||||
|         preparedListener?.invoke() | ||||
|     } | ||||
| 
 | ||||
|     companion object { | ||||
|         fun attach(delegateFragment: FragmentDelegateOwner, onPreparedListener: () -> Unit): LazyDelegate { | ||||
|             val delegate = LazyDelegate() | ||||
|             delegate.preparedListener = onPreparedListener | ||||
|             delegateFragment.addDelegate(delegate) | ||||
|             return delegate | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| class SimpleLazyLoadListener(private val onFirstLoad: () -> Unit) : (() -> Unit) { | ||||
| 
 | ||||
|     private var isCalled = false | ||||
| 
 | ||||
|     override fun invoke() { | ||||
|         if (!isCalled) { | ||||
|             onFirstLoad() | ||||
|             isCalled = true | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,114 @@ | ||||
| package com.android.base.app.fragment.tools | ||||
| 
 | ||||
| import android.os.Binder | ||||
| import android.os.Bundle | ||||
| import androidx.core.app.BundleCompat | ||||
| import androidx.fragment.app.Fragment | ||||
| import kotlin.properties.ReadWriteProperty | ||||
| import kotlin.reflect.KProperty | ||||
| 
 | ||||
| fun <T : Any> fragmentArgument(name: String? = null, defaultValue: T? = null): ReadWriteProperty<Fragment, T> { | ||||
|     return FragmentArgumentDelegate(name, defaultValue) | ||||
| } | ||||
| 
 | ||||
| fun <T : Any?> nullableFragmentArgument(name: String? = null): ReadWriteProperty<Fragment, T?> { | ||||
|     return NullableFragmentArgumentDelegate(name) | ||||
| } | ||||
| 
 | ||||
| internal class FragmentArgumentDelegate<T : Any>( | ||||
|         /**value name*/ | ||||
|         private val name: String? = null, | ||||
|         /**default cannot be null*/ | ||||
|         private val defaultValue: T? = null | ||||
| ) : ReadWriteProperty<Fragment, T> { | ||||
| 
 | ||||
|     private lateinit var value: T | ||||
| 
 | ||||
|     override operator fun getValue(thisRef: Fragment, property: KProperty<*>): T { | ||||
|         if (!::value.isInitialized) { | ||||
| 
 | ||||
|             val args = thisRef.arguments | ||||
| 
 | ||||
|             if (args == null) { | ||||
|                 if (defaultValue != null) { | ||||
|                     value = defaultValue | ||||
|                 } else { | ||||
|                     throw IllegalStateException("Cannot read property ${confirmPropertyName(name, property)} if no arguments have been set") | ||||
|                 } | ||||
|             } else { | ||||
|                 @Suppress("UNCHECKED_CAST") | ||||
|                 value = args.get(confirmPropertyName(name, property)) as? T | ||||
|                         ?: throw IllegalStateException("Property ${confirmPropertyName(name, property)} could not be read") | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         return value | ||||
|     } | ||||
| 
 | ||||
|     override operator fun setValue(thisRef: Fragment, property: KProperty<*>, value: T) { | ||||
|         this.value = value | ||||
|         saveValue(thisRef, name, property, value) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| internal class NullableFragmentArgumentDelegate<T : Any?>( | ||||
|         /**value name*/ | ||||
|         private val name: String? = null | ||||
| ) : ReadWriteProperty<Fragment, T?> { | ||||
| 
 | ||||
|     private var value: T? = null | ||||
| 
 | ||||
|     override operator fun getValue(thisRef: Fragment, property: KProperty<*>): T? { | ||||
|         if (value == null) { | ||||
|             @Suppress("UNCHECKED_CAST") | ||||
|             value = thisRef.arguments?.get(confirmPropertyName(name, property)) as? T | ||||
|         } | ||||
| 
 | ||||
|         return value | ||||
|     } | ||||
| 
 | ||||
|     override operator fun setValue(thisRef: Fragment, property: KProperty<*>, value: T?) { | ||||
|         this.value = value | ||||
|         saveValue(thisRef, name, property, value) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| private fun confirmPropertyName(name: String?, property: KProperty<*>): String { | ||||
|     return name ?: property.name | ||||
| } | ||||
| 
 | ||||
| private fun saveValue(thisRef: Fragment, name: String?, property: KProperty<*>, value: Any?) { | ||||
|     var args = thisRef.arguments | ||||
|     val key = confirmPropertyName(name, property) | ||||
| 
 | ||||
|     if (value == null) { | ||||
|         args?.remove(key) | ||||
|         return | ||||
|     } | ||||
| 
 | ||||
|     if (args == null) { | ||||
|         args = Bundle() | ||||
|         thisRef.arguments = args | ||||
|     } | ||||
| 
 | ||||
|     when (value) { | ||||
|         is String -> args.putString(key, value) | ||||
|         is Int -> args.putInt(key, value) | ||||
|         is Short -> args.putShort(key, value) | ||||
|         is Long -> args.putLong(key, value) | ||||
|         is Byte -> args.putByte(key, value) | ||||
|         is ByteArray -> args.putByteArray(key, value) | ||||
|         is Char -> args.putChar(key, value) | ||||
|         is CharArray -> args.putCharArray(key, value) | ||||
|         is CharSequence -> args.putCharSequence(key, value) | ||||
|         is Float -> args.putFloat(key, value) | ||||
|         is Bundle -> args.putBundle(key, value) | ||||
|         is Binder -> BundleCompat.putBinder(args, key, value) | ||||
|         is android.os.Parcelable -> args.putParcelable(key, value) | ||||
|         is java.io.Serializable -> args.putSerializable(key, value) | ||||
|         else -> throw IllegalStateException("Type \"$value\" of property ${property.name} is not supported") | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,35 @@ | ||||
| package com.android.base.app.fragment.tools; | ||||
| 
 | ||||
| import com.android.base.app.fragment.animator.FragmentAnimator; | ||||
| 
 | ||||
| /** | ||||
|  * @author Ztiany | ||||
|  * Email: ztiany3@gmail.com | ||||
|  * Date : 2019-03-05 15:25 | ||||
|  */ | ||||
| public class FragmentConfig { | ||||
| 
 | ||||
|     private static final int INVALIDATE_ID = -1; | ||||
|     private static int sDefaultContainerId = INVALIDATE_ID; | ||||
|     private static FragmentAnimator sFragmentAnimator = null; | ||||
| 
 | ||||
|     public static void setDefaultContainerId(int defaultContainerId) { | ||||
|         sDefaultContainerId = defaultContainerId; | ||||
|     } | ||||
| 
 | ||||
|     public static int defaultContainerId() { | ||||
|         if (sDefaultContainerId == INVALIDATE_ID) { | ||||
|             throw new IllegalStateException("sDefaultContainerId has not set"); | ||||
|         } | ||||
|         return sDefaultContainerId; | ||||
|     } | ||||
| 
 | ||||
|     public static void setDefaultFragmentAnimator(FragmentAnimator animator) { | ||||
|         sFragmentAnimator = animator; | ||||
|     } | ||||
| 
 | ||||
|     public static FragmentAnimator defaultFragmentAnimator() { | ||||
|         return sFragmentAnimator; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,133 @@ | ||||
| package com.android.base.app.fragment.tools; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.os.Bundle; | ||||
| 
 | ||||
| import java.lang.ref.WeakReference; | ||||
| 
 | ||||
| import androidx.fragment.app.Fragment; | ||||
| 
 | ||||
| @SuppressWarnings("WeakerAccess,unused") | ||||
| public class FragmentInfo { | ||||
| 
 | ||||
|     private final int mPageId; | ||||
|     private final String mTag; | ||||
|     private final Class<? extends Fragment> mClazz; | ||||
|     private final int mTitleId; | ||||
|     private final Bundle mArguments; | ||||
|     private final boolean mIsToStack; | ||||
|     private final String mStackName; | ||||
|     private WeakReference<Fragment> mFragment; | ||||
| 
 | ||||
|     private FragmentInfo(int pageId, String tag, Class<? extends Fragment> clazz, int titleId, Bundle arguments, boolean toStack, String stackName) { | ||||
|         mPageId = pageId; | ||||
|         mTag = tag; | ||||
|         mClazz = clazz; | ||||
|         mTitleId = titleId; | ||||
|         mArguments = arguments; | ||||
|         mIsToStack = toStack; | ||||
|         mStackName = stackName; | ||||
|     } | ||||
| 
 | ||||
|     public Fragment getInstance() { | ||||
|         return mFragment == null ? null : mFragment.get(); | ||||
|     } | ||||
| 
 | ||||
|     public void setInstance(Fragment fragment) { | ||||
|         mFragment = new WeakReference<>(fragment); | ||||
|     } | ||||
| 
 | ||||
|     public Fragment newFragment(Context context) { | ||||
|         return Fragment.instantiate(context, mClazz.getName(), mArguments); | ||||
|     } | ||||
| 
 | ||||
|     public boolean isToStack() { | ||||
|         return mIsToStack; | ||||
|     } | ||||
| 
 | ||||
|     public String getStackName() { | ||||
|         return mStackName; | ||||
|     } | ||||
| 
 | ||||
|     public Bundle getArguments() { | ||||
|         return mArguments; | ||||
|     } | ||||
| 
 | ||||
|     public int getTitleId() { | ||||
|         return mTitleId; | ||||
|     } | ||||
| 
 | ||||
|     public int getPageId() { | ||||
|         return mPageId; | ||||
|     } | ||||
| 
 | ||||
|     public String getTag() { | ||||
|         return mTag; | ||||
|     } | ||||
| 
 | ||||
|     public Class<? extends Fragment> getClazz() { | ||||
|         return mClazz; | ||||
|     } | ||||
| 
 | ||||
|     public static PageBuilder builder() { | ||||
|         return new PageBuilder(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public static class PageBuilder { | ||||
| 
 | ||||
|         private int mPagerId; | ||||
|         private String mTag; | ||||
|         private Class<? extends Fragment> mClazz; | ||||
|         private int mTitleId; | ||||
|         private Bundle mArguments; | ||||
|         private boolean mIsToStack; | ||||
|         private String mStackName; | ||||
| 
 | ||||
|         public FragmentInfo build() { | ||||
|             return new FragmentInfo(mPagerId, mTag, mClazz, mTitleId, mArguments, mIsToStack, mStackName); | ||||
|         } | ||||
| 
 | ||||
|         public PageBuilder pagerId(int pagerId) { | ||||
|             mPagerId = pagerId; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public PageBuilder tag(String tag) { | ||||
|             this.mTag = tag; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public PageBuilder clazz(Class<? extends Fragment> clazz) { | ||||
|             mClazz = clazz; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public PageBuilder titleId(int titleId) { | ||||
|             mTitleId = titleId; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public PageBuilder arguments(Bundle arguments) { | ||||
|             mArguments = arguments; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public PageBuilder toStack(boolean toStack) { | ||||
|             this.mIsToStack = toStack; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * 如果需要加入到Stack,建议加上StackName。 | ||||
|          * | ||||
|          * @param stackName StackName | ||||
|          * @return PageBuilder | ||||
|          */ | ||||
|         public PageBuilder stackName(String stackName) { | ||||
|             mStackName = stackName; | ||||
|             return this; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,500 @@ | ||||
| @file:JvmName("Fragments") | ||||
| 
 | ||||
| package com.android.base.app.fragment.tools | ||||
| 
 | ||||
| 
 | ||||
| import android.view.View | ||||
| import androidx.annotation.NonNull | ||||
| import androidx.fragment.app.Fragment | ||||
| import androidx.fragment.app.FragmentActivity | ||||
| import androidx.fragment.app.FragmentManager | ||||
| import androidx.fragment.app.FragmentTransaction | ||||
| import com.android.base.app.activity.ActivityDelegate | ||||
| import com.android.base.app.activity.ActivityDelegateOwner | ||||
| import com.android.base.app.activity.ActivityState | ||||
| import com.android.base.app.fragment.delegates.FragmentDelegate | ||||
| import com.android.base.app.fragment.delegates.FragmentDelegateOwner | ||||
| import com.android.base.utils.common.javaClassName | ||||
| import kotlin.reflect.KClass | ||||
| 
 | ||||
| /**被此 annotation 标注的方法,表示需要使用 [Fragment] 的全类名作为 [FragmentTransaction] 中相关方法的 flag 参数的实参,比如 add/replace 等*/ | ||||
| annotation class UsingFragmentClassNameAsFlag | ||||
| 
 | ||||
| @JvmOverloads | ||||
| fun Fragment.exitFragment(immediate: Boolean = false) { | ||||
|     activity.exitFragment(immediate) | ||||
| } | ||||
| 
 | ||||
| @JvmOverloads | ||||
| fun FragmentActivity?.exitFragment(immediate: Boolean = false) { | ||||
|     if (this == null) { | ||||
|         return | ||||
|     } | ||||
|     val supportFragmentManager = this.supportFragmentManager | ||||
|     val backStackEntryCount = supportFragmentManager.backStackEntryCount | ||||
|     if (backStackEntryCount > 0) { | ||||
|         if (immediate) { | ||||
|             supportFragmentManager.popBackStackImmediate() | ||||
|         } else { | ||||
|             supportFragmentManager.popBackStack() | ||||
|         } | ||||
|     } else { | ||||
|         this.supportFinishAfterTransition() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * @param clazz    the interface container must implemented | ||||
|  * @param <T>      Type | ||||
|  * @return the interface context must implemented | ||||
|  */ | ||||
| fun <T> Fragment.requireContainerImplement(clazz: Class<T>): T? { | ||||
|     if (clazz.isInstance(parentFragment)) { | ||||
|         return clazz.cast(parentFragment) | ||||
|     } | ||||
|     return if (clazz.isInstance(activity)) { | ||||
|         clazz.cast(activity) | ||||
|     } else { | ||||
|         throw RuntimeException("use this Fragment:$this, Activity or Fragment must impl interface :$clazz") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * @param clazz    the interface context must implemented | ||||
|  * @param <T>      Type | ||||
|  * @return the interface context must implemented | ||||
|  */ | ||||
| fun <T> Fragment.requireContextImplement(clazz: Class<T>): T? { | ||||
|     return if (!clazz.isInstance(activity)) { | ||||
|         throw RuntimeException("use this Fragment:$this, Activity must impl interface :$clazz") | ||||
|     } else { | ||||
|         clazz.cast(activity) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * @param clazz    the interface parent must implemented | ||||
|  * @param <T>      Type | ||||
|  * @return the interface context must implemented | ||||
|  */ | ||||
| fun <T> Fragment.requireParentImplement(clazz: Class<T>): T? { | ||||
|     return if (!clazz.isInstance(parentFragment)) { | ||||
|         throw RuntimeException("use this Fragment:$this, ParentFragment must impl interface :$clazz") | ||||
|     } else { | ||||
|         clazz.cast(parentFragment) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** 使用 [clazz] 的全限定类名作为 tag 查找 Fragment */ | ||||
| fun <T : Fragment> FragmentManager.findFragmentByTag(clazz: KClass<T>): T? { | ||||
|     @Suppress("UNCHECKED_CAST") | ||||
|     return findFragmentByTag(clazz.java.name) as? T | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Fragment 出栈,包括 flag 对应的 Fragment,如果 flag 对应的 Fragment 不在栈中,此方法什么都不做。 | ||||
|  */ | ||||
| @UsingFragmentClassNameAsFlag | ||||
| fun FragmentManager.popBackUntil(flag: String, immediate: Boolean = false) { | ||||
|     if (immediate) { | ||||
|         popBackStackImmediate(flag, FragmentManager.POP_BACK_STACK_INCLUSIVE) | ||||
|     } else { | ||||
|         popBackStack(flag, FragmentManager.POP_BACK_STACK_INCLUSIVE) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Fragment 出栈,不包括 flag 对应的 Fragment,如果 flag 对应的 Fragment 不在栈中,此方法什么都不做。 | ||||
|  */ | ||||
| @UsingFragmentClassNameAsFlag | ||||
| fun FragmentManager.popBackTo(flag: String, immediate: Boolean = false) { | ||||
|     if (immediate) { | ||||
|         popBackStackImmediate(flag, 0) | ||||
|     } else { | ||||
|         popBackStack(flag, 0) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 回到对应的 Fragment,如果 Fragment 在栈中,则该 Fragment 回到栈顶,如果 Fragment 不在栈中,则做一次弹栈操作。 | ||||
|  */ | ||||
| @UsingFragmentClassNameAsFlag | ||||
| fun FragmentManager.backToFragment(flag: String, immediate: Boolean = false) { | ||||
|     val target = findFragmentByTag(flag) | ||||
| 
 | ||||
|     if (target != null) { | ||||
|         if (isFragmentInStack(target.javaClass)) { | ||||
|             if (immediate) { | ||||
|                 popBackStackImmediate(flag, 0) | ||||
|             } else { | ||||
|                 popBackStack(flag, 0) | ||||
|             } | ||||
|         } else { | ||||
|             popBackStack() | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun FragmentManager.clearBackStack(immediate: Boolean = false) { | ||||
|     if (immediate) { | ||||
|         this.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) | ||||
|     } else { | ||||
|         this.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @UsingFragmentClassNameAsFlag | ||||
| fun FragmentManager.isFragmentInStack(clazz: Class<out Fragment>): Boolean { | ||||
|     val backStackEntryCount = backStackEntryCount | ||||
|     if (backStackEntryCount == 0) { | ||||
|         return false | ||||
|     } | ||||
|     for (i in 0 until backStackEntryCount) { | ||||
|         if (clazz.name == getBackStackEntryAt(i).name) { | ||||
|             return true | ||||
|         } | ||||
|     } | ||||
|     return false | ||||
| } | ||||
| 
 | ||||
| inline fun FragmentManager.inTransaction(func: EnhanceFragmentTransaction.() -> Unit) { | ||||
|     val fragmentTransaction = beginTransaction() | ||||
|     EnhanceFragmentTransaction(this, fragmentTransaction).func() | ||||
|     fragmentTransaction.commit() | ||||
| } | ||||
| 
 | ||||
| inline fun FragmentActivity.inFragmentTransaction(func: EnhanceFragmentTransaction.() -> Unit) { | ||||
|     val transaction = supportFragmentManager.beginTransaction() | ||||
|     EnhanceFragmentTransaction(supportFragmentManager, transaction).func() | ||||
|     transaction.commit() | ||||
| } | ||||
| 
 | ||||
| fun <T> T.inSafelyFragmentTransaction( | ||||
|         func: EnhanceFragmentTransaction.() -> Unit | ||||
| ): Boolean where T : FragmentActivity, T : ActivityDelegateOwner { | ||||
| 
 | ||||
|     var delegate = findDelegate { | ||||
|         it is SafelyFragmentTransactionActivityDelegate | ||||
|     } as? SafelyFragmentTransactionActivityDelegate | ||||
| 
 | ||||
|     if (delegate == null) { | ||||
|         delegate = SafelyFragmentTransactionActivityDelegate() | ||||
|         addDelegate(delegate) | ||||
|     } | ||||
| 
 | ||||
|     val transaction = supportFragmentManager.beginTransaction() | ||||
| 
 | ||||
|     EnhanceFragmentTransaction(supportFragmentManager, transaction).func() | ||||
| 
 | ||||
|     return delegate.safeCommit(this, transaction) | ||||
| } | ||||
| 
 | ||||
| inline fun Fragment.inChildFragmentTransaction(func: EnhanceFragmentTransaction.() -> Unit) { | ||||
|     val transaction = childFragmentManager.beginTransaction() | ||||
|     EnhanceFragmentTransaction(childFragmentManager, transaction).func() | ||||
|     transaction.commit() | ||||
| } | ||||
| 
 | ||||
| fun <T> T.inSafelyChildFragmentTransaction( | ||||
|         func: EnhanceFragmentTransaction.() -> Unit | ||||
| ): Boolean where T : Fragment, T : FragmentDelegateOwner { | ||||
| 
 | ||||
|     var delegate: SafelyFragmentTransactionFragmentDelegate? = findDelegate { | ||||
|         it is SafelyFragmentTransactionFragmentDelegate | ||||
|     } as? SafelyFragmentTransactionFragmentDelegate | ||||
| 
 | ||||
|     if (delegate == null) { | ||||
|         delegate = SafelyFragmentTransactionFragmentDelegate() | ||||
|         addDelegate(delegate) | ||||
|     } | ||||
| 
 | ||||
|     val transaction = childFragmentManager.beginTransaction() | ||||
| 
 | ||||
|     EnhanceFragmentTransaction(childFragmentManager, transaction).func() | ||||
| 
 | ||||
|     return delegate.safeCommit(this, transaction) | ||||
| } | ||||
| 
 | ||||
| private class SafelyFragmentTransactionActivityDelegate : ActivityDelegate<FragmentActivity> { | ||||
| 
 | ||||
|     private val mPendingTransactions = mutableListOf<FragmentTransaction>() | ||||
| 
 | ||||
|     fun safeCommit(@NonNull activityDelegateOwner: ActivityDelegateOwner, @NonNull transaction: FragmentTransaction): Boolean { | ||||
|         val status = activityDelegateOwner.status | ||||
|         val isCommitterResumed = (status == ActivityState.CREATE || status == ActivityState.START || status == ActivityState.RESUME) | ||||
| 
 | ||||
|         return if (isCommitterResumed) { | ||||
|             transaction.commit() | ||||
|             false | ||||
|         } else { | ||||
|             mPendingTransactions.add(transaction) | ||||
|             true | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun onResumeFragments() { | ||||
|         if (mPendingTransactions.isNotEmpty()) { | ||||
|             mPendingTransactions.forEach { it.commit() } | ||||
|             mPendingTransactions.clear() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| private class SafelyFragmentTransactionFragmentDelegate : FragmentDelegate<Fragment> { | ||||
| 
 | ||||
|     private val pendingTransactions = mutableListOf<FragmentTransaction>() | ||||
| 
 | ||||
|     fun safeCommit(@NonNull fragment: Fragment, @NonNull transaction: FragmentTransaction): Boolean { | ||||
|         return if (fragment.isResumed) { | ||||
|             transaction.commit() | ||||
|             false | ||||
|         } else { | ||||
|             pendingTransactions.add(transaction) | ||||
|             true | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun onResume() { | ||||
|         if (pendingTransactions.isNotEmpty()) { | ||||
|             pendingTransactions.forEach { it.commit() } | ||||
|             pendingTransactions.clear() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| class EnhanceFragmentTransaction constructor( | ||||
|         private val fragmentManager: FragmentManager, | ||||
|         private val fragmentTransaction: FragmentTransaction | ||||
| ) : FragmentTransaction() { | ||||
| 
 | ||||
|     //------------------------------------------------------------------------------------------------ | ||||
|     // extra functions | ||||
|     //------------------------------------------------------------------------------------------------ | ||||
| 
 | ||||
|     /** | ||||
|      * 把 [fragment] 添加到回退栈中,并 hide 其他 fragment, | ||||
|      * 如果 [containerId]==0,则使用 [com.android.base.app.Sword.setDefaultFragmentContainerId] 中配置的 id, | ||||
|      * 如果 [tag] ==null 则使用 fragment 对应 class 的全限定类名。 | ||||
|      */ | ||||
|     fun addWithStack(containerId: Int = 0, fragment: Fragment, tag: String? = null, transition: Boolean = true): EnhanceFragmentTransaction { | ||||
|         //set add to stack | ||||
|         val nonnullTag = (tag ?: fragment.javaClassName()) | ||||
|         addToBackStack(nonnullTag) | ||||
|         //add | ||||
|         fragmentTransaction.add(confirmLayoutId(containerId), fragment, nonnullTag) | ||||
|         //hide top | ||||
|         hideTopFragment() | ||||
|         if (transition) { | ||||
|             //set a transition | ||||
|             setOpeningTransition() | ||||
|         } | ||||
|         return this | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * replace 方式把 [fragment] 添加到回退栈中,如果 [containerId]==0,则使用 [com.android.base.app.Sword.setDefaultFragmentContainerId] 中配置的 id, | ||||
|      * 如果 [tag] ==null 则使用 fragment 对应 class 的全限定类名。 | ||||
|      */ | ||||
|     fun replaceWithStack(containerId: Int = 0, fragment: Fragment, tag: String? = null, transition: Boolean = true): EnhanceFragmentTransaction { | ||||
|         //set add to stack | ||||
|         val nonnullTag = (tag ?: fragment.javaClassName()) | ||||
|         addToBackStack(nonnullTag) | ||||
|         //add | ||||
|         fragmentTransaction.replace(confirmLayoutId(containerId), fragment, nonnullTag) | ||||
|         //set a transition | ||||
|         if (transition) { | ||||
|             setOpeningTransition() | ||||
|         } | ||||
|         return this | ||||
|     } | ||||
| 
 | ||||
|     private fun confirmLayoutId(layoutId: Int): Int { | ||||
|         return if (layoutId == 0) { | ||||
|             FragmentConfig.defaultContainerId() | ||||
|         } else { | ||||
|             layoutId | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 添加 [fragment],默认使用 [com.android.base.app.Sword.setDefaultFragmentContainerId] 中配置的 id,如果 [tag] 为null,则使用 [fragment] 的全限定类。 | ||||
|      */ | ||||
|     fun addFragment(fragment: Fragment, tag: String? = null): FragmentTransaction { | ||||
|         val nonnullTag = (tag ?: fragment.javaClassName()) | ||||
|         return fragmentTransaction.add(FragmentConfig.defaultContainerId(), fragment, nonnullTag) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 替换为 [fragment],id 使用 [com.android.base.app.Sword.setDefaultFragmentContainerId] 中配置的 id,如果 [tag] 为null,则使用 [fragment] 的全限定类名。 | ||||
|      */ | ||||
|     fun replaceFragment(fragment: Fragment, tag: String? = null, transition: Boolean = true): FragmentTransaction { | ||||
|         val nonnullTag = (tag ?: fragment.javaClassName()) | ||||
|         if (transition) { | ||||
|             setOpeningTransition() | ||||
|         } | ||||
|         return fragmentTransaction.replace(FragmentConfig.defaultContainerId(), fragment, nonnullTag) | ||||
|     } | ||||
| 
 | ||||
|     /**隐藏所有的 fragment */ | ||||
|     private fun hideFragments() { | ||||
|         for (fragment in fragmentManager.fragments) { | ||||
|             if (fragment != null && fragment.isVisible) { | ||||
|                 fragmentTransaction.hide(fragment) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /**隐藏第一个可见的 fragment */ | ||||
|     private fun hideTopFragment() { | ||||
|         fragmentManager.fragments.lastOrNull { it.isVisible }?.let { | ||||
|             fragmentTransaction.hide(it) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fun setOpeningTransition(): FragmentTransaction { | ||||
|         return fragmentTransaction.setTransition(TRANSIT_FRAGMENT_OPEN) | ||||
|     } | ||||
| 
 | ||||
|     fun setClosingTransition(): FragmentTransaction { | ||||
|         return fragmentTransaction.setTransition(TRANSIT_FRAGMENT_CLOSE) | ||||
|     } | ||||
| 
 | ||||
|     fun setFadingTransition(): FragmentTransaction { | ||||
|         return fragmentTransaction.setTransition(TRANSIT_FRAGMENT_FADE) | ||||
|     } | ||||
| 
 | ||||
|     //------------------------------------------------------------------------------------------------ | ||||
|     // original functions | ||||
|     //------------------------------------------------------------------------------------------------ | ||||
|     override fun setBreadCrumbShortTitle(res: Int): FragmentTransaction { | ||||
|         return fragmentTransaction.setBreadCrumbShortTitle(res) | ||||
|     } | ||||
| 
 | ||||
|     override fun setBreadCrumbShortTitle(text: CharSequence?): FragmentTransaction { | ||||
|         return fragmentTransaction.setBreadCrumbShortTitle(text) | ||||
|     } | ||||
| 
 | ||||
|     override fun setPrimaryNavigationFragment(fragment: Fragment?): FragmentTransaction { | ||||
|         return fragmentTransaction.setPrimaryNavigationFragment(fragment) | ||||
|     } | ||||
| 
 | ||||
|     override fun runOnCommit(runnable: Runnable): FragmentTransaction { | ||||
|         return fragmentTransaction.runOnCommit(runnable) | ||||
|     } | ||||
| 
 | ||||
|     override fun add(fragment: Fragment, tag: String?): FragmentTransaction { | ||||
|         return fragmentTransaction.add(fragment, tag) | ||||
|     } | ||||
| 
 | ||||
|     override fun add(containerViewId: Int, fragment: Fragment): FragmentTransaction { | ||||
|         return fragmentTransaction.add(containerViewId, fragment) | ||||
|     } | ||||
| 
 | ||||
|     override fun add(containerViewId: Int, fragment: Fragment, tag: String?): FragmentTransaction { | ||||
|         return fragmentTransaction.add(containerViewId, fragment, tag) | ||||
|     } | ||||
| 
 | ||||
|     override fun hide(fragment: Fragment): FragmentTransaction { | ||||
|         return fragmentTransaction.hide(fragment) | ||||
|     } | ||||
| 
 | ||||
|     override fun replace(containerViewId: Int, fragment: Fragment): FragmentTransaction { | ||||
|         return fragmentTransaction.replace(containerViewId, fragment) | ||||
|     } | ||||
| 
 | ||||
|     override fun replace(containerViewId: Int, fragment: Fragment, tag: String?): FragmentTransaction { | ||||
|         return fragmentTransaction.replace(containerViewId, fragment, tag) | ||||
|     } | ||||
| 
 | ||||
|     override fun detach(fragment: Fragment): FragmentTransaction { | ||||
|         return fragmentTransaction.detach(fragment) | ||||
|     } | ||||
| 
 | ||||
|     @Deprecated("") | ||||
|     override fun setAllowOptimization(allowOptimization: Boolean): FragmentTransaction { | ||||
|         return fragmentTransaction.setAllowOptimization(allowOptimization) | ||||
|     } | ||||
| 
 | ||||
|     override fun setCustomAnimations(enter: Int, exit: Int): FragmentTransaction { | ||||
|         return fragmentTransaction.setCustomAnimations(enter, exit) | ||||
|     } | ||||
| 
 | ||||
|     override fun setCustomAnimations(enter: Int, exit: Int, popEnter: Int, popExit: Int): FragmentTransaction { | ||||
|         return fragmentTransaction.setCustomAnimations(enter, exit, popEnter, popExit) | ||||
|     } | ||||
| 
 | ||||
|     override fun addToBackStack(name: String?): FragmentTransaction { | ||||
|         return fragmentTransaction.addToBackStack(name) | ||||
|     } | ||||
| 
 | ||||
|     override fun disallowAddToBackStack(): FragmentTransaction { | ||||
|         return fragmentTransaction.disallowAddToBackStack() | ||||
|     } | ||||
| 
 | ||||
|     override fun setTransitionStyle(styleRes: Int): FragmentTransaction { | ||||
|         return fragmentTransaction.setTransitionStyle(styleRes) | ||||
|     } | ||||
| 
 | ||||
|     override fun setTransition(transit: Int): FragmentTransaction { | ||||
|         return fragmentTransaction.setTransition(transit) | ||||
|     } | ||||
| 
 | ||||
|     override fun attach(fragment: Fragment): FragmentTransaction { | ||||
|         return fragmentTransaction.attach(fragment) | ||||
|     } | ||||
| 
 | ||||
|     override fun show(fragment: Fragment): FragmentTransaction { | ||||
|         return fragmentTransaction.show(fragment) | ||||
|     } | ||||
| 
 | ||||
|     override fun isEmpty(): Boolean { | ||||
|         return fragmentTransaction.isEmpty | ||||
|     } | ||||
| 
 | ||||
|     override fun remove(fragment: Fragment): FragmentTransaction { | ||||
|         return fragmentTransaction.remove(fragment) | ||||
|     } | ||||
| 
 | ||||
|     override fun isAddToBackStackAllowed(): Boolean { | ||||
|         return fragmentTransaction.isAddToBackStackAllowed | ||||
|     } | ||||
| 
 | ||||
|     override fun addSharedElement(sharedElement: View, name: String): FragmentTransaction { | ||||
|         return fragmentTransaction.addSharedElement(sharedElement, name) | ||||
|     } | ||||
| 
 | ||||
|     override fun setBreadCrumbTitle(res: Int): FragmentTransaction { | ||||
|         return fragmentTransaction.setBreadCrumbTitle(res) | ||||
|     } | ||||
| 
 | ||||
|     override fun setBreadCrumbTitle(text: CharSequence?): FragmentTransaction { | ||||
|         return fragmentTransaction.setBreadCrumbTitle(text) | ||||
|     } | ||||
| 
 | ||||
|     override fun setReorderingAllowed(reorderingAllowed: Boolean): FragmentTransaction { | ||||
|         return fragmentTransaction.setReorderingAllowed(reorderingAllowed) | ||||
|     } | ||||
| 
 | ||||
|     @Deprecated("commit will be called automatically") | ||||
|     override fun commit(): Int { | ||||
|         throw UnsupportedOperationException("commit will be called automatically") | ||||
|     } | ||||
| 
 | ||||
|     @Deprecated("commitAllowingStateLoss will be called automatically") | ||||
|     override fun commitAllowingStateLoss(): Int { | ||||
|         throw UnsupportedOperationException("commitAllowingStateLoss will be called automatically") | ||||
|     } | ||||
| 
 | ||||
|     @Deprecated("commitNow will be called automatically", ReplaceWith("throw UnsupportedOperationException(\"commitNow will be called automatically\")")) | ||||
|     override fun commitNow() { | ||||
|         throw UnsupportedOperationException("commitNow will be called automatically") | ||||
|     } | ||||
| 
 | ||||
|     @Deprecated("commitNowAllowingStateLoss will be called automatically", ReplaceWith("throw UnsupportedOperationException(\"commitNowAllowingStateLoss will be called automatically\")")) | ||||
|     override fun commitNowAllowingStateLoss() { | ||||
|         throw UnsupportedOperationException("commitNowAllowingStateLoss will be called automatically") | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,211 @@ | ||||
| package com.android.base.app.fragment.tools; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.os.Bundle; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import androidx.fragment.app.Fragment; | ||||
| import androidx.fragment.app.FragmentManager; | ||||
| import androidx.fragment.app.FragmentTransaction; | ||||
| 
 | ||||
| public abstract class TabManager { | ||||
| 
 | ||||
|     @SuppressWarnings("WeakerAccess") public static final int ATTACH_DETACH = 1; | ||||
|     @SuppressWarnings("WeakerAccess") public static final int SHOW_HIDE = 2; | ||||
| 
 | ||||
|     private final FragmentManager mFragmentManager; | ||||
|     private final int mContainerId; | ||||
|     private final Tabs mMainTabs; | ||||
|     private final Context mContext; | ||||
| 
 | ||||
|     private FragmentInfo mCurrentFragmentInfo; | ||||
| 
 | ||||
|     private static final String CURRENT_ID_KET = "main_tab_id"; | ||||
|     private static final int NONE = -1; | ||||
|     private int mCurrentId = NONE; | ||||
|     private final int mOperationType; | ||||
| 
 | ||||
|     public TabManager(Context context, FragmentManager fragmentManager, Tabs tabs, int containerId) { | ||||
|         this(context, fragmentManager, tabs, containerId, ATTACH_DETACH); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param operationType {@link #ATTACH_DETACH} or {@link #SHOW_HIDE} | ||||
|      */ | ||||
|     public TabManager(Context context, FragmentManager fragmentManager, Tabs tabs, int containerId, int operationType) { | ||||
|         if (operationType != ATTACH_DETACH && operationType != SHOW_HIDE) { | ||||
|             throw new IllegalArgumentException("the operationType must be ATTACH_DETACH or SHOW_HIDE"); | ||||
|         } | ||||
|         mMainTabs = tabs; | ||||
|         mContainerId = containerId; | ||||
|         mContext = context; | ||||
|         mFragmentManager = fragmentManager; | ||||
|         mOperationType = operationType; | ||||
|     } | ||||
| 
 | ||||
|     public final void setup(Bundle bundle) { | ||||
|         int pageId = mMainTabs.homePage().getPageId(); | ||||
|         if (bundle == null) { | ||||
|             switchPage(pageId); | ||||
|         } else { | ||||
|             mCurrentId = bundle.getInt(CURRENT_ID_KET, pageId); | ||||
|             restoreState(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void restoreState() { | ||||
|         List<FragmentInfo> pages = mMainTabs.getPages(); | ||||
|         for (FragmentInfo page : pages) { | ||||
|             page.setInstance(mFragmentManager.findFragmentByTag(page.getTag())); | ||||
|             if (mCurrentId == page.getPageId()) { | ||||
|                 mCurrentFragmentInfo = page; | ||||
|             } | ||||
|         } | ||||
|         if (mCurrentId == NONE) { | ||||
|             doChangeTab(mMainTabs.homePage().getPageId()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void switchPage(int pageId) { | ||||
|         if (mCurrentId == pageId) { | ||||
|             return; | ||||
|         } | ||||
|         FragmentTransaction ft = null; | ||||
|         if (mCurrentFragmentInfo != null) { | ||||
|             Fragment fragment = mCurrentFragmentInfo.getInstance(); | ||||
|             if (fragment != null) { | ||||
|                 ft = mFragmentManager.beginTransaction(); | ||||
|                 hideOrDetach(ft, fragment); | ||||
|             } | ||||
|         } | ||||
|         if (ft != null) { | ||||
|             ft.commit(); | ||||
|         } | ||||
|         doChangeTab(pageId); | ||||
|     } | ||||
| 
 | ||||
|     private void hideOrDetach(FragmentTransaction ft, Fragment fragment) { | ||||
|         if (mOperationType == SHOW_HIDE) { | ||||
|             ft.hide(fragment); | ||||
|         } else { | ||||
|             ft.detach(fragment); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void showOrAttach(FragmentTransaction fragmentTransaction, Fragment fragment) { | ||||
|         if (mOperationType == SHOW_HIDE) { | ||||
|             fragmentTransaction.show(fragment); | ||||
|         } else { | ||||
|             fragmentTransaction.attach(fragment); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void doChangeTab(int fragmentId) { | ||||
|         FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); | ||||
|         FragmentInfo fragmentInfo = mMainTabs.getFragmentInfo(fragmentId); | ||||
|         Fragment fragment = fragmentInfo.getInstance(); | ||||
|         if (fragment != null) { | ||||
|             showOrAttach(fragmentTransaction, fragment); | ||||
|         } else { | ||||
|             Fragment newFragment = fragmentInfo.newFragment(mContext); | ||||
|             fragmentInfo.setInstance(newFragment); | ||||
|             onFragmentCreated(fragmentId, newFragment); | ||||
|             fragmentTransaction.add(mContainerId, newFragment, fragmentInfo.getTag()); | ||||
|         } | ||||
|         mCurrentFragmentInfo = fragmentInfo; | ||||
|         mCurrentId = fragmentId; | ||||
|         fragmentTransaction.commit(); | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("WeakerAccess") | ||||
|     public void selectTabByPosition(int position) { | ||||
|         switchPage(mMainTabs.getIdByPosition(position)); | ||||
|     } | ||||
| 
 | ||||
|     public void selectTabById(int tabId) { | ||||
|         selectTabByPosition(mMainTabs.getPositionById(tabId)); | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("unused") | ||||
|     public int getCurrentPosition() { | ||||
|         return mMainTabs.getPositionById(mCurrentId); | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("WeakerAccess,unused") | ||||
|     protected void onFragmentCreated(int id, Fragment newFragment) { | ||||
|     } | ||||
| 
 | ||||
|     public void onSaveInstanceState(Bundle bundle) { | ||||
|         bundle.putInt(CURRENT_ID_KET, mCurrentId); | ||||
|     } | ||||
| 
 | ||||
|     public static abstract class Tabs { | ||||
| 
 | ||||
|         private final List<FragmentInfo> mPages; | ||||
| 
 | ||||
|         protected Tabs() { | ||||
|             mPages = new ArrayList<>(); | ||||
|         } | ||||
| 
 | ||||
|         protected void add(FragmentInfo page) { | ||||
|             mPages.add(page); | ||||
|         } | ||||
| 
 | ||||
|         FragmentInfo homePage() { | ||||
|             return mPages.get(defaultIndex()); | ||||
|         } | ||||
| 
 | ||||
|         protected int defaultIndex() { | ||||
|             return 0; | ||||
|         } | ||||
| 
 | ||||
|         public int size() { | ||||
|             return mPages.size(); | ||||
|         } | ||||
| 
 | ||||
|         FragmentInfo getFragmentInfo(int id) { | ||||
|             for (FragmentInfo page : mPages) { | ||||
|                 if (page.getPageId() == id) { | ||||
|                     return page; | ||||
|                 } | ||||
|             } | ||||
|             throw new IllegalArgumentException("MainPages not has this pageId :" + id); | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * @param clazz Fragment对应的clazz | ||||
|          * @return pagerId ,没有则返回-1 | ||||
|          */ | ||||
|         @SuppressWarnings("unused") | ||||
|         int getIdByClazz(Class<? extends Fragment> clazz) { | ||||
|             for (FragmentInfo page : mPages) { | ||||
|                 if (page.getClazz() == clazz) { | ||||
|                     return page.getPageId(); | ||||
|                 } | ||||
|             } | ||||
|             return -1; | ||||
|         } | ||||
| 
 | ||||
|         List<FragmentInfo> getPages() { | ||||
|             return Collections.unmodifiableList(mPages); | ||||
|         } | ||||
| 
 | ||||
|         private int getPositionById(int tabId) { | ||||
|             int size = mPages.size(); | ||||
|             for (int i = 0; i < size; i++) { | ||||
|                 if (mPages.get(i).getPageId() == tabId) { | ||||
|                     return i; | ||||
|                 } | ||||
|             } | ||||
|             return -1; | ||||
|         } | ||||
| 
 | ||||
|         private int getIdByPosition(int position) { | ||||
|             return mPages.get(position).getPageId(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,52 @@ | ||||
| package com.android.base.app.fragment | ||||
| 
 | ||||
| import android.os.Bundle | ||||
| import androidx.lifecycle.LifecycleOwner | ||||
| import androidx.lifecycle.lifecycleScope | ||||
| import com.android.base.app.ui.LoadingView | ||||
| import kotlinx.coroutines.CancellationException | ||||
| import kotlinx.coroutines.delay | ||||
| import kotlinx.coroutines.launch | ||||
| 
 | ||||
| /** | ||||
|  *@author Ztiany | ||||
|  *      Email: ztiany3@gmail.com | ||||
|  *      Date : 2019-10-16 17:28 | ||||
|  */ | ||||
| internal fun <T> T.dismissDialog(recentShowingDialogTime: Long, minimumMills: Long, onDismiss: () -> Unit) where T : LoadingView, T : LifecycleOwner { | ||||
| 
 | ||||
|     if (!isLoadingDialogShowing()) { | ||||
|         onDismiss() | ||||
|         return | ||||
|     } | ||||
| 
 | ||||
|     val dialogShowingTime = System.currentTimeMillis() - recentShowingDialogTime | ||||
| 
 | ||||
|     if (dialogShowingTime >= minimumMills) { | ||||
|         dismissLoadingDialog() | ||||
|         onDismiss() | ||||
|     } else { | ||||
|         lifecycleScope.launch { | ||||
|             try { | ||||
|                 delay(minimumMills - dialogShowingTime) | ||||
|                 dismissLoadingDialog() | ||||
|                 onDismiss() | ||||
|             } catch (e: CancellationException) { | ||||
|                 onDismiss() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| private const val FRAGMENT_STATE_KEY = "fragment_state_key" | ||||
| 
 | ||||
| internal fun saveInstanceState(outState: Bundle, _state: Bundle?) { | ||||
|     _state?.let { | ||||
|         outState.putBundle(FRAGMENT_STATE_KEY, it) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| internal fun getInstanceState(state: Bundle?): Bundle? { | ||||
|     return state?.getBundle(FRAGMENT_STATE_KEY) | ||||
| } | ||||
| @ -0,0 +1,44 @@ | ||||
| package com.android.base.app.mvp | ||||
| 
 | ||||
| import androidx.annotation.CallSuper | ||||
| import java.lang.ref.WeakReference | ||||
| 
 | ||||
| abstract class AbstractPresenter<V : IBaseView> : IPresenter<V> { | ||||
| 
 | ||||
|     private var _view: WeakReference<V>? = null | ||||
| 
 | ||||
|     protected val view: V? | ||||
|         get() = if (_view != null) { | ||||
|             _view?.get() | ||||
|         } else null | ||||
| 
 | ||||
|     protected val isViewAttached: Boolean | ||||
|         get() = _view != null && _view?.get() != null | ||||
| 
 | ||||
|     final override fun bindView(view: V?) { | ||||
|         if (view == null) { | ||||
|             throw NullPointerException("Presenter bindView --> view is null") | ||||
|         } | ||||
|         if (_view != null) { | ||||
|             throw UnsupportedOperationException("Presenter bindView --> the view already bind") | ||||
|         } | ||||
|         _view = WeakReference(view) | ||||
|     } | ||||
| 
 | ||||
|     override fun onPostStart() {} | ||||
| 
 | ||||
|     override fun onPause() { | ||||
|     } | ||||
| 
 | ||||
|     override fun onResume() { | ||||
|     } | ||||
| 
 | ||||
|     @CallSuper | ||||
|     override fun onDestroy() { | ||||
|         if (_view != null) { | ||||
|             _view?.clear() | ||||
|             _view = null | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,3 @@ | ||||
| package com.android.base.app.mvp | ||||
| 
 | ||||
| interface IBaseView | ||||
| @ -0,0 +1,13 @@ | ||||
| package com.android.base.app.mvp | ||||
| 
 | ||||
| 
 | ||||
| interface IPresenter<V : IBaseView> : Lifecycle { | ||||
| 
 | ||||
|     /** | ||||
|      * bind a view | ||||
|      * | ||||
|      * @param view V | ||||
|      */ | ||||
|     fun bindView(view: V?) | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,31 @@ | ||||
| package com.android.base.app.mvp | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * @author Ztiany | ||||
|  * Email: 1169654504@qq.com | ||||
|  * Date : 2017-04-18 16:23 | ||||
|  */ | ||||
| interface Lifecycle { | ||||
| 
 | ||||
|     /** | ||||
|      * start the Lifecycle , initialize something, will be called only once | ||||
|      */ | ||||
|     fun onStart() | ||||
| 
 | ||||
|     /** | ||||
|      * will be called when view is ready. | ||||
|      */ | ||||
|     fun onPostStart() | ||||
| 
 | ||||
|     fun onResume() | ||||
| 
 | ||||
|     fun onPause() | ||||
| 
 | ||||
|     /** | ||||
|      * destroy the Lifecycle and release resource, will be called only once | ||||
|      */ | ||||
|     fun onDestroy() | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,66 @@ | ||||
| package com.android.base.app.mvp | ||||
| 
 | ||||
| import android.os.Bundle | ||||
| import androidx.fragment.app.Fragment | ||||
| import android.view.View | ||||
| import com.android.base.app.fragment.delegates.FragmentDelegate | ||||
| import com.android.base.app.fragment.delegates.FragmentDelegateOwner | ||||
| 
 | ||||
| 
 | ||||
| class PresenterBinder constructor(private val lifecycle: Lifecycle) : FragmentDelegate<Fragment> { | ||||
| 
 | ||||
|     private var isCalled: Boolean = false | ||||
|     private var host: Fragment? = null | ||||
| 
 | ||||
|     override fun onAttachToFragment(fragment: Fragment) { | ||||
|         host = fragment | ||||
|     } | ||||
| 
 | ||||
|     override fun onDetachFromFragment() { | ||||
|         host = null | ||||
|     } | ||||
| 
 | ||||
|     override fun onActivityCreated(savedInstanceState: Bundle?) { | ||||
|         if (!isCalled) { | ||||
|             lifecycle.onStart() | ||||
|             val activity = host?.activity | ||||
|             activity?.findViewById<View>(android.R.id.content)?.post { lifecycle.onPostStart() } | ||||
|             isCalled = true | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun onResume() { | ||||
|         lifecycle.onResume() | ||||
|     } | ||||
| 
 | ||||
|     override fun onPause() { | ||||
|         lifecycle.onPause() | ||||
|     } | ||||
| 
 | ||||
|     override fun onDestroy() { | ||||
|         lifecycle.onDestroy() | ||||
|     } | ||||
| 
 | ||||
|     companion object { | ||||
| 
 | ||||
|         /** | ||||
|          * @param v                     The  MVP of the V | ||||
|          * @param p                     The MVP of  the P | ||||
|          * @param <V>                   The MVP of the V | ||||
|         </V> */ | ||||
|         fun <V : IBaseView> bind(fragmentDelegateOwner: FragmentDelegateOwner, v: V, p: IPresenter<V>): PresenterBinder { | ||||
|             p.bindView(v) | ||||
|             val lifecycleDelegate = PresenterBinder(p) | ||||
|             fragmentDelegateOwner.addDelegate(lifecycleDelegate) | ||||
|             return lifecycleDelegate | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| fun <V : IBaseView> FragmentDelegateOwner.bindPresenter(v: V, p: IPresenter<V>): PresenterBinder { | ||||
|     p.bindView(v) | ||||
|     val lifecycleDelegate = PresenterBinder(p) | ||||
|     addDelegate(lifecycleDelegate) | ||||
|     return lifecycleDelegate | ||||
| } | ||||
| @ -0,0 +1,65 @@ | ||||
| package com.android.base.app.mvp | ||||
| 
 | ||||
| import androidx.annotation.CallSuper | ||||
| import com.android.base.rx.autodispose.AutoDisposeLifecycleScopeProviderEx | ||||
| import com.uber.autodispose.lifecycle.CorrespondingEventsFunction | ||||
| import com.uber.autodispose.lifecycle.LifecycleEndedException | ||||
| import com.uber.autodispose.lifecycle.LifecycleScopes | ||||
| import io.reactivex.CompletableSource | ||||
| import io.reactivex.Observable | ||||
| import io.reactivex.subjects.BehaviorSubject | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * work with RxJava | ||||
|  * | ||||
|  * @author Ztiany | ||||
|  * Date : 2016-10-19 12:17 | ||||
|  * Email: 1169654504@qq.com | ||||
|  */ | ||||
| abstract class RxPresenter<V : IBaseView> : AbstractPresenter<V>(), AutoDisposeLifecycleScopeProviderEx<RxPresenter.LifecycleEvent> { | ||||
| 
 | ||||
|     private val lifecycleSubject = BehaviorSubject.create<LifecycleEvent>() | ||||
| 
 | ||||
|     enum class LifecycleEvent { | ||||
|         START, | ||||
|         DESTROY | ||||
|     } | ||||
| 
 | ||||
|     @CallSuper | ||||
|     override fun onStart() { | ||||
|         lifecycleSubject.onNext(LifecycleEvent.START) | ||||
|     } | ||||
| 
 | ||||
|     @CallSuper | ||||
|     override fun onDestroy() { | ||||
|         lifecycleSubject.onNext(LifecycleEvent.DESTROY) | ||||
|         super@RxPresenter.onDestroy() | ||||
|     } | ||||
| 
 | ||||
|     final override fun lifecycle(): Observable<LifecycleEvent> { | ||||
|         return lifecycleSubject | ||||
|     } | ||||
| 
 | ||||
|     final override fun correspondingEvents(): CorrespondingEventsFunction<LifecycleEvent> { | ||||
|         return LIFECYCLE_CORRESPONDING_EVENTS | ||||
|     } | ||||
| 
 | ||||
|     final override fun peekLifecycle(): LifecycleEvent? { | ||||
|         return lifecycleSubject.value | ||||
|     } | ||||
| 
 | ||||
|     final override fun requestScope(): CompletableSource { | ||||
|         return LifecycleScopes.resolveScopeFromLifecycle<LifecycleEvent>(this) | ||||
|     } | ||||
| 
 | ||||
|     companion object { | ||||
|         internal val LIFECYCLE_CORRESPONDING_EVENTS: CorrespondingEventsFunction<LifecycleEvent> = CorrespondingEventsFunction { | ||||
|             return@CorrespondingEventsFunction when (it) { | ||||
|                 LifecycleEvent.START -> LifecycleEvent.DESTROY | ||||
|                 else -> throw LifecycleEndedException("Cannot bind to LifecycleEvent  when outside of it.") | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,61 @@ | ||||
| package com.android.base.app.mvvm | ||||
| 
 | ||||
| 
 | ||||
| import androidx.annotation.CallSuper | ||||
| import androidx.lifecycle.ViewModel | ||||
| import com.android.base.rx.autodispose.AutoDisposeLifecycleScopeProviderEx | ||||
| import com.uber.autodispose.lifecycle.CorrespondingEventsFunction | ||||
| import com.uber.autodispose.lifecycle.LifecycleEndedException | ||||
| import com.uber.autodispose.lifecycle.LifecycleScopes | ||||
| import io.reactivex.CompletableSource | ||||
| import io.reactivex.Observable | ||||
| import io.reactivex.subjects.BehaviorSubject | ||||
| 
 | ||||
| /** | ||||
|  * ArchViewModel  work with Rx | ||||
|  * | ||||
|  * @author Ztiany | ||||
|  * Email: 1169654504@qq.com | ||||
|  * Date : 2017-04-18 16:25 | ||||
|  */ | ||||
| abstract class RxViewModel : ViewModel(), AutoDisposeLifecycleScopeProviderEx<RxViewModel.ViewModelEvent> { | ||||
| 
 | ||||
|     private val archLifecycleSubject = BehaviorSubject.createDefault(ViewModelEvent.CREATED) | ||||
| 
 | ||||
|     enum class ViewModelEvent { | ||||
|         CREATED, CLEARED | ||||
|     } | ||||
| 
 | ||||
|     @CallSuper | ||||
|     override fun onCleared() { | ||||
|         archLifecycleSubject.onNext(ViewModelEvent.CLEARED) | ||||
|         super.onCleared() | ||||
|     } | ||||
| 
 | ||||
|     final override fun correspondingEvents(): CorrespondingEventsFunction<ViewModelEvent> { | ||||
|         return CORRESPONDING_EVENTS | ||||
|     } | ||||
| 
 | ||||
|     final override fun lifecycle(): Observable<ViewModelEvent> { | ||||
|         return archLifecycleSubject.hide() | ||||
|     } | ||||
| 
 | ||||
|     final override fun peekLifecycle(): ViewModelEvent? { | ||||
|         return archLifecycleSubject.value | ||||
|     } | ||||
| 
 | ||||
|     final override fun requestScope(): CompletableSource { | ||||
|         return LifecycleScopes.resolveScopeFromLifecycle(this) | ||||
|     } | ||||
| 
 | ||||
|     companion object { | ||||
|         private val CORRESPONDING_EVENTS = CorrespondingEventsFunction<ViewModelEvent> { event -> | ||||
|             when (event) { | ||||
|                 ViewModelEvent.CREATED -> ViewModelEvent.CLEARED | ||||
|                 else -> throw LifecycleEndedException( | ||||
|                         "Cannot bind to ViewModel lifecycle after onCleared.") | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,39 @@ | ||||
| package com.android.base.app.ui; | ||||
| 
 | ||||
| import com.android.base.adapter.DataManager; | ||||
| 
 | ||||
| /** | ||||
|  * @author Ztiany | ||||
|  * Email: ztiany3@gmail.com | ||||
|  * Date : 2018-04-27 14:04 | ||||
|  */ | ||||
| public class AutoPageNumber extends PageNumber { | ||||
| 
 | ||||
|     private final DataManager mDataManager; | ||||
|     private final RefreshListLayout mRefreshListLayout; | ||||
| 
 | ||||
|     public AutoPageNumber(RefreshListLayout refreshListLayout, DataManager dataManager) { | ||||
|         mRefreshListLayout = refreshListLayout; | ||||
|         mDataManager = dataManager; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int getCurrentPage() { | ||||
|         return calcPageNumber(mDataManager.getDataSize()); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int getLoadingPage() { | ||||
|         if (mRefreshListLayout.isRefreshing()) { | ||||
|             return getPageStart(); | ||||
|         } else { | ||||
|             return getCurrentPage() + 1; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int getItemCount() { | ||||
|         return mDataManager.getDataSize(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,22 @@ | ||||
| package com.android.base.app.ui; | ||||
| 
 | ||||
| 
 | ||||
| import com.android.base.R; | ||||
| 
 | ||||
| public class CommonId { | ||||
| 
 | ||||
|     private CommonId() { | ||||
|         throw new UnsupportedOperationException(); | ||||
|     } | ||||
| 
 | ||||
|     public static final int REFRESH_ID = R.id.base_refresh_layout; | ||||
|     public static final int STATE_ID = R.id.base_state_layout; | ||||
|     public static final int LIST_ID = R.id.base_list_layout; | ||||
| 
 | ||||
|     public static final int RETRY_TV_ID = R.id.base_retry_tv; | ||||
|     public static final int RETRY_IV_ID = R.id.base_retry_icon; | ||||
|     public static final int RETRY_BTN_ID = R.id.base_retry_btn; | ||||
| 
 | ||||
|     public static final int TOOLBAR_ID = R.id.common_toolbar; | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,11 @@ | ||||
| package com.android.base.app.ui | ||||
| 
 | ||||
| import androidx.lifecycle.LifecycleOwner | ||||
| import androidx.lifecycle.LiveData | ||||
| import androidx.lifecycle.Observer | ||||
| import com.android.base.app.Sword | ||||
| import com.android.base.data.State | ||||
| import timber.log.Timber | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| @ -0,0 +1,25 @@ | ||||
| package com.android.base.app.ui | ||||
| 
 | ||||
| import androidx.annotation.StringRes | ||||
| 
 | ||||
| /** | ||||
|  * 显示通用的 LoadingDialog 和 Message | ||||
|  * | ||||
|  * @author Ztiany | ||||
|  * Email: 1169654504@qq.com | ||||
|  * Date : 2016-12-02 15:12 | ||||
|  */ | ||||
| interface LoadingView { | ||||
| 
 | ||||
|     fun showLoadingDialog() | ||||
|     fun showLoadingDialog(cancelable: Boolean) | ||||
|     fun showLoadingDialog(message: CharSequence, cancelable: Boolean) | ||||
|     fun showLoadingDialog(@StringRes messageId: Int, cancelable: Boolean) | ||||
|     fun dismissLoadingDialog() | ||||
|     fun dismissLoadingDialog(minimumMills: Long, onDismiss: () -> Unit) | ||||
|     fun isLoadingDialogShowing(): Boolean | ||||
| 
 | ||||
|     fun showMessage(message: CharSequence) | ||||
|     fun showMessage(@StringRes messageId: Int) | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,9 @@ | ||||
| package com.android.base.app.ui; | ||||
| 
 | ||||
| import static com.android.base.app.ui.StateLayoutConfig.*; | ||||
| 
 | ||||
| public interface OnRetryActionListener { | ||||
| 
 | ||||
|     void onRetry(@RetryableState int state); | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,99 @@ | ||||
| package com.android.base.app.ui; | ||||
| 
 | ||||
| /** | ||||
|  * @author Ztiany | ||||
|  * @version 1.0 | ||||
|  */ | ||||
| public abstract class PageNumber { | ||||
| 
 | ||||
|     private static int PAGE_START = 1; | ||||
|     private static int PAGE_SIZE = 20; | ||||
| 
 | ||||
|     private int mPageStart; | ||||
|     private int mPageSize; | ||||
| 
 | ||||
|     private Object mPageToken; | ||||
| 
 | ||||
|     @SuppressWarnings("WeakerAccess,unused") | ||||
|     public PageNumber() { | ||||
|         this(PAGE_START, PAGE_SIZE); | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("WeakerAccess") | ||||
|     public PageNumber(int pageStart, int pageSize) { | ||||
|         mPageStart = pageStart; | ||||
|         mPageSize = pageSize; | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("unused") | ||||
|     public void setPageToken(Object pageToken) { | ||||
|         mPageToken = pageToken; | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("unchecked,unused") | ||||
|     public <T> T getPageToken() { | ||||
|         return (T) mPageToken; | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("unused") | ||||
|     public int getPageSize() { | ||||
|         return mPageSize; | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("WeakerAccess") | ||||
|     public int getPageStart() { | ||||
|         return mPageStart; | ||||
|     } | ||||
| 
 | ||||
|     public boolean hasMore(int size) { | ||||
|         return size >= mPageSize; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 根据page size计算当前的页码 | ||||
|      */ | ||||
|     int calcPageNumber(int dataSize) { | ||||
|         /*                          s=1        s=0 | ||||
|           19/20     = 0          1          0 | ||||
|           21/20     = 1          2          1 | ||||
|           54/20     = 2          3          2 | ||||
|           64/20     = 3          4          3 | ||||
|          */ | ||||
|         int pageNumber; | ||||
|         int pageSize = mPageSize; | ||||
|         int pageStart = mPageStart; | ||||
|         if (pageStart == 0) { | ||||
|             pageNumber = (dataSize / pageSize) - 1; | ||||
|             pageNumber = pageNumber < 0 ? 0 : pageNumber; | ||||
|         } else if (pageStart == 1) { | ||||
|             pageNumber = (dataSize / pageSize); | ||||
|             pageNumber = pageNumber < 1 ? 1 : pageNumber; | ||||
|         } else { | ||||
|             throw new RuntimeException("pageStart must be 0 or 1"); | ||||
|         } | ||||
|         return pageNumber; | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("unused") | ||||
|     public void changePageSetting(int pageStart, int pageSize) { | ||||
|         mPageStart = pageStart; | ||||
|         mPageSize = pageSize; | ||||
|     } | ||||
| 
 | ||||
|     public static void setDefaultPageStart(int pageSize) { | ||||
|         PAGE_START = pageSize; | ||||
|     } | ||||
| 
 | ||||
|     public static void setDefaultPageSize(int pageSize) { | ||||
|         PAGE_SIZE = pageSize; | ||||
|     } | ||||
| 
 | ||||
|     public abstract int getCurrentPage(); | ||||
| 
 | ||||
|     @SuppressWarnings("unused") | ||||
|     public abstract int getLoadingPage(); | ||||
| 
 | ||||
|     @SuppressWarnings("unused") | ||||
|     public abstract int getItemCount(); | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,31 @@ | ||||
| package com.android.base.app.ui; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| /** | ||||
|  * 列表视图行为 | ||||
|  * | ||||
|  * @author Ztiany | ||||
|  * Email: 1169654504@qq.com | ||||
|  * Date : 2017-03-29 22:16 | ||||
|  */ | ||||
| public interface RefreshListLayout<T> extends RefreshStateLayout { | ||||
| 
 | ||||
|     void replaceData(List<T> data); | ||||
| 
 | ||||
|     void addData(List<T> data); | ||||
| 
 | ||||
|     PageNumber getPager(); | ||||
| 
 | ||||
|     boolean isEmpty(); | ||||
| 
 | ||||
|     void loadMoreCompleted(boolean hasMore); | ||||
| 
 | ||||
|     void loadMoreFailed(); | ||||
| 
 | ||||
|     boolean isLoadingMore(); | ||||
| 
 | ||||
|     @Override | ||||
|     boolean isRefreshing(); | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,40 @@ | ||||
| package com.android.base.app.ui; | ||||
| 
 | ||||
| /** | ||||
|  * <br/>   对下拉刷新的抽象 | ||||
|  * <br/>   Email: 1169654504@qq.com | ||||
|  * | ||||
|  * @author Ztiany | ||||
|  * @version 1.0 | ||||
|  */ | ||||
| public interface RefreshLoadMoreView { | ||||
| 
 | ||||
|     void autoRefresh(); | ||||
| 
 | ||||
|     void refreshCompleted(); | ||||
| 
 | ||||
|     void loadMoreCompleted(boolean hasMore); | ||||
| 
 | ||||
|     void loadMoreFailed(); | ||||
| 
 | ||||
|     void setRefreshHandler(RefreshHandler refreshHandler); | ||||
| 
 | ||||
|     void setLoadMoreHandler(LoadMoreHandler loadMoreHandler); | ||||
| 
 | ||||
|     boolean isRefreshing(); | ||||
| 
 | ||||
|     boolean isLoadingMore(); | ||||
| 
 | ||||
|     void setRefreshEnable(boolean enable); | ||||
| 
 | ||||
|     void setLoadMoreEnable(boolean enable); | ||||
| 
 | ||||
|     interface RefreshHandler { | ||||
|         void onRefresh(); | ||||
|     } | ||||
| 
 | ||||
|     interface LoadMoreHandler { | ||||
|         void onRefresh(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,31 @@ | ||||
| package com.android.base.app.ui; | ||||
| 
 | ||||
| import android.view.View; | ||||
| 
 | ||||
| /** | ||||
|  * RefreshLoadMoreView Factory | ||||
|  * | ||||
|  * @author Ztiany | ||||
|  * Email: 1169654504@qq.com | ||||
|  * @version 1.0 | ||||
|  */ | ||||
| public class RefreshLoadViewFactory { | ||||
| 
 | ||||
|     private static Factory sFactory; | ||||
| 
 | ||||
|     public static RefreshLoadMoreView createRefreshView(View view) { | ||||
|         if (sFactory != null) { | ||||
|             return sFactory.createRefreshView(view); | ||||
|         } | ||||
|         throw new IllegalArgumentException("RefreshLoadViewFactory does not support create RefreshLoadMoreView . the view :" + view); | ||||
|     } | ||||
| 
 | ||||
|     public static void registerFactory(Factory factory) { | ||||
|         sFactory = factory; | ||||
|     } | ||||
| 
 | ||||
|     public interface Factory { | ||||
|         RefreshLoadMoreView createRefreshView(View view); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,11 @@ | ||||
| package com.android.base.app.ui; | ||||
| 
 | ||||
| public interface RefreshStateLayout extends StateLayout{ | ||||
| 
 | ||||
|     void autoRefresh(); | ||||
| 
 | ||||
|     void refreshCompleted(); | ||||
| 
 | ||||
|     boolean isRefreshing(); | ||||
| 
 | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
					Loading…
					
					
				
		Reference in new issue