Compare commits

..

No commits in common. 'main' and 'androidx' have entirely different histories.

  1. 176
      .gitignore
  2. 13
      README.md
  3. 22
      lib_base/.gitignore
  4. 28
      lib_base/README.md
  5. 130
      lib_base/build.gradle
  6. 17
      lib_base/proguard-rules.pro
  7. 583
      lib_base/src/github/java/com/jaredrummler/android/util/HtmlBuilder.java
  8. 7
      lib_base/src/main/AndroidManifest.xml
  9. 79
      lib_base/src/main/java/com/android/base/adapter/DataManager.java
  10. 73
      lib_base/src/main/java/com/android/base/adapter/ItemHelper.java
  11. 185
      lib_base/src/main/java/com/android/base/adapter/list/BaseListAdapter.java
  12. 191
      lib_base/src/main/java/com/android/base/adapter/list/ListDataManagerImpl.java
  13. 21
      lib_base/src/main/java/com/android/base/adapter/list/SmartViewHolder.java
  14. 35
      lib_base/src/main/java/com/android/base/adapter/list/ViewHolder.java
  15. 154
      lib_base/src/main/java/com/android/base/adapter/pager/RecycleBin.java
  16. 118
      lib_base/src/main/java/com/android/base/adapter/pager/RecyclingPagerAdapter.java
  17. 33
      lib_base/src/main/java/com/android/base/adapter/pager/SimpleViewPagerAdapter.kt
  18. 69
      lib_base/src/main/java/com/android/base/adapter/pager/ViewPagerAdapter.java
  19. 48
      lib_base/src/main/java/com/android/base/adapter/pager/ViewPagerFragmentAdapter.java
  20. 19
      lib_base/src/main/java/com/android/base/adapter/pager/ViewPagerInfo.java
  21. 51
      lib_base/src/main/java/com/android/base/adapter/pager/ViewPagerStateFragmentAdapter.java
  22. 50
      lib_base/src/main/java/com/android/base/adapter/recycler/AdapterDataObserverProxy.java
  23. 266
      lib_base/src/main/java/com/android/base/adapter/recycler/DiffRecyclerAdapter.java
  24. 5
      lib_base/src/main/java/com/android/base/adapter/recycler/HeaderSize.java
  25. 38
      lib_base/src/main/java/com/android/base/adapter/recycler/ItemViewBinder.kt
  26. 14
      lib_base/src/main/java/com/android/base/adapter/recycler/KtViewHolder.kt
  27. 169
      lib_base/src/main/java/com/android/base/adapter/recycler/MultiTypeAdapter.java
  28. 177
      lib_base/src/main/java/com/android/base/adapter/recycler/RecyclerAdapter.java
  29. 272
      lib_base/src/main/java/com/android/base/adapter/recycler/RecyclerDataManagerImpl.java
  30. 41
      lib_base/src/main/java/com/android/base/adapter/recycler/SimpleRecyclerAdapter.kt
  31. 21
      lib_base/src/main/java/com/android/base/adapter/recycler/SmartViewHolder.java
  32. 29
      lib_base/src/main/java/com/android/base/adapter/recycler/ViewHolder.java
  33. 67
      lib_base/src/main/java/com/android/base/app/AndroidComponentLifecycleInjector.kt
  34. 80
      lib_base/src/main/java/com/android/base/app/ApplicationDelegate.kt
  35. 55
      lib_base/src/main/java/com/android/base/app/BaseAppContext.kt
  36. 133
      lib_base/src/main/java/com/android/base/app/Sword.kt
  37. 79
      lib_base/src/main/java/com/android/base/app/aac/AutoClearedValue.kt
  38. 23
      lib_base/src/main/java/com/android/base/app/aac/LiveDataEx.kt
  39. 116
      lib_base/src/main/java/com/android/base/app/aac/SingleLiveData.java
  40. 62
      lib_base/src/main/java/com/android/base/app/aac/SingleLiveEvent.java
  41. 69
      lib_base/src/main/java/com/android/base/app/activity/ActivityDelegate.java
  42. 20
      lib_base/src/main/java/com/android/base/app/activity/ActivityDelegateOwner.java
  43. 138
      lib_base/src/main/java/com/android/base/app/activity/ActivityDelegates.java
  44. 11
      lib_base/src/main/java/com/android/base/app/activity/ActivityState.kt
  45. 72
      lib_base/src/main/java/com/android/base/app/activity/BackHandlerHelper.java
  46. 191
      lib_base/src/main/java/com/android/base/app/activity/BaseActivity.kt
  47. 26
      lib_base/src/main/java/com/android/base/app/activity/InjectorBaseActivity.java
  48. 13
      lib_base/src/main/java/com/android/base/app/activity/OnBackPressListener.java
  49. 14
      lib_base/src/main/java/com/android/base/app/dagger/ActivityScope.java
  50. 22
      lib_base/src/main/java/com/android/base/app/dagger/ContextType.java
  51. 14
      lib_base/src/main/java/com/android/base/app/dagger/FragmentScope.java
  52. 18
      lib_base/src/main/java/com/android/base/app/dagger/HasViewInjector.java
  53. 26
      lib_base/src/main/java/com/android/base/app/dagger/Injectable.java
  54. 14
      lib_base/src/main/java/com/android/base/app/dagger/ViewComponent.java
  55. 15
      lib_base/src/main/java/com/android/base/app/dagger/ViewComponentBuilder.java
  56. 60
      lib_base/src/main/java/com/android/base/app/dagger/ViewInjection.java
  57. 18
      lib_base/src/main/java/com/android/base/app/dagger/ViewKey.java
  58. 48
      lib_base/src/main/java/com/android/base/app/dagger/ViewModelFactory.java
  59. 19
      lib_base/src/main/java/com/android/base/app/dagger/ViewModelKey.java
  60. 21
      lib_base/src/main/java/com/android/base/app/dagger/ViewModelModule.java
  61. 19
      lib_base/src/main/java/com/android/base/app/dagger/ViewScope.java
  62. 277
      lib_base/src/main/java/com/android/base/app/fragment/BaseDialogFragment.kt
  63. 295
      lib_base/src/main/java/com/android/base/app/fragment/BaseFragment.kt
  64. 89
      lib_base/src/main/java/com/android/base/app/fragment/BaseListDialogFragment.kt
  65. 89
      lib_base/src/main/java/com/android/base/app/fragment/BaseListFragment.kt
  66. 119
      lib_base/src/main/java/com/android/base/app/fragment/BaseListV2DialogFragment.kt
  67. 115
      lib_base/src/main/java/com/android/base/app/fragment/BaseListV2Fragment.kt
  68. 94
      lib_base/src/main/java/com/android/base/app/fragment/BaseStateDialogFragment.kt
  69. 101
      lib_base/src/main/java/com/android/base/app/fragment/BaseStateFragment.kt
  70. 169
      lib_base/src/main/java/com/android/base/app/fragment/FragmentDelegates.java
  71. 85
      lib_base/src/main/java/com/android/base/app/fragment/RefreshLoadMoreStateLayoutImpl.kt
  72. 207
      lib_base/src/main/java/com/android/base/app/fragment/RefreshableStateLayoutImpl.java
  73. 103
      lib_base/src/main/java/com/android/base/app/fragment/animator/FragmentAnimator.kt
  74. 103
      lib_base/src/main/java/com/android/base/app/fragment/animator/FragmentAnimatorHelper.java
  75. 74
      lib_base/src/main/java/com/android/base/app/fragment/delegates/FragmentDelegate.java
  76. 17
      lib_base/src/main/java/com/android/base/app/fragment/delegates/FragmentDelegateOwner.java
  77. 96
      lib_base/src/main/java/com/android/base/app/fragment/delegates/LazyDelegate.kt
  78. 114
      lib_base/src/main/java/com/android/base/app/fragment/tools/FragmentArgumentDelegate.kt
  79. 35
      lib_base/src/main/java/com/android/base/app/fragment/tools/FragmentConfig.java
  80. 133
      lib_base/src/main/java/com/android/base/app/fragment/tools/FragmentInfo.java
  81. 500
      lib_base/src/main/java/com/android/base/app/fragment/tools/Fragments.kt
  82. 211
      lib_base/src/main/java/com/android/base/app/fragment/tools/TabManager.java
  83. 52
      lib_base/src/main/java/com/android/base/app/fragment/utils.kt
  84. 44
      lib_base/src/main/java/com/android/base/app/mvp/AbstractPresenter.kt
  85. 3
      lib_base/src/main/java/com/android/base/app/mvp/IBaseView.kt
  86. 13
      lib_base/src/main/java/com/android/base/app/mvp/IPresenter.kt
  87. 31
      lib_base/src/main/java/com/android/base/app/mvp/Lifecycle.kt
  88. 66
      lib_base/src/main/java/com/android/base/app/mvp/PresenterBinder.kt
  89. 65
      lib_base/src/main/java/com/android/base/app/mvp/RxPresenter.kt
  90. 61
      lib_base/src/main/java/com/android/base/app/mvvm/RxViewModel.kt
  91. 39
      lib_base/src/main/java/com/android/base/app/ui/AutoPageNumber.java
  92. 22
      lib_base/src/main/java/com/android/base/app/ui/CommonId.java
  93. 11
      lib_base/src/main/java/com/android/base/app/ui/LiveStateHandler.kt
  94. 25
      lib_base/src/main/java/com/android/base/app/ui/LoadingView.kt
  95. 9
      lib_base/src/main/java/com/android/base/app/ui/OnRetryActionListener.java
  96. 99
      lib_base/src/main/java/com/android/base/app/ui/PageNumber.java
  97. 31
      lib_base/src/main/java/com/android/base/app/ui/RefreshListLayout.java
  98. 40
      lib_base/src/main/java/com/android/base/app/ui/RefreshLoadMoreView.java
  99. 31
      lib_base/src/main/java/com/android/base/app/ui/RefreshLoadViewFactory.java
  100. 11
      lib_base/src/main/java/com/android/base/app/ui/RefreshStateLayout.java
  101. Some files were not shown because too many files have changed in this diff Show More

176
.gitignore vendored

@ -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():
* // &lt;p&gt;&lt;b&gt;&lt;font color=&quot;red&quot; face=&quot;sans-serif-condensed&quot;&gt;Hello&lt;/font&gt;&lt;/b&gt;&lt;/p&gt;
*
* yourEditText.setText(html.toSpan());
* </code>
* </pre>
*
* <p>HTML Tags Supported by {@link TextView}:</p>
*
* <ul>
* <li><code>&lt;a href=&quot;...&quot;&gt;</code></li>
* <li><code>&lt;b&gt;</code></li>
* <li><code>&lt;big&gt;</code></li>
* <li><code>&lt;blockquote&gt;</code></li>
* <li><code>&lt;br&gt;</code></li>
* <li><code>&lt;cite&gt;</code></li>
* <li><code>&lt;dfn&gt;</code></li>
* <li><code>&lt;div align=&quot;...&quot;&gt;</code></li>
* <li><code>&lt;em&gt;</code></li>
* <li><code>&lt;font color=&quot;...&quot; face=&quot;...&quot;&gt;</code></li>
* <li><code>&lt;h1&gt;</code></li>
* <li><code>&lt;h2&gt;</code></li>
* <li><code>&lt;h3&gt;</code></li>
* <li><code>&lt;h4&gt;</code></li>
* <li><code>&lt;h5&gt;</code></li>
* <li><code>&lt;h6&gt;</code></li>
* <li><code>&lt;i&gt;</code></li>
* <li><code>&lt;img src=&quot;...&quot;&gt;</code></li>
* <li><code>&lt;p&gt;</code></li>
* <li><code>&lt;small&gt;</code></li>
* <li><code>&lt;strike&gt;</code></li>
* <li><code>&lt;strong&gt;</code></li>
* <li><code>&lt;sub&gt;</code></li>
* <li><code>&lt;sup&gt;</code></li>
* <li><code>&lt;tt&gt;</code></li>
* <li><code>&lt;u&gt;</code></li>
* <li><code>&lt;ul&gt;</code> (Android 7.0+)</li>
* <li><code>&lt;li&gt;</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的一个重要的设计理念是不吃掉任何一个异常产生的问题是当RxJava2downStream取消订阅后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 pressfalse表示由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…
Cancel
Save