Compare commits
No commits in common. 'main' and 'androidx' have entirely different histories.
@ -1,176 +0,0 @@ |
|||||||
# ---> Android |
|
||||||
# Gradle files |
|
||||||
.gradle/ |
|
||||||
build/ |
|
||||||
|
|
||||||
# Local configuration file (sdk path, etc) |
|
||||||
local.properties |
|
||||||
|
|
||||||
# Log/OS Files |
|
||||||
*.log |
|
||||||
|
|
||||||
# Android Studio generated files and folders |
|
||||||
captures/ |
|
||||||
.externalNativeBuild/ |
|
||||||
.cxx/ |
|
||||||
*.apk |
|
||||||
output.json |
|
||||||
|
|
||||||
# IntelliJ |
|
||||||
*.iml |
|
||||||
.idea/ |
|
||||||
misc.xml |
|
||||||
deploymentTargetDropDown.xml |
|
||||||
render.experimental.xml |
|
||||||
|
|
||||||
# Keystore files |
|
||||||
*.jks |
|
||||||
*.keystore |
|
||||||
|
|
||||||
# Google Services (e.g. APIs or Firebase) |
|
||||||
google-services.json |
|
||||||
|
|
||||||
# Android Profiling |
|
||||||
*.hprof |
|
||||||
|
|
||||||
# ---> CMake |
|
||||||
CMakeLists.txt.user |
|
||||||
CMakeCache.txt |
|
||||||
CMakeFiles |
|
||||||
CMakeScripts |
|
||||||
Testing |
|
||||||
Makefile |
|
||||||
cmake_install.cmake |
|
||||||
install_manifest.txt |
|
||||||
compile_commands.json |
|
||||||
CTestTestfile.cmake |
|
||||||
_deps |
|
||||||
|
|
||||||
# ---> Gradle |
|
||||||
.gradle |
|
||||||
**/build/ |
|
||||||
!src/**/build/ |
|
||||||
|
|
||||||
# Ignore Gradle GUI config |
|
||||||
gradle-app.setting |
|
||||||
|
|
||||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) |
|
||||||
!gradle-wrapper.jar |
|
||||||
|
|
||||||
# Avoid ignore Gradle wrappper properties |
|
||||||
!gradle-wrapper.properties |
|
||||||
|
|
||||||
# Cache of project |
|
||||||
.gradletasknamecache |
|
||||||
|
|
||||||
# Eclipse Gradle plugin generated files |
|
||||||
# Eclipse Core |
|
||||||
.project |
|
||||||
# JDT-specific (Eclipse Java Development Tools) |
|
||||||
.classpath |
|
||||||
|
|
||||||
# ---> Java |
|
||||||
# Compiled class file |
|
||||||
*.class |
|
||||||
|
|
||||||
# Log file |
|
||||||
*.log |
|
||||||
|
|
||||||
# BlueJ files |
|
||||||
*.ctxt |
|
||||||
|
|
||||||
# Mobile Tools for Java (J2ME) |
|
||||||
.mtj.tmp/ |
|
||||||
|
|
||||||
# Package Files # |
|
||||||
*.jar |
|
||||||
*.war |
|
||||||
*.nar |
|
||||||
*.ear |
|
||||||
*.zip |
|
||||||
*.tar.gz |
|
||||||
*.rar |
|
||||||
|
|
||||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml |
|
||||||
hs_err_pid* |
|
||||||
replay_pid* |
|
||||||
|
|
||||||
# ---> JetBrains |
|
||||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider |
|
||||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 |
|
||||||
|
|
||||||
# User-specific stuff |
|
||||||
.idea/**/workspace.xml |
|
||||||
.idea/**/tasks.xml |
|
||||||
.idea/**/usage.statistics.xml |
|
||||||
.idea/**/dictionaries |
|
||||||
.idea/**/shelf |
|
||||||
|
|
||||||
# AWS User-specific |
|
||||||
.idea/**/aws.xml |
|
||||||
|
|
||||||
# Generated files |
|
||||||
.idea/**/contentModel.xml |
|
||||||
|
|
||||||
# Sensitive or high-churn files |
|
||||||
.idea/**/dataSources/ |
|
||||||
.idea/**/dataSources.ids |
|
||||||
.idea/**/dataSources.local.xml |
|
||||||
.idea/**/sqlDataSources.xml |
|
||||||
.idea/**/dynamic.xml |
|
||||||
.idea/**/uiDesigner.xml |
|
||||||
.idea/**/dbnavigator.xml |
|
||||||
|
|
||||||
# Gradle |
|
||||||
.idea/**/gradle.xml |
|
||||||
.idea/**/libraries |
|
||||||
|
|
||||||
# Gradle and Maven with auto-import |
|
||||||
# When using Gradle or Maven with auto-import, you should exclude module files, |
|
||||||
# since they will be recreated, and may cause churn. Uncomment if using |
|
||||||
# auto-import. |
|
||||||
# .idea/artifacts |
|
||||||
# .idea/compiler.xml |
|
||||||
# .idea/jarRepositories.xml |
|
||||||
# .idea/modules.xml |
|
||||||
# .idea/*.iml |
|
||||||
# .idea/modules |
|
||||||
# *.iml |
|
||||||
# *.ipr |
|
||||||
|
|
||||||
# CMake |
|
||||||
cmake-build-*/ |
|
||||||
|
|
||||||
# Mongo Explorer plugin |
|
||||||
.idea/**/mongoSettings.xml |
|
||||||
|
|
||||||
# File-based project format |
|
||||||
*.iws |
|
||||||
|
|
||||||
# IntelliJ |
|
||||||
out/ |
|
||||||
|
|
||||||
# mpeltonen/sbt-idea plugin |
|
||||||
.idea_modules/ |
|
||||||
|
|
||||||
# JIRA plugin |
|
||||||
atlassian-ide-plugin.xml |
|
||||||
|
|
||||||
# Cursive Clojure plugin |
|
||||||
.idea/replstate.xml |
|
||||||
|
|
||||||
# SonarLint plugin |
|
||||||
.idea/sonarlint/ |
|
||||||
|
|
||||||
# Crashlytics plugin (for Android Studio and IntelliJ) |
|
||||||
com_crashlytics_export_strings.xml |
|
||||||
crashlytics.properties |
|
||||||
crashlytics-build.properties |
|
||||||
fabric.properties |
|
||||||
|
|
||||||
# Editor-based Rest Client |
|
||||||
.idea/httpRequests |
|
||||||
|
|
||||||
# Android studio 3.1+ serialized cache file |
|
||||||
.idea/caches/build_file_checksums.ser |
|
||||||
|
|
@ -1,2 +1,13 @@ |
|||||||
# android-libs |
# Android-Libs |
||||||
|
|
||||||
|
## modules |
||||||
|
|
||||||
|
- lib_base:common util classes and base classes,including BaseActivity, BaseFragment, BaseStateFragment, BaseListFragment, BaseAdapter, MVVM, MVP, MultiStateView, Dagger2, AAC, RxJava2, Kotlin, etc. |
||||||
|
- lib_media_selector:gets photos or videos from system. |
||||||
|
- lib_qrcode:library for scanning and generating qrcode. |
||||||
|
- lib_network:RxJava + Retrofit + OkHttp. |
||||||
|
- lib_cache: DiskLruCache + MMKV. |
||||||
|
|
||||||
|
## how to use |
||||||
|
|
||||||
|
refer [AndroidArchitecture](https://github.com/Ztiany/AndroidArchitecture) |
||||||
|
@ -0,0 +1,22 @@ |
|||||||
|
*.iml |
||||||
|
.gradle |
||||||
|
/local.properties |
||||||
|
/.idea |
||||||
|
.DS_Store |
||||||
|
/build |
||||||
|
/captures |
||||||
|
.externalNativeBuild |
||||||
|
*.iml |
||||||
|
.idea/ |
||||||
|
.gradle |
||||||
|
/local.properties |
||||||
|
.DS_Store |
||||||
|
/build |
||||||
|
/captures |
||||||
|
*.apk |
||||||
|
*.ap_ |
||||||
|
*.dex |
||||||
|
*.class |
||||||
|
bin/ |
||||||
|
gen/ |
||||||
|
local.properties |
@ -0,0 +1,28 @@ |
|||||||
|
# Android Development Base Library |
||||||
|
|
||||||
|
## 1 Third part Libraries |
||||||
|
|
||||||
|
- [AndroidX](https://developer.android.com/jetpack/androidx) |
||||||
|
- [RxJava(2)](https://github.com/ReactiveX/RxJava) |
||||||
|
- [RxAndroid(2)](https://github.com/ReactiveX/RxAndroid) |
||||||
|
- [AutoDispose](https://github.com/uber/AutoDispose) |
||||||
|
- [Dagger2](https://github.com/google/dagger) |
||||||
|
- [Glide](https://github.com/bumptech/glide) |
||||||
|
- [OkHttp](https://github.com/square/okhttp) |
||||||
|
- [Timber](https://github.com/JakeWharton/timber) |
||||||
|
- [WrapperAdapter](https://github.com/Ztiany/WrapperAdapter) |
||||||
|
- [MultiTypeAdapter](https://github.com/drakeet/MultiType) |
||||||
|
- [AndroidUtilCode](https://github.com/Blankj/AndroidUtilCode) |
||||||
|
|
||||||
|
## 2 Environment |
||||||
|
|
||||||
|
- AndroidStudio 3+ |
||||||
|
- Java8 with desugar |
||||||
|
|
||||||
|
## 3 How do we make Dagger2 working with SavedStateViewModel? |
||||||
|
|
||||||
|
refer |
||||||
|
|
||||||
|
- [saving-ui-state-with-viewmodel-savedstate-and-dagger](https://proandroiddev.com/saving-ui-state-with-viewmodel-savedstate-and-dagger-f77bcaeb8b08#7f89) |
||||||
|
- [AssistedInject](https://github.com/square/AssistedInject/issues/81) |
||||||
|
- [github-commit-browser](https://github.com/Nimrodda/github-commit-browser) |
@ -0,0 +1,130 @@ |
|||||||
|
apply plugin: 'com.android.library' |
||||||
|
apply plugin: 'kotlin-android' |
||||||
|
apply plugin: 'kotlin-android-extensions' |
||||||
|
apply plugin: 'kotlin-kapt' |
||||||
|
|
||||||
|
androidExtensions { |
||||||
|
experimental = true |
||||||
|
} |
||||||
|
|
||||||
|
android { |
||||||
|
compileSdkVersion rootProject.compileSdkVersion |
||||||
|
buildToolsVersion rootProject.buildToolsVersion |
||||||
|
|
||||||
|
defaultConfig { |
||||||
|
|
||||||
|
minSdkVersion rootProject.minSdkVersion |
||||||
|
targetSdkVersion rootProject.targetSdkVersion |
||||||
|
versionCode 1 |
||||||
|
versionName "1.0" |
||||||
|
|
||||||
|
javaCompileOptions { |
||||||
|
annotationProcessorOptions { |
||||||
|
includeCompileClasspath false |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
buildTypes { |
||||||
|
release { |
||||||
|
minifyEnabled false |
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
lintOptions { |
||||||
|
abortOnError false |
||||||
|
} |
||||||
|
|
||||||
|
compileOptions { |
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8 |
||||||
|
targetCompatibility JavaVersion.VERSION_1_8 |
||||||
|
} |
||||||
|
|
||||||
|
kotlinOptions { |
||||||
|
jvmTarget = "1.8" |
||||||
|
} |
||||||
|
|
||||||
|
sourceSets { |
||||||
|
main { |
||||||
|
java.srcDirs += "src/github/java" |
||||||
|
res.srcDirs += "src/github/res" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
dependencies { |
||||||
|
api fileTree(dir: 'libs', include: ['*.jar']) |
||||||
|
|
||||||
|
//测试 |
||||||
|
testImplementation testLibraries.junit |
||||||
|
|
||||||
|
//AndroidSupport |
||||||
|
api androidLibraries.supportV4 |
||||||
|
api androidLibraries.appcompat |
||||||
|
api androidLibraries.recyclerView |
||||||
|
api androidLibraries.material |
||||||
|
api androidLibraries.percentLayout |
||||||
|
api androidLibraries.constraintLayout |
||||||
|
api androidLibraries.viewpager2 |
||||||
|
api androidLibraries.annotation |
||||||
|
|
||||||
|
//AAC |
||||||
|
api androidLibraries.archRuntime |
||||||
|
api androidLibraries.archCommon |
||||||
|
api androidLibraries.fragmentKtx |
||||||
|
api androidLibraries.lifecycleCommon |
||||||
|
api androidLibraries.lifecycleCommonJava8 |
||||||
|
api androidLibraries.lifecycleRuntimeKtx |
||||||
|
api androidLibraries.lifecycleLiveDataCore |
||||||
|
api androidLibraries.lifecycleLiveData |
||||||
|
api androidLibraries.lifecycleLiveKtx |
||||||
|
api androidLibraries.lifecycleViewModel |
||||||
|
api androidLibraries.lifecycleViewModelKtx |
||||||
|
api androidLibraries.lifecycleExtensions |
||||||
|
api androidLibraries.lifecycleReactiveStreams |
||||||
|
|
||||||
|
//Kotlin |
||||||
|
api kotlinLibraries.kotlinStdlib |
||||||
|
api kotlinLibraries.kotlinReflect |
||||||
|
api kotlinLibraries.kotlinCoroutines |
||||||
|
api kotlinLibraries.kotlinAndroidCoroutines |
||||||
|
api kotlinLibraries.kotlinxCoroutinesRx2 |
||||||
|
|
||||||
|
//RxJava |
||||||
|
api thirdLibraries.rxJava |
||||||
|
api thirdLibraries.rxAndroid |
||||||
|
api thirdLibraries.rxBinding |
||||||
|
api thirdLibraries.autoDispose |
||||||
|
api thirdLibraries.autoDisposeAndroid |
||||||
|
api thirdLibraries.autoDisposeLifecycle |
||||||
|
api thirdLibraries.autoDisposeLifecycleArchcomponents |
||||||
|
|
||||||
|
/*Dagger2*/ |
||||||
|
api thirdLibraries.dagger2 |
||||||
|
api thirdLibraries.jsr305 |
||||||
|
api thirdLibraries.dagger2Android |
||||||
|
api thirdLibraries.dagger2AndroidSupport |
||||||
|
kapt thirdLibraries.dagger2Apt |
||||||
|
kapt thirdLibraries.dagger2AndroidApt |
||||||
|
|
||||||
|
//LoadMore |
||||||
|
api uiLibraries.wrapperAdapter |
||||||
|
|
||||||
|
//Adapter |
||||||
|
api uiLibraries.multiType |
||||||
|
|
||||||
|
//Log |
||||||
|
api thirdLibraries.timber |
||||||
|
|
||||||
|
//ImageLoader |
||||||
|
api thirdLibraries.glide |
||||||
|
api thirdLibraries.glideOkHttp |
||||||
|
api thirdLibraries.okHttp |
||||||
|
|
||||||
|
//Utils |
||||||
|
api thirdLibraries.utilcode |
||||||
|
api thirdLibraries.jOOR |
||||||
|
api thirdLibraries.supportOptional |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
# Add project specific ProGuard rules here. |
||||||
|
# By default, the flags in this file are appended to flags specified |
||||||
|
# in E:\DeveloperTools\sdk/tools/proguard/proguard-android.txt |
||||||
|
# You can edit the include path and order by changing the proguardFiles |
||||||
|
# directive in build.gradle. |
||||||
|
# |
||||||
|
# For more details, see |
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html |
||||||
|
|
||||||
|
# Add any project specific keep options here: |
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following |
||||||
|
# and specify the fully qualified class name to the JavaScript interface |
||||||
|
# class: |
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { |
||||||
|
# public *; |
||||||
|
#} |
@ -0,0 +1,583 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2017 Jared Rummler |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.jaredrummler.android.util; |
||||||
|
|
||||||
|
import android.os.Build; |
||||||
|
import android.text.Html; |
||||||
|
import android.text.Spanned; |
||||||
|
import android.widget.TextView; |
||||||
|
|
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.LinkedList; |
||||||
|
|
||||||
|
import androidx.annotation.RequiresApi; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p>Build a valid HTML string for a {@link TextView}.</p> |
||||||
|
* |
||||||
|
* <p>Example usage:</p> |
||||||
|
* |
||||||
|
* <pre> |
||||||
|
* <code> |
||||||
|
* HtmlBuilder html = new HtmlBuilder() |
||||||
|
* .p() |
||||||
|
* .b() |
||||||
|
* .font().color("red").face("sans-serif-condensed").text("Hello").close() |
||||||
|
* .close() |
||||||
|
* .close(); |
||||||
|
* |
||||||
|
* // html.toString():
|
||||||
|
* // <p><b><font color="red" face="sans-serif-condensed">Hello</font></b></p>
|
||||||
|
* |
||||||
|
* yourEditText.setText(html.toSpan()); |
||||||
|
* </code> |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* <p>HTML Tags Supported by {@link TextView}:</p> |
||||||
|
* |
||||||
|
* <ul> |
||||||
|
* <li><code><a href="..."></code></li> |
||||||
|
* <li><code><b></code></li> |
||||||
|
* <li><code><big></code></li> |
||||||
|
* <li><code><blockquote></code></li> |
||||||
|
* <li><code><br></code></li> |
||||||
|
* <li><code><cite></code></li> |
||||||
|
* <li><code><dfn></code></li> |
||||||
|
* <li><code><div align="..."></code></li> |
||||||
|
* <li><code><em></code></li> |
||||||
|
* <li><code><font color="..." face="..."></code></li> |
||||||
|
* <li><code><h1></code></li> |
||||||
|
* <li><code><h2></code></li> |
||||||
|
* <li><code><h3></code></li> |
||||||
|
* <li><code><h4></code></li> |
||||||
|
* <li><code><h5></code></li> |
||||||
|
* <li><code><h6></code></li> |
||||||
|
* <li><code><i></code></li> |
||||||
|
* <li><code><img src="..."></code></li> |
||||||
|
* <li><code><p></code></li> |
||||||
|
* <li><code><small></code></li> |
||||||
|
* <li><code><strike></code></li> |
||||||
|
* <li><code><strong></code></li> |
||||||
|
* <li><code><sub></code></li> |
||||||
|
* <li><code><sup></code></li> |
||||||
|
* <li><code><tt></code></li> |
||||||
|
* <li><code><u></code></li> |
||||||
|
* <li><code><ul></code> (Android 7.0+)</li> |
||||||
|
* <li><code><li></code> (Android 7.0+)</li> |
||||||
|
* </ul> |
||||||
|
* |
||||||
|
* @see <a href='https://github.com/jaredrummler/HtmlBuilder'>HtmlBuilder</a>
|
||||||
|
*/ |
||||||
|
public class HtmlBuilder { |
||||||
|
|
||||||
|
private final StringBuilder html = new StringBuilder(); |
||||||
|
|
||||||
|
private final LinkedList<String> tags = new LinkedList<>(); |
||||||
|
|
||||||
|
public HtmlBuilder open(String element, String data) { |
||||||
|
tags.add(element); |
||||||
|
html.append('<'); |
||||||
|
html.append(element); |
||||||
|
if (data != null) { |
||||||
|
html.append(' ').append(data); |
||||||
|
} |
||||||
|
html.append('>'); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder open(String element) { |
||||||
|
return open(element, null); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder close(String element) { |
||||||
|
html.append("</").append(element).append('>'); |
||||||
|
for (Iterator<String> iterator = tags.iterator(); iterator.hasNext(); ) { |
||||||
|
if (iterator.next().equals(element)) { |
||||||
|
iterator.remove(); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder close() { |
||||||
|
if (tags.isEmpty()) { |
||||||
|
return this; |
||||||
|
} |
||||||
|
html.append("</").append(tags.removeLast()).append('>'); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder close(char element) { |
||||||
|
return close(String.valueOf(element)); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder append(boolean b) { |
||||||
|
html.append(b); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder append(char c) { |
||||||
|
html.append(c); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder append(int i) { |
||||||
|
html.append(i); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder append(long l) { |
||||||
|
html.append(l); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder append(float f) { |
||||||
|
html.append(f); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder append(double d) { |
||||||
|
html.append(d); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder append(Object obj) { |
||||||
|
html.append(obj); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder append(String str) { |
||||||
|
html.append(str); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder append(StringBuffer sb) { |
||||||
|
html.append(sb); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder append(char[] chars) { |
||||||
|
html.append(chars); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder append(char[] str, int offset, int len) { |
||||||
|
html.append(str, offset, len); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder append(CharSequence csq) { |
||||||
|
html.append(csq); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder append(CharSequence csq, int start, int end) { |
||||||
|
html.append(csq, start, end); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder append(Tag tag) { |
||||||
|
html.append(tag.toString()); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder a(String href, String text) { |
||||||
|
return append(String.format("<a href=\"%s\">%s</a>", href, text)); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder b() { |
||||||
|
return open("b"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder b(String text) { |
||||||
|
html.append("<b>").append(text).append("</b>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder big() { |
||||||
|
return open("big"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder big(String text) { |
||||||
|
html.append("<big>").append(text).append("</big>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder blockquote() { |
||||||
|
return open("blockquote"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder blockquote(String text) { |
||||||
|
html.append("<blockquote>").append(text).append("</blockquote>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder br() { |
||||||
|
html.append("<br>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder cite() { |
||||||
|
return open("cite"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder cite(String text) { |
||||||
|
html.append("<cite>").append(text).append("</cite>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder dfn() { |
||||||
|
return open("dfn"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder dfn(String text) { |
||||||
|
html.append("<dfn>").append(text).append("</dfn>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder div() { |
||||||
|
return open("div"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder div(String align) { |
||||||
|
html.append(String.format("<div align=\"%s\">", align)); |
||||||
|
tags.add("div"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder em() { |
||||||
|
return open("em"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder em(String text) { |
||||||
|
html.append("<em>").append(text).append("</em>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Font font() { |
||||||
|
return new Font(this); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder font(int color, String text) { |
||||||
|
return font().color(color).text(text).close(); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder font(String face, String text) { |
||||||
|
return font().face(face).text(text).close(); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder h1() { |
||||||
|
return open("h1"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder h1(String text) { |
||||||
|
html.append("<h1>").append(text).append("</h1>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder h2() { |
||||||
|
return open("h2"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder h2(String text) { |
||||||
|
html.append("<h2>").append(text).append("</h2>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder h3() { |
||||||
|
return open("h3"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder h3(String text) { |
||||||
|
html.append("<h3>").append(text).append("</h3>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder h4() { |
||||||
|
return open("h4"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder h4(String text) { |
||||||
|
html.append("<h4>").append(text).append("</h4>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder h5() { |
||||||
|
return open("h5"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder h5(String text) { |
||||||
|
html.append("<h5>").append(text).append("</h5>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder h6() { |
||||||
|
return open("h6"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder h6(String text) { |
||||||
|
html.append("<h6>").append(text).append("</h6>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder i() { |
||||||
|
return open("i"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder i(String text) { |
||||||
|
html.append("<i>").append(text).append("</i>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Img img() { |
||||||
|
return new Img(this); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder img(String src) { |
||||||
|
return img().src(src).close(); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder p() { |
||||||
|
return open("p"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder p(String text) { |
||||||
|
html.append("<p>").append(text).append("</p>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder small() { |
||||||
|
return open("small"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder small(String text) { |
||||||
|
html.append("<small>").append(text).append("</small>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder strike() { |
||||||
|
return open("strike"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder strike(String text) { |
||||||
|
html.append("<strike>").append(text).append("</strike>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder strong() { |
||||||
|
return open("strong"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder strong(String text) { |
||||||
|
html.append("<strong>").append(text).append("</strong>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder sub() { |
||||||
|
return open("sub"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder sub(String text) { |
||||||
|
html.append("<sub>").append(text).append("</sub>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder sup() { |
||||||
|
return open("sup"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder sup(String text) { |
||||||
|
html.append("<sup>").append(text).append("</sup>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder tt() { |
||||||
|
return open("tt"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder tt(String text) { |
||||||
|
html.append("<tt>").append(text).append("</tt>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder u() { |
||||||
|
return open("u"); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder u(String text) { |
||||||
|
html.append("<u>").append(text).append("</u>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.N) |
||||||
|
public HtmlBuilder ul() { |
||||||
|
return open("ul"); |
||||||
|
} |
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.N) |
||||||
|
public HtmlBuilder li() { |
||||||
|
return open("li"); |
||||||
|
} |
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.N) |
||||||
|
public HtmlBuilder li(String text) { |
||||||
|
html.append("<li>").append(text).append("</li>"); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.N) |
||||||
|
public Spanned build(int flags) { |
||||||
|
return Html.fromHtml(html.toString(), flags); |
||||||
|
} |
||||||
|
|
||||||
|
public Spanned build() { |
||||||
|
//noinspection deprecation
|
||||||
|
return Html.fromHtml(html.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return html.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
public static class Tag { |
||||||
|
|
||||||
|
final HtmlBuilder builder; |
||||||
|
final String element; |
||||||
|
String separator = ""; |
||||||
|
|
||||||
|
public Tag(HtmlBuilder builder, String element) { |
||||||
|
this.builder = builder; |
||||||
|
this.element = element; |
||||||
|
open(); |
||||||
|
} |
||||||
|
|
||||||
|
protected void open() { |
||||||
|
builder.append('<').append(element).append(' '); |
||||||
|
} |
||||||
|
|
||||||
|
public HtmlBuilder close() { |
||||||
|
return builder.append("</").append(element).append('>'); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return builder.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public static class Font extends Tag { |
||||||
|
|
||||||
|
public Font() { |
||||||
|
this(new HtmlBuilder()); |
||||||
|
} |
||||||
|
|
||||||
|
public Font(HtmlBuilder builder) { |
||||||
|
super(builder, "font"); |
||||||
|
} |
||||||
|
|
||||||
|
public Font size(int size) { |
||||||
|
builder.append(separator).append("size=\"").append(size).append('\"'); |
||||||
|
separator = " "; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Font size(String size) { |
||||||
|
builder.append(separator).append("size=\"").append(size).append('\"'); |
||||||
|
separator = " "; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Font color(int color) { |
||||||
|
return color(String.format("#%06X", (0xFFFFFF & color))); |
||||||
|
} |
||||||
|
|
||||||
|
public Font color(String color) { |
||||||
|
builder.append(separator).append("color=\"").append(color).append('\"'); |
||||||
|
separator = " "; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Font face(String face) { |
||||||
|
builder.append(separator).append("face=\"").append(face).append('\"'); |
||||||
|
separator = " "; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Font text(String text) { |
||||||
|
builder.append('>').append(text); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public static class Img extends Tag { |
||||||
|
|
||||||
|
public Img() { |
||||||
|
this(new HtmlBuilder()); |
||||||
|
} |
||||||
|
|
||||||
|
public Img(HtmlBuilder builder) { |
||||||
|
super(builder, "img"); |
||||||
|
} |
||||||
|
|
||||||
|
public Img src(String src) { |
||||||
|
builder.append(separator).append("src=\"").append(src).append('\"'); |
||||||
|
separator = " "; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Img alt(String alt) { |
||||||
|
builder.append(separator).append("alt=\"").append(alt).append('\"'); |
||||||
|
separator = " "; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Img height(String height) { |
||||||
|
builder.append(separator).append("height=\"").append(height).append('\"'); |
||||||
|
separator = " "; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Img height(int height) { |
||||||
|
builder.append(separator).append("height=\"").append(height).append('\"'); |
||||||
|
separator = " "; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Img width(String width) { |
||||||
|
builder.append(separator).append("width=\"").append(width).append('\"'); |
||||||
|
separator = " "; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Img width(int width) { |
||||||
|
builder.append(separator).append("width=\"").append(width).append('\"'); |
||||||
|
separator = " "; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public HtmlBuilder close() { |
||||||
|
return builder.append('>'); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
<manifest package="com.android.base"> |
||||||
|
|
||||||
|
<application> |
||||||
|
|
||||||
|
</application> |
||||||
|
|
||||||
|
</manifest> |
@ -0,0 +1,79 @@ |
|||||||
|
package com.android.base.adapter; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import androidx.annotation.Nullable; |
||||||
|
|
||||||
|
/** |
||||||
|
* <pre> |
||||||
|
* 注意数据源引用的替换,只有setDataSource方法会把elements替换掉之前的数据源引用,其他方法都是基于现有数据集合做删除与添加操作。 |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* @author Ztiany |
||||||
|
* Date : 2016-09-12 11:33 |
||||||
|
* Email: 1169654504@qq.com |
||||||
|
*/ |
||||||
|
public interface DataManager<T> { |
||||||
|
|
||||||
|
void add(T element); |
||||||
|
|
||||||
|
void addAt(int location, T element); |
||||||
|
|
||||||
|
void addItems(List<T> elements); |
||||||
|
|
||||||
|
/** |
||||||
|
* 添加元素前会使用equals方法进行比较。 |
||||||
|
* |
||||||
|
* @param elements 元素 |
||||||
|
*/ |
||||||
|
void addItemsChecked(List<T> elements); |
||||||
|
|
||||||
|
void addItemsAt(int location, List<T> elements); |
||||||
|
|
||||||
|
void replace(T oldElement, T newElement); |
||||||
|
|
||||||
|
void replaceAt(int index, T element); |
||||||
|
|
||||||
|
/** |
||||||
|
* 清除之前集合中的数据,然后把elements添加到之前的集合中,不会使用elements作为数据源 |
||||||
|
* |
||||||
|
* @param elements 元素 |
||||||
|
*/ |
||||||
|
void replaceAll(List<T> elements); |
||||||
|
|
||||||
|
/** |
||||||
|
* 此方法会使用 newDataSource 替换掉之前的数据源,而不对之前的数据源做任何操作。 |
||||||
|
* |
||||||
|
* @param newDataSource 新的数据集 |
||||||
|
* @param notifyDataSetChanged 是否调用adapter的notifyDataSetChanged方法 |
||||||
|
*/ |
||||||
|
void setDataSource(List<T> newDataSource, boolean notifyDataSetChanged); |
||||||
|
|
||||||
|
void remove(T element); |
||||||
|
|
||||||
|
void removeAt(int index); |
||||||
|
|
||||||
|
void removeItems(List<T> elements); |
||||||
|
|
||||||
|
void removeItems(List<T> elements, boolean isSuccessive); |
||||||
|
|
||||||
|
@Nullable |
||||||
|
T getItem(int position); |
||||||
|
|
||||||
|
List<T> getItems(); |
||||||
|
|
||||||
|
int getDataSize(); |
||||||
|
|
||||||
|
boolean contains(T element); |
||||||
|
|
||||||
|
boolean isEmpty(); |
||||||
|
|
||||||
|
void clear(); |
||||||
|
|
||||||
|
/** |
||||||
|
* @param t element |
||||||
|
* @return -1 if not contains this element |
||||||
|
*/ |
||||||
|
int indexItem(T t); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,73 @@ |
|||||||
|
package com.android.base.adapter; |
||||||
|
|
||||||
|
import android.util.SparseArray; |
||||||
|
import android.view.View; |
||||||
|
import android.widget.TextView; |
||||||
|
|
||||||
|
import androidx.annotation.IdRes; |
||||||
|
import androidx.annotation.NonNull; |
||||||
|
import androidx.annotation.StringRes; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @author Ztiany |
||||||
|
* Email :1169654504@qq.com |
||||||
|
* Date :015-12-29 20:47 |
||||||
|
*/ |
||||||
|
public class ItemHelper { |
||||||
|
|
||||||
|
private View mItemView; |
||||||
|
private SparseArray<View> views; |
||||||
|
|
||||||
|
public ItemHelper(View itemView) { |
||||||
|
mItemView = itemView; |
||||||
|
views = new SparseArray<>(); |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("unchecked,WeakerAccess") |
||||||
|
public <T extends View> T getView(int viewId) { |
||||||
|
View view = views.get(viewId); |
||||||
|
if (view == null) { |
||||||
|
view = mItemView.findViewById(viewId); |
||||||
|
views.put(viewId, view); |
||||||
|
} |
||||||
|
return (T) view; |
||||||
|
} |
||||||
|
|
||||||
|
public ItemHelper setText(CharSequence str, @IdRes int viewId) { |
||||||
|
((TextView) getView(viewId)).setText(str == null ? "" : str); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public ItemHelper setText(@StringRes int strId, @IdRes int viewId) { |
||||||
|
((TextView) getView(viewId)).setText(strId); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public ItemHelper setTag(@NonNull Object object, @IdRes int tagId, @IdRes int viewID) { |
||||||
|
View view = getView(viewID); |
||||||
|
view.setTag(tagId, object); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public ItemHelper setTag(@NonNull Object object, @IdRes int viewID) { |
||||||
|
View view = getView(viewID); |
||||||
|
view.setTag(object); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public <T> T getTag(@IdRes int tagId, @IdRes int viewID) { |
||||||
|
View view = getView(viewID); |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
T tag = (T) view.getTag(tagId); |
||||||
|
return tag; |
||||||
|
} |
||||||
|
|
||||||
|
public <T> T getTag(@IdRes int viewID) { |
||||||
|
View view = getView(viewID); |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
T tag = (T) view.getTag(); |
||||||
|
return tag; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,185 @@ |
|||||||
|
package com.android.base.adapter.list; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.view.LayoutInflater; |
||||||
|
import android.view.View; |
||||||
|
import android.view.ViewGroup; |
||||||
|
import android.widget.BaseAdapter; |
||||||
|
|
||||||
|
import com.android.base.R; |
||||||
|
import com.android.base.adapter.DataManager; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
import androidx.annotation.Nullable; |
||||||
|
|
||||||
|
/** |
||||||
|
* absListView通用的Adapter,注意:只有setDataSource才能替换原有数据源的引用。 |
||||||
|
* |
||||||
|
* @param <T> 数据模型 |
||||||
|
* @author Ztiany |
||||||
|
*/ |
||||||
|
@SuppressWarnings("unused") |
||||||
|
public abstract class BaseListAdapter<T, VH extends ViewHolder> extends BaseAdapter implements DataManager<T> { |
||||||
|
|
||||||
|
private final Context mContext; |
||||||
|
private final static int ITEM_ID = R.id.base_item_tag_view_id; |
||||||
|
private DataManager<T> mDataManager; |
||||||
|
private final LayoutInflater mLayoutInflater; |
||||||
|
|
||||||
|
public BaseListAdapter(@NonNull Context context) { |
||||||
|
this(context, null); |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("all") |
||||||
|
public BaseListAdapter(Context context, List<T> data) { |
||||||
|
this.mContext = context; |
||||||
|
mLayoutInflater = LayoutInflater.from(context); |
||||||
|
mDataManager = new ListDataManagerImpl<>(data, this); |
||||||
|
} |
||||||
|
|
||||||
|
public Context getContext() { |
||||||
|
return mContext; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getCount() { |
||||||
|
return getDataSize(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public long getItemId(int position) { |
||||||
|
return position; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
public View getView(int position, View convertView, ViewGroup parent) { |
||||||
|
VH viewHolder; |
||||||
|
int type = getItemViewType(position); |
||||||
|
if (convertView == null) { |
||||||
|
viewHolder = onCreateViewHolder(mLayoutInflater, parent, type); |
||||||
|
viewHolder.mItemView.setTag(ITEM_ID, viewHolder); |
||||||
|
} else { |
||||||
|
viewHolder = (VH) convertView.getTag(ITEM_ID); |
||||||
|
} |
||||||
|
viewHolder.setPosition(position); |
||||||
|
viewHolder.setType(type); |
||||||
|
T item = getItem(position); |
||||||
|
onBindData(viewHolder, item); |
||||||
|
return viewHolder.mItemView; |
||||||
|
} |
||||||
|
|
||||||
|
protected abstract void onBindData(@NonNull VH viewHolder, T item); |
||||||
|
|
||||||
|
@NonNull |
||||||
|
protected abstract VH onCreateViewHolder(@NonNull LayoutInflater layoutInflater, @NonNull ViewGroup parent, int type); |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getItemViewType(int position) { |
||||||
|
return super.getItemViewType(position); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getViewTypeCount() { |
||||||
|
return super.getViewTypeCount(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void add(T elem) { |
||||||
|
mDataManager.add(elem); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addAt(int location, T elem) { |
||||||
|
mDataManager.addAt(location, elem); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addItems(List<T> elements) { |
||||||
|
mDataManager.addItems(elements); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addItemsChecked(List<T> elements) { |
||||||
|
mDataManager.addItemsChecked(elements); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addItemsAt(int location, List<T> elements) { |
||||||
|
mDataManager.addItemsAt(location, elements); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void replace(T oldElem, T newElem) { |
||||||
|
mDataManager.replace(oldElem, newElem); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void replaceAt(int index, T elem) { |
||||||
|
mDataManager.replaceAt(index, elem); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void replaceAll(List<T> elements) { |
||||||
|
mDataManager.replaceAll(elements); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void remove(T elem) { |
||||||
|
mDataManager.remove(elem); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeItems(List<T> elements) { |
||||||
|
mDataManager.removeItems(elements); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeItems(List<T> elements, boolean isSuccessive) { |
||||||
|
mDataManager.removeItems(elements, isSuccessive); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeAt(int index) { |
||||||
|
mDataManager.removeAt(index); |
||||||
|
} |
||||||
|
|
||||||
|
@Nullable |
||||||
|
@Override |
||||||
|
public T getItem(int position) { |
||||||
|
return mDataManager.getItem(position); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public final int getDataSize() { |
||||||
|
return mDataManager.getDataSize(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean contains(T elem) { |
||||||
|
return mDataManager.contains(elem); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setDataSource(List<T> elements, boolean notifyDataSetChanged) { |
||||||
|
mDataManager.setDataSource(elements, notifyDataSetChanged); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int indexItem(T t) { |
||||||
|
return mDataManager.indexItem(t); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void clear() { |
||||||
|
mDataManager.clear(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<T> getItems() { |
||||||
|
return mDataManager.getItems(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,191 @@ |
|||||||
|
package com.android.base.adapter.list; |
||||||
|
|
||||||
|
import android.widget.BaseAdapter; |
||||||
|
|
||||||
|
import com.android.base.adapter.DataManager; |
||||||
|
import com.android.base.utils.common.Checker; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import androidx.annotation.Nullable; |
||||||
|
|
||||||
|
|
||||||
|
final class ListDataManagerImpl<T> implements DataManager<T> { |
||||||
|
|
||||||
|
private List<T> mData; |
||||||
|
private BaseAdapter mBaseAdapter; |
||||||
|
|
||||||
|
ListDataManagerImpl(List<T> tList, BaseAdapter adapter) { |
||||||
|
mData = tList; |
||||||
|
mBaseAdapter = adapter; |
||||||
|
} |
||||||
|
|
||||||
|
private void checkData() { |
||||||
|
if (mData == null) { |
||||||
|
mData = new ArrayList<>(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void add(T elem) { |
||||||
|
checkData(); |
||||||
|
mData.add(elem); |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addAt(int location, T elem) { |
||||||
|
checkData(); |
||||||
|
mData.add(location, elem); |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addItems(List<T> elements) { |
||||||
|
checkData(); |
||||||
|
mData.addAll(elements); |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addItemsChecked(List<T> elements) { |
||||||
|
if (Checker.isEmpty(elements)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (mData == null) { |
||||||
|
addItems(elements); |
||||||
|
return; |
||||||
|
} |
||||||
|
for (T element : elements) { |
||||||
|
if (element == null) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
mData.remove(element); |
||||||
|
} |
||||||
|
mData.addAll(elements); |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addItemsAt(int location, List<T> elements) { |
||||||
|
checkData(); |
||||||
|
mData.addAll(location, elements); |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void replace(T oldElem, T newElem) { |
||||||
|
if (mData != null && mData.contains(oldElem)) { |
||||||
|
replaceAt(mData.indexOf(oldElem), newElem); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void replaceAt(int index, T elem) { |
||||||
|
if (mData != null && mData.size() > index) { |
||||||
|
mData.set(index, elem); |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void replaceAll(List<T> elements) { |
||||||
|
if (mData == null) { |
||||||
|
mData = new ArrayList<>(); |
||||||
|
} |
||||||
|
mData.clear(); |
||||||
|
if (elements != null) { |
||||||
|
mData.addAll(elements); |
||||||
|
} |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setDataSource(List<T> newDataSource, boolean notifyDataSetChanged) { |
||||||
|
mData = newDataSource; |
||||||
|
if (notifyDataSetChanged) { |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void remove(T elem) { |
||||||
|
if (mData != null && mData.contains(elem)) { |
||||||
|
mData.remove(elem); |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeItems(List<T> elements) { |
||||||
|
if (mData != null && mData.containsAll(elements)) { |
||||||
|
mData.removeAll(elements); |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeItems(List<T> elements, boolean isSuccessive) { |
||||||
|
removeItems(elements); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeAt(int index) { |
||||||
|
if (mData != null && mData.size() > index) { |
||||||
|
mData.remove(index); |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
@Nullable |
||||||
|
public T getItem(int position) { |
||||||
|
if (mData != null && mData.size() > position) { |
||||||
|
return mData.get(position); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public final int getDataSize() { |
||||||
|
return mData == null ? 0 : mData.size(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean contains(T elem) { |
||||||
|
return mData != null && mData.contains(elem); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isEmpty() { |
||||||
|
return mData == null || mData.size() == 0; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void clear() { |
||||||
|
if (mData != null && !mData.isEmpty()) { |
||||||
|
mData.clear(); |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int indexItem(T t) { |
||||||
|
List<T> items = getItems(); |
||||||
|
if (items == null) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
return items.indexOf(t); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<T> getItems() { |
||||||
|
return mData; |
||||||
|
} |
||||||
|
|
||||||
|
private void notifyDataSetChanged() { |
||||||
|
mBaseAdapter.notifyDataSetChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
package com.android.base.adapter.list; |
||||||
|
|
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
import com.android.base.adapter.ItemHelper; |
||||||
|
|
||||||
|
@SuppressWarnings("unused") |
||||||
|
public class SmartViewHolder extends ViewHolder { |
||||||
|
|
||||||
|
private final ItemHelper mHelper; |
||||||
|
|
||||||
|
public SmartViewHolder(View itemView) { |
||||||
|
super(itemView); |
||||||
|
mHelper = new ItemHelper(itemView); |
||||||
|
} |
||||||
|
|
||||||
|
public ItemHelper helper() { |
||||||
|
return mHelper; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
package com.android.base.adapter.list; |
||||||
|
|
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
public class ViewHolder { |
||||||
|
|
||||||
|
protected final View mItemView; |
||||||
|
private int mPosition; |
||||||
|
private int mType; |
||||||
|
|
||||||
|
public ViewHolder(View itemView) { |
||||||
|
mItemView = itemView; |
||||||
|
} |
||||||
|
|
||||||
|
public int getPosition() { |
||||||
|
return mPosition; |
||||||
|
} |
||||||
|
|
||||||
|
void setPosition(int position) { |
||||||
|
mPosition = position; |
||||||
|
} |
||||||
|
|
||||||
|
public int getType() { |
||||||
|
return mType; |
||||||
|
} |
||||||
|
|
||||||
|
void setType(int type) { |
||||||
|
mType = type; |
||||||
|
} |
||||||
|
|
||||||
|
public View getItemView() { |
||||||
|
return mItemView; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,154 @@ |
|||||||
|
package com.android.base.adapter.pager; |
||||||
|
|
||||||
|
import android.os.Build; |
||||||
|
import android.util.SparseArray; |
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
/** |
||||||
|
* The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of |
||||||
|
* storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the |
||||||
|
* start of a layout. By construction, they are displaying current information. At the end of |
||||||
|
* layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that |
||||||
|
* could potentially be used by the adapter to avoid allocating views unnecessarily. |
||||||
|
* <p> |
||||||
|
* This class was taken from Android's implementation of {@link android.widget.AbsListView} which |
||||||
|
* is copyrighted 2006 The Android Open Source Project. |
||||||
|
*/ |
||||||
|
public class RecycleBin { |
||||||
|
|
||||||
|
/** |
||||||
|
* Views that were on screen at the start of layout. This array is populated at the start of |
||||||
|
* layout, and at the end of layout all view in activeViews are moved to scrapViews. |
||||||
|
* Views in activeViews represent a contiguous range of Views, with position of the first |
||||||
|
* view store in mFirstActivePosition. |
||||||
|
*/ |
||||||
|
private View[] activeViews = new View[0]; |
||||||
|
private int[] activeViewTypes = new int[0]; |
||||||
|
|
||||||
|
/** Unsorted views that can be used by the adapter as a convert view. */ |
||||||
|
private SparseArray<View>[] scrapViews; |
||||||
|
|
||||||
|
private int viewTypeCount; |
||||||
|
|
||||||
|
private SparseArray<View> currentScrapViews; |
||||||
|
|
||||||
|
public void setViewTypeCount(int viewTypeCount) { |
||||||
|
if (viewTypeCount < 1) { |
||||||
|
throw new IllegalArgumentException("Can't have a viewTypeCount < 1"); |
||||||
|
} |
||||||
|
//noinspection unchecked
|
||||||
|
SparseArray<View>[] scrapViews = new SparseArray[viewTypeCount]; |
||||||
|
for (int i = 0; i < viewTypeCount; i++) { |
||||||
|
scrapViews[i] = new SparseArray<View>(); |
||||||
|
} |
||||||
|
this.viewTypeCount = viewTypeCount; |
||||||
|
currentScrapViews = scrapViews[0]; |
||||||
|
this.scrapViews = scrapViews; |
||||||
|
} |
||||||
|
|
||||||
|
protected boolean shouldRecycleViewType(int viewType) { |
||||||
|
return viewType >= 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** @return A view from the ScrapViews collection. These are unordered. */ |
||||||
|
View getScrapView(int position, int viewType) { |
||||||
|
if (viewTypeCount == 1) { |
||||||
|
return retrieveFromScrap(currentScrapViews, position); |
||||||
|
} else if (viewType >= 0 && viewType < scrapViews.length) { |
||||||
|
return retrieveFromScrap(scrapViews[viewType], position); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Put a view into the ScrapViews list. These views are unordered. |
||||||
|
* |
||||||
|
* @param scrap The view to add |
||||||
|
*/ |
||||||
|
void addScrapView(View scrap, int position, int viewType) { |
||||||
|
if (viewTypeCount == 1) { |
||||||
|
currentScrapViews.put(position, scrap); |
||||||
|
} else { |
||||||
|
scrapViews[viewType].put(position, scrap); |
||||||
|
} |
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { |
||||||
|
scrap.setAccessibilityDelegate(null); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** Move all views remaining in activeViews to scrapViews. */ |
||||||
|
void scrapActiveViews() { |
||||||
|
final View[] activeViews = this.activeViews; |
||||||
|
final int[] activeViewTypes = this.activeViewTypes; |
||||||
|
final boolean multipleScraps = viewTypeCount > 1; |
||||||
|
|
||||||
|
SparseArray<View> scrapViews = currentScrapViews; |
||||||
|
final int count = activeViews.length; |
||||||
|
for (int i = count - 1; i >= 0; i--) { |
||||||
|
final View victim = activeViews[i]; |
||||||
|
if (victim != null) { |
||||||
|
int whichScrap = activeViewTypes[i]; |
||||||
|
|
||||||
|
activeViews[i] = null; |
||||||
|
activeViewTypes[i] = -1; |
||||||
|
|
||||||
|
if (!shouldRecycleViewType(whichScrap)) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (multipleScraps) { |
||||||
|
scrapViews = this.scrapViews[whichScrap]; |
||||||
|
} |
||||||
|
scrapViews.put(i, victim); |
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { |
||||||
|
victim.setAccessibilityDelegate(null); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pruneScrapViews(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Makes sure that the size of scrapViews does not exceed the size of activeViews. |
||||||
|
* (This can happen if an adapter does not recycle its views). |
||||||
|
*/ |
||||||
|
private void pruneScrapViews() { |
||||||
|
final int maxViews = activeViews.length; |
||||||
|
final int viewTypeCount = this.viewTypeCount; |
||||||
|
final SparseArray<View>[] scrapViews = this.scrapViews; |
||||||
|
for (int i = 0; i < viewTypeCount; ++i) { |
||||||
|
final SparseArray<View> scrapPile = scrapViews[i]; |
||||||
|
int size = scrapPile.size(); |
||||||
|
final int extras = size - maxViews; |
||||||
|
size--; |
||||||
|
for (int j = 0; j < extras; j++) { |
||||||
|
scrapPile.remove(scrapPile.keyAt(size--)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static View retrieveFromScrap(SparseArray<View> scrapViews, int position) { |
||||||
|
int size = scrapViews.size(); |
||||||
|
if (size > 0) { |
||||||
|
// See if we still have a view for this position.
|
||||||
|
for (int i = 0; i < size; i++) { |
||||||
|
int fromPosition = scrapViews.keyAt(i); |
||||||
|
View view = scrapViews.get(fromPosition); |
||||||
|
if (fromPosition == position) { |
||||||
|
scrapViews.remove(fromPosition); |
||||||
|
return view; |
||||||
|
} |
||||||
|
} |
||||||
|
int index = size - 1; |
||||||
|
View r = scrapViews.valueAt(index); |
||||||
|
scrapViews.remove(scrapViews.keyAt(index)); |
||||||
|
return r; |
||||||
|
} else { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,118 @@ |
|||||||
|
package com.android.base.adapter.pager; |
||||||
|
|
||||||
|
import android.view.View; |
||||||
|
import android.view.ViewGroup; |
||||||
|
import android.widget.AdapterView; |
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import androidx.viewpager.widget.PagerAdapter; |
||||||
|
|
||||||
|
/** |
||||||
|
* A {@link PagerAdapter} which behaves like an {@link android.widget.Adapter} with view types and view recycling. |
||||||
|
* |
||||||
|
* @see <a href='https://github.com/JakeWharton/salvage'>JakeWharton/salvage</a>
|
||||||
|
*/ |
||||||
|
public abstract class RecyclingPagerAdapter extends PagerAdapter { |
||||||
|
|
||||||
|
static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE; |
||||||
|
|
||||||
|
private final RecycleBin recycleBin; |
||||||
|
|
||||||
|
public RecyclingPagerAdapter() { |
||||||
|
this(new RecycleBin()); |
||||||
|
} |
||||||
|
|
||||||
|
RecyclingPagerAdapter(RecycleBin recycleBin) { |
||||||
|
this.recycleBin = recycleBin; |
||||||
|
recycleBin.setViewTypeCount(getViewTypeCount()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void notifyDataSetChanged() { |
||||||
|
recycleBin.scrapActiveViews(); |
||||||
|
super.notifyDataSetChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public final Object instantiateItem(@NotNull ViewGroup container, int position) { |
||||||
|
int viewType = getItemViewType(position); |
||||||
|
View view = null; |
||||||
|
if (viewType != IGNORE_ITEM_VIEW_TYPE) { |
||||||
|
view = recycleBin.getScrapView(position, viewType); |
||||||
|
} |
||||||
|
view = getView(position, view, container); |
||||||
|
container.addView(view); |
||||||
|
return view; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public final void destroyItem(ViewGroup container, int position, @NotNull Object object) { |
||||||
|
View view = (View) object; |
||||||
|
container.removeView(view); |
||||||
|
int viewType = getItemViewType(position); |
||||||
|
if (viewType != IGNORE_ITEM_VIEW_TYPE) { |
||||||
|
recycleBin.addScrapView(view, position, viewType); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public final boolean isViewFromObject(@NotNull View view, @NotNull Object object) { |
||||||
|
return view == object; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* Returns the number of types of Views that will be created by |
||||||
|
* {@link #getView}. Each type represents a set of views that can be |
||||||
|
* converted in {@link #getView}. If the adapter always returns the same |
||||||
|
* type of View for all items, this method should return 1. |
||||||
|
* </p> |
||||||
|
* <p> |
||||||
|
* This method will only be called when when the adapter is set on the |
||||||
|
* the {@link AdapterView}. |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @return The number of types of Views that will be created by this adapter |
||||||
|
*/ |
||||||
|
public int getViewTypeCount() { |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the type of View that will be created by {@link #getView} for the specified item. |
||||||
|
* |
||||||
|
* @param position The position of the item within the adapter's data set whose view type we |
||||||
|
* want. |
||||||
|
* @return An integer representing the type of View. Two views should share the same type if one |
||||||
|
* can be converted to the other in {@link #getView}. Note: Integers must be in the |
||||||
|
* range 0 to {@link #getViewTypeCount} - 1. {@link #IGNORE_ITEM_VIEW_TYPE} can |
||||||
|
* also be returned. |
||||||
|
* @see #IGNORE_ITEM_VIEW_TYPE |
||||||
|
*/ |
||||||
|
@SuppressWarnings("UnusedParameters") // Argument potentially used by subclasses.
|
||||||
|
public int getItemViewType(int position) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get a View that displays the data at the specified position in the data set. You can either |
||||||
|
* create a View manually or inflate it from an XML layout file. When the View is inflated, the |
||||||
|
* parent View (GridView, ListView...) will apply default layout parameters unless you use |
||||||
|
* {@link android.view.LayoutInflater#inflate(int, ViewGroup, boolean)} |
||||||
|
* to specify a root view and to prevent attachment to the root. |
||||||
|
* |
||||||
|
* @param position The position of the item within the adapter's data set of the item whose view |
||||||
|
* we want. |
||||||
|
* @param convertView The old view to reuse, if possible. Note: You should check that this view |
||||||
|
* is non-null and of an appropriate type before using. If it is not possible to convert |
||||||
|
* this view to display the correct data, this method can create a new view. |
||||||
|
* Heterogeneous lists can specify their number of view types, so that this View is |
||||||
|
* always of the right type (see {@link #getViewTypeCount()} and |
||||||
|
* {@link #getItemViewType(int)}). |
||||||
|
* @param container The parent that this view will eventually be attached to |
||||||
|
* @return A View corresponding to the data at the specified position. |
||||||
|
*/ |
||||||
|
public abstract View getView(int position, View convertView, ViewGroup container); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
package com.android.base.adapter.pager |
||||||
|
|
||||||
|
import android.view.LayoutInflater |
||||||
|
import android.view.View |
||||||
|
import android.view.ViewGroup |
||||||
|
import kotlinx.android.extensions.LayoutContainer |
||||||
|
|
||||||
|
/** |
||||||
|
* 如果使用可缩放的 View 作为 pager,可能不适合使用此Adapter |
||||||
|
* |
||||||
|
* @param <T> 数据 |
||||||
|
* @param <VH> View Holder类型 |
||||||
|
</VH></T> */ |
||||||
|
abstract class SimpleViewPagerAdapter<T>(data: List<T>) : ViewPagerAdapter<T, KtViewHolder>(data) { |
||||||
|
|
||||||
|
override fun createViewHolder(container: ViewGroup): KtViewHolder { |
||||||
|
val layout = provideLayout(container) |
||||||
|
val itemView = if (layout is Int) { |
||||||
|
LayoutInflater.from(container.context).inflate(layout, container, false) |
||||||
|
} else { |
||||||
|
layout as View |
||||||
|
} |
||||||
|
return KtViewHolder(itemView) |
||||||
|
} |
||||||
|
|
||||||
|
/**provide a layout id or a View*/ |
||||||
|
abstract fun provideLayout(parent: ViewGroup): Any |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
class KtViewHolder(itemView: View) : ViewPagerAdapter.ViewHolder(itemView), LayoutContainer { |
||||||
|
override val containerView: View = itemView |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
package com.android.base.adapter.pager; |
||||||
|
|
||||||
|
import android.view.View; |
||||||
|
import android.view.ViewGroup; |
||||||
|
|
||||||
|
import com.android.base.R; |
||||||
|
import com.android.base.utils.common.Checker; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
import androidx.annotation.Nullable; |
||||||
|
|
||||||
|
/** |
||||||
|
* 如果使用可缩放的 View 作为 pager,可能不适合使用此 Adapter |
||||||
|
* |
||||||
|
* @param <T> 数据 |
||||||
|
* @param <VH> View Holder类型 |
||||||
|
*/ |
||||||
|
public abstract class ViewPagerAdapter<T, VH extends ViewPagerAdapter.ViewHolder> extends RecyclingPagerAdapter { |
||||||
|
|
||||||
|
private List<T> mData; |
||||||
|
|
||||||
|
public ViewPagerAdapter(@Nullable List<T> data) { |
||||||
|
mData = data; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
public View getView(int position, View convertView, ViewGroup container) { |
||||||
|
VH viewHolder; |
||||||
|
if (convertView == null) { |
||||||
|
viewHolder = createViewHolder(container); |
||||||
|
} else { |
||||||
|
viewHolder = (VH) convertView.getTag(R.id.base_item_tag_view_id); |
||||||
|
} |
||||||
|
T item = getItem(position); |
||||||
|
onBindData(viewHolder, item); |
||||||
|
return viewHolder.itemView; |
||||||
|
} |
||||||
|
|
||||||
|
protected abstract VH createViewHolder(@NonNull ViewGroup container); |
||||||
|
|
||||||
|
protected abstract void onBindData(@NonNull VH viewHolder, @NonNull T item); |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getCount() { |
||||||
|
return Checker.isEmpty(mData) ? 0 : mData.size(); |
||||||
|
} |
||||||
|
|
||||||
|
public T getItem(int position) { |
||||||
|
if (position < 0 || position >= mData.size()) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
return mData.get(position); |
||||||
|
} |
||||||
|
|
||||||
|
public static class ViewHolder { |
||||||
|
|
||||||
|
public View itemView; |
||||||
|
|
||||||
|
public ViewHolder(@NonNull View itemView) { |
||||||
|
this.itemView = itemView; |
||||||
|
itemView.setTag(R.id.base_item_tag_view_id, this); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
package com.android.base.adapter.pager; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment; |
||||||
|
import androidx.fragment.app.FragmentManager; |
||||||
|
import androidx.fragment.app.FragmentPagerAdapter; |
||||||
|
|
||||||
|
@SuppressWarnings("unused") |
||||||
|
public class ViewPagerFragmentAdapter extends FragmentPagerAdapter { |
||||||
|
|
||||||
|
private final List<ViewPagerInfo> mTabs; |
||||||
|
private Context mContext; |
||||||
|
|
||||||
|
public ViewPagerFragmentAdapter(FragmentManager fragmentManager, Context context) { |
||||||
|
super(fragmentManager); |
||||||
|
mContext = context; |
||||||
|
mTabs = new ArrayList<>(); |
||||||
|
} |
||||||
|
|
||||||
|
public void setDataSource(List<ViewPagerInfo> viewPagerInfoList) { |
||||||
|
mTabs.clear(); |
||||||
|
mTabs.addAll(viewPagerInfoList); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getCount() { |
||||||
|
return mTabs.size(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Fragment getItem(int position) { |
||||||
|
ViewPagerInfo viewPagerInfo = mTabs.get(position); |
||||||
|
return Fragment.instantiate(mContext, viewPagerInfo.clazz.getName(), viewPagerInfo.args); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public CharSequence getPageTitle(int position) { |
||||||
|
return mTabs.get(position).title; |
||||||
|
} |
||||||
|
|
||||||
|
public List<ViewPagerInfo> getTabs() { |
||||||
|
return mTabs; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
package com.android.base.adapter.pager; |
||||||
|
|
||||||
|
import android.os.Bundle; |
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment; |
||||||
|
|
||||||
|
@SuppressWarnings("all") |
||||||
|
public class ViewPagerInfo { |
||||||
|
|
||||||
|
public final Class<? extends Fragment> clazz; |
||||||
|
public final Bundle args; |
||||||
|
public final String title; |
||||||
|
|
||||||
|
public ViewPagerInfo(String title, Class<? extends Fragment> clazz, Bundle args) { |
||||||
|
this.title = title; |
||||||
|
this.clazz = clazz; |
||||||
|
this.args = args; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
package com.android.base.adapter.pager; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment; |
||||||
|
import androidx.fragment.app.FragmentManager; |
||||||
|
import androidx.fragment.app.FragmentStatePagerAdapter; |
||||||
|
|
||||||
|
public class ViewPagerStateFragmentAdapter extends FragmentStatePagerAdapter { |
||||||
|
|
||||||
|
private final List<ViewPagerInfo> mTabs; |
||||||
|
private Context mContext; |
||||||
|
|
||||||
|
public ViewPagerStateFragmentAdapter(FragmentManager fragmentManager, Context context) { |
||||||
|
super(fragmentManager); |
||||||
|
mContext = context; |
||||||
|
mTabs = new ArrayList<>(); |
||||||
|
} |
||||||
|
|
||||||
|
public void setDataSource(List<ViewPagerInfo> viewPagerInfoList) { |
||||||
|
mTabs.clear(); |
||||||
|
mTabs.addAll(viewPagerInfoList); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getCount() { |
||||||
|
return mTabs.size(); |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
@Override |
||||||
|
public Fragment getItem(int position) { |
||||||
|
ViewPagerInfo viewPagerInfo = mTabs.get(position); |
||||||
|
return Fragment.instantiate(mContext, viewPagerInfo.clazz.getName(), viewPagerInfo.args); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public CharSequence getPageTitle(int position) { |
||||||
|
return mTabs.get(position).title; |
||||||
|
} |
||||||
|
|
||||||
|
protected List<ViewPagerInfo> getTabs() { |
||||||
|
return mTabs; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
package com.android.base.adapter.recycler; |
||||||
|
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable; |
||||||
|
import androidx.recyclerview.widget.RecyclerView; |
||||||
|
|
||||||
|
/** |
||||||
|
* @see <a href='http://www.cezcb.com/2018/08/24/PagingWithHeader/'>PagingWithHeader</a>
|
||||||
|
*/ |
||||||
|
class AdapterDataObserverProxy extends RecyclerView.AdapterDataObserver { |
||||||
|
|
||||||
|
private RecyclerView.AdapterDataObserver adapterDataObserver; |
||||||
|
private int headerCount; |
||||||
|
|
||||||
|
AdapterDataObserverProxy(RecyclerView.AdapterDataObserver adapterDataObserver, int headerCount) { |
||||||
|
this.adapterDataObserver = adapterDataObserver; |
||||||
|
this.headerCount = headerCount; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onChanged() { |
||||||
|
adapterDataObserver.onChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onItemRangeChanged(int positionStart, int itemCount) { |
||||||
|
adapterDataObserver.onItemRangeChanged(positionStart + headerCount, itemCount); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) { |
||||||
|
adapterDataObserver.onItemRangeChanged(positionStart + headerCount, itemCount, payload); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onItemRangeInserted(int positionStart, int itemCount) { |
||||||
|
adapterDataObserver.onItemRangeInserted(positionStart + headerCount, itemCount); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onItemRangeRemoved(int positionStart, int itemCount) { |
||||||
|
adapterDataObserver.onItemRangeRemoved(positionStart + headerCount, itemCount); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { |
||||||
|
super.onItemRangeMoved(fromPosition + headerCount, toPosition + headerCount, itemCount); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,266 @@ |
|||||||
|
package com.android.base.adapter.recycler; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.view.ViewGroup; |
||||||
|
|
||||||
|
import com.android.base.adapter.DataManager; |
||||||
|
import com.android.base.utils.common.Checker; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.concurrent.Executor; |
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
import androidx.annotation.Nullable; |
||||||
|
import androidx.recyclerview.widget.AdapterListUpdateCallback; |
||||||
|
import androidx.recyclerview.widget.AsyncDifferConfig; |
||||||
|
import androidx.recyclerview.widget.AsyncListDiffer; |
||||||
|
import androidx.recyclerview.widget.DiffUtil; |
||||||
|
import androidx.recyclerview.widget.RecyclerView; |
||||||
|
|
||||||
|
/** |
||||||
|
* RecyclerView 的适配器,注意: 只有{@link #setDataSource(List, boolean)}才能替换原有数据源的引用。 |
||||||
|
* |
||||||
|
* @param <T> 当前列表使用的数据类型 |
||||||
|
* @author Ztiany |
||||||
|
* date : 2015-05-11 22:38 |
||||||
|
* email: 1169654504@qq.com |
||||||
|
*/ |
||||||
|
@SuppressWarnings("unused") |
||||||
|
public abstract class DiffRecyclerAdapter<T, VH extends ViewHolder> extends RecyclerView.Adapter<VH> implements DataManager<T> { |
||||||
|
|
||||||
|
@NonNull |
||||||
|
private Context mContext; |
||||||
|
|
||||||
|
private AsyncListDiffer<T> mAsyncListDiffer; |
||||||
|
|
||||||
|
private final int mHeaderCount; |
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
public DiffRecyclerAdapter(@NonNull Context context, @NonNull DiffUtil.ItemCallback<T> itemCallback, @Nullable Executor executor, int headerCount) { |
||||||
|
mContext = context; |
||||||
|
mHeaderCount = headerCount; |
||||||
|
|
||||||
|
AsyncDifferConfig.Builder<T> tBuilder = new AsyncDifferConfig.Builder<>(itemCallback); |
||||||
|
if (executor != null) { |
||||||
|
tBuilder.setBackgroundThreadExecutor(executor); |
||||||
|
} |
||||||
|
AsyncDifferConfig<T> differConfig = tBuilder.build(); |
||||||
|
|
||||||
|
mAsyncListDiffer = new AsyncListDiffer<>(new AdapterListUpdateCallback(this), differConfig); |
||||||
|
} |
||||||
|
|
||||||
|
public DiffRecyclerAdapter(@NonNull Context context, @NonNull DiffUtil.ItemCallback<T> itemCallback, int headerCount) { |
||||||
|
this(context, itemCallback, null, headerCount); |
||||||
|
} |
||||||
|
|
||||||
|
public DiffRecyclerAdapter(@NonNull Context context, @NonNull DiffUtil.ItemCallback<T> itemCallback) { |
||||||
|
this(context, itemCallback, null, 0); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void registerAdapterDataObserver(@NonNull RecyclerView.AdapterDataObserver observer) { |
||||||
|
if (mHeaderCount != 0) { |
||||||
|
super.registerAdapterDataObserver(new AdapterDataObserverProxy(observer, mHeaderCount)); |
||||||
|
} else { |
||||||
|
super.registerAdapterDataObserver(observer); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@NonNull |
||||||
|
public Context getContext() { |
||||||
|
return mContext; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getItemCount() { |
||||||
|
return getDataSize(); |
||||||
|
} |
||||||
|
|
||||||
|
@NonNull |
||||||
|
@Override |
||||||
|
public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType); |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onBindViewHolder(@NonNull VH holder, int position, @NonNull List<Object> payloads) { |
||||||
|
super.onBindViewHolder(holder, position, payloads); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public abstract void onBindViewHolder(@NonNull VH viewHolder, int position); |
||||||
|
|
||||||
|
public void notifyEntryChanged(T t) { |
||||||
|
int itemPosition = indexItem(t); |
||||||
|
if (itemPosition != -1) { |
||||||
|
notifyItemChanged(itemPosition); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void add(T element) { |
||||||
|
List<T> newList = newList(); |
||||||
|
newList.add(element); |
||||||
|
mAsyncListDiffer.submitList(newList); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addAt(int location, T element) { |
||||||
|
if (location > getDataSize()) { |
||||||
|
location = getDataSize(); |
||||||
|
} |
||||||
|
List<T> newList = newList(); |
||||||
|
newList.add(location, element); |
||||||
|
mAsyncListDiffer.submitList(newList); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addItems(List<T> elements) { |
||||||
|
if (Checker.isEmpty(elements)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
List<T> newList = newList(); |
||||||
|
newList.addAll(elements); |
||||||
|
mAsyncListDiffer.submitList(newList); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addItemsChecked(List<T> elements) { |
||||||
|
if (Checker.isEmpty(elements)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
List<T> newList = newList(); |
||||||
|
|
||||||
|
for (T element : elements) { |
||||||
|
if (element == null) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
newList.remove(element); |
||||||
|
} |
||||||
|
|
||||||
|
newList.addAll(elements); |
||||||
|
mAsyncListDiffer.submitList(newList); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addItemsAt(int location, List<T> elements) { |
||||||
|
if (Checker.isEmpty(elements)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
List<T> newList = newList(); |
||||||
|
|
||||||
|
if (location > newList.size()) { |
||||||
|
location = newList.size(); |
||||||
|
} |
||||||
|
|
||||||
|
newList.addAll(location, elements); |
||||||
|
mAsyncListDiffer.submitList(newList); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void replace(T oldElement, T newElement) { |
||||||
|
if (!contains(oldElement)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
List<T> newList = newList(); |
||||||
|
newList.set(newList.indexOf(oldElement), newElement); |
||||||
|
mAsyncListDiffer.submitList(newList); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void replaceAt(int index, T element) { |
||||||
|
if (getDataSize() > index) { |
||||||
|
List<T> newList = newList(); |
||||||
|
newList.set(index, element); |
||||||
|
mAsyncListDiffer.submitList(newList); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void replaceAll(List<T> elements) { |
||||||
|
List<T> newList = new ArrayList<>(elements); |
||||||
|
mAsyncListDiffer.submitList(newList); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void remove(T element) { |
||||||
|
if (contains(element)) { |
||||||
|
List<T> newList = newList(); |
||||||
|
newList.remove(element); |
||||||
|
mAsyncListDiffer.submitList(newList); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeItems(List<T> elements) { |
||||||
|
if (Checker.isEmpty(elements) || isEmpty() || !getItems().containsAll(elements)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
List<T> newList = newList(); |
||||||
|
newList.removeAll(elements); |
||||||
|
mAsyncListDiffer.submitList(newList); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeItems(List<T> elements, boolean isSuccessive) { |
||||||
|
removeItems(elements); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeAt(int index) { |
||||||
|
if (getDataSize() > index) { |
||||||
|
List<T> newList = newList(); |
||||||
|
newList.remove(index); |
||||||
|
mAsyncListDiffer.submitList(newList); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public T getItem(int position) { |
||||||
|
return getDataSize() > position ? getItems().get(position) : null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public final int getDataSize() { |
||||||
|
return getItems() == null ? 0 : getItems().size(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean contains(T element) { |
||||||
|
return !isEmpty() && getItems().contains(element); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void clear() { |
||||||
|
mAsyncListDiffer.submitList(null); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setDataSource(List<T> elements, boolean notifyDataSetChanged) { |
||||||
|
mAsyncListDiffer.submitList(elements); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<T> getItems() { |
||||||
|
return mAsyncListDiffer.getCurrentList(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isEmpty() { |
||||||
|
return getDataSize() == 0; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int indexItem(T t) { |
||||||
|
return isEmpty() ? -1 : getItems().indexOf(t); |
||||||
|
} |
||||||
|
|
||||||
|
private List<T> newList() { |
||||||
|
if (getItems() == null) { |
||||||
|
return new ArrayList<>(); |
||||||
|
} else { |
||||||
|
return new ArrayList<>(getItems()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
package com.android.base.adapter.recycler; |
||||||
|
|
||||||
|
public interface HeaderSize { |
||||||
|
int getHeaderSize(); |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
package com.android.base.adapter.recycler |
||||||
|
|
||||||
|
import android.view.LayoutInflater |
||||||
|
import android.view.View |
||||||
|
import android.view.ViewGroup |
||||||
|
import androidx.recyclerview.widget.RecyclerView |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Ztiany |
||||||
|
* Email: 1169654504@qq.com |
||||||
|
* Date : 2017-09-13 15:33 |
||||||
|
*/ |
||||||
|
abstract class ItemViewBinder<T, VH : RecyclerView.ViewHolder> : com.drakeet.multitype.ItemViewBinder<T, VH>() { |
||||||
|
|
||||||
|
protected val dataManager: MultiTypeAdapter |
||||||
|
get() = adapter as MultiTypeAdapter |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
abstract class SimpleItemViewBinder<T> : ItemViewBinder<T, KtViewHolder>() { |
||||||
|
|
||||||
|
override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): KtViewHolder { |
||||||
|
val layout = provideLayout(inflater, parent) |
||||||
|
val itemView = if (layout is Int) { |
||||||
|
inflater.inflate(layout, parent, false) |
||||||
|
} else |
||||||
|
layout as View |
||||||
|
return KtViewHolder(itemView).apply { |
||||||
|
onViewHolderCreated(this) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected open fun onViewHolderCreated(viewHolder: KtViewHolder) = Unit |
||||||
|
|
||||||
|
/**provide a layout id or a View*/ |
||||||
|
abstract fun provideLayout(inflater: LayoutInflater, parent: ViewGroup): Any |
||||||
|
|
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
package com.android.base.adapter.recycler |
||||||
|
|
||||||
|
import android.view.View |
||||||
|
import kotlinx.android.extensions.CacheImplementation |
||||||
|
import kotlinx.android.extensions.ContainerOptions |
||||||
|
import kotlinx.android.extensions.LayoutContainer |
||||||
|
|
||||||
|
/** |
||||||
|
*@author Ztiany |
||||||
|
* Email: ztiany3@gmail.com |
||||||
|
* Date : 2018-11-12 17:07 |
||||||
|
*/ |
||||||
|
@ContainerOptions(cache = CacheImplementation.SPARSE_ARRAY) |
||||||
|
open class KtViewHolder(override val containerView: View) : ViewHolder(containerView), LayoutContainer |
@ -0,0 +1,169 @@ |
|||||||
|
package com.android.base.adapter.recycler; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
|
||||||
|
import com.android.base.adapter.DataManager; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
import androidx.annotation.Nullable; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @see <a href='https://github.com/drakeet/MultiType'>drakeet/MultiTypeAdapter</a>
|
||||||
|
*/ |
||||||
|
public class MultiTypeAdapter extends com.drakeet.multitype.MultiTypeAdapter implements DataManager<Object> { |
||||||
|
|
||||||
|
private final Context mContext; |
||||||
|
|
||||||
|
private RecyclerDataManagerImpl<Object> mRecyclerDataManager; |
||||||
|
|
||||||
|
public MultiTypeAdapter(Context context) { |
||||||
|
super(); |
||||||
|
mContext = context; |
||||||
|
ArrayList<Object> objects = new ArrayList<>(); |
||||||
|
mRecyclerDataManager = new RecyclerDataManagerImpl<>(objects, this); |
||||||
|
super.setItems(objects); |
||||||
|
} |
||||||
|
|
||||||
|
public MultiTypeAdapter(Context context, @NonNull List<?> items) { |
||||||
|
super(); |
||||||
|
mContext = context; |
||||||
|
ArrayList<Object> objects = new ArrayList<>(items); |
||||||
|
mRecyclerDataManager = new RecyclerDataManagerImpl<>(objects, this); |
||||||
|
super.setItems(objects); |
||||||
|
} |
||||||
|
|
||||||
|
public MultiTypeAdapter(Context context, @NonNull List<?> items, int initialCapacity) { |
||||||
|
super(items, initialCapacity); |
||||||
|
mContext = context; |
||||||
|
ArrayList<Object> objects = new ArrayList<>(items); |
||||||
|
mRecyclerDataManager = new RecyclerDataManagerImpl<>(objects, this); |
||||||
|
super.setItems(objects); |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("unused") |
||||||
|
public void notifyEntryChanged(Object entry) { |
||||||
|
int itemPosition = indexItem(entry); |
||||||
|
if (itemPosition != -1) { |
||||||
|
notifyItemChanged(itemPosition); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void add(Object elem) { |
||||||
|
mRecyclerDataManager.add(elem); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addAt(int location, Object elem) { |
||||||
|
mRecyclerDataManager.addAt(location, elem); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addItems(List<Object> elements) { |
||||||
|
mRecyclerDataManager.addItems(elements); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addItemsChecked(List<Object> elements) { |
||||||
|
mRecyclerDataManager.addItemsChecked(elements); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addItemsAt(int location, List<Object> elements) { |
||||||
|
mRecyclerDataManager.addItemsAt(location, elements); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void replace(Object oldElem, Object newElem) { |
||||||
|
mRecyclerDataManager.replace(oldElem, newElem); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void replaceAt(int index, Object elem) { |
||||||
|
mRecyclerDataManager.replaceAt(index, elem); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void replaceAll(List<Object> elements) { |
||||||
|
mRecyclerDataManager.replaceAll(elements); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setDataSource(@NonNull List<Object> newDataSource, boolean notifyDataSetChanged) { |
||||||
|
super.setItems(newDataSource); |
||||||
|
mRecyclerDataManager.setDataSource(newDataSource, notifyDataSetChanged); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void remove(Object elem) { |
||||||
|
mRecyclerDataManager.remove(elem); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeAt(int index) { |
||||||
|
mRecyclerDataManager.removeAt(index); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeItems(List<Object> elements) { |
||||||
|
mRecyclerDataManager.removeItems(elements); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeItems(List<Object> elements, boolean isSuccessive) { |
||||||
|
mRecyclerDataManager.removeItems(elements, isSuccessive); |
||||||
|
} |
||||||
|
|
||||||
|
@Nullable |
||||||
|
@Override |
||||||
|
public Object getItem(int position) { |
||||||
|
return mRecyclerDataManager.getItem(position); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getDataSize() { |
||||||
|
return mRecyclerDataManager.getDataSize(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean contains(Object elem) { |
||||||
|
return mRecyclerDataManager.contains(elem); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isEmpty() { |
||||||
|
return mRecyclerDataManager.isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void clear() { |
||||||
|
mRecyclerDataManager.clear(); |
||||||
|
} |
||||||
|
|
||||||
|
@NonNull |
||||||
|
@Override |
||||||
|
public List<Object> getItems() { |
||||||
|
return mRecyclerDataManager.getItems(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int indexItem(Object o) { |
||||||
|
return mRecyclerDataManager.indexItem(o); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setItems(@NonNull List<?> items) { |
||||||
|
ArrayList<Object> objects = new ArrayList<>(items); |
||||||
|
super.setItems(objects); |
||||||
|
mRecyclerDataManager.setDataSource(objects, true); |
||||||
|
} |
||||||
|
|
||||||
|
public Context getContext() { |
||||||
|
return mContext; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,177 @@ |
|||||||
|
package com.android.base.adapter.recycler; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.view.ViewGroup; |
||||||
|
|
||||||
|
import com.android.base.adapter.DataManager; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
import androidx.annotation.Nullable; |
||||||
|
import androidx.recyclerview.widget.RecyclerView; |
||||||
|
|
||||||
|
/** |
||||||
|
* RecyclerView 的适配器 |
||||||
|
* 注意: 只有setDataSource才能替换原有数据源的引用。 |
||||||
|
* |
||||||
|
* @param <T> 当前列表使用的数据类型 |
||||||
|
* @author Ztiany |
||||||
|
* date : 2015-05-11 22:38 |
||||||
|
* email: 1169654504@qq.com |
||||||
|
*/ |
||||||
|
public abstract class RecyclerAdapter<T, VH extends ViewHolder> extends RecyclerView.Adapter<VH> implements DataManager<T> { |
||||||
|
|
||||||
|
private RecyclerDataManagerImpl<T> mDataManager; |
||||||
|
|
||||||
|
@NonNull |
||||||
|
private final Context mContext; |
||||||
|
|
||||||
|
public RecyclerAdapter(@NonNull Context context, List<T> data) { |
||||||
|
mDataManager = new RecyclerDataManagerImpl<>(data, this); |
||||||
|
this.mContext = context; |
||||||
|
} |
||||||
|
|
||||||
|
public RecyclerAdapter(@NonNull Context context) { |
||||||
|
this(context, null); |
||||||
|
} |
||||||
|
|
||||||
|
@NonNull |
||||||
|
public Context getContext() { |
||||||
|
return mContext; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getItemCount() { |
||||||
|
return getDataSize(); |
||||||
|
} |
||||||
|
|
||||||
|
@NonNull |
||||||
|
@Override |
||||||
|
public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType); |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onBindViewHolder(@NonNull VH holder, int position, @NonNull List<Object> payloads) { |
||||||
|
super.onBindViewHolder(holder, position, payloads); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public abstract void onBindViewHolder(@NonNull VH viewHolder, int position); |
||||||
|
|
||||||
|
public void notifyEntryChanged(T t) { |
||||||
|
int itemPosition = indexItem(t); |
||||||
|
if (itemPosition != -1) { |
||||||
|
notifyItemChanged(itemPosition); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// DataManager
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void add(T elem) { |
||||||
|
mDataManager.add(elem); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addAt(int location, T elem) { |
||||||
|
mDataManager.addAt(location, elem); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addItems(List<T> elements) { |
||||||
|
mDataManager.addItems(elements); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addItemsChecked(List<T> elements) { |
||||||
|
mDataManager.addItemsChecked(elements); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addItemsAt(int location, List<T> elements) { |
||||||
|
mDataManager.addItemsAt(location, elements); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void replace(T oldElem, T newElem) { |
||||||
|
mDataManager.replace(oldElem, newElem); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void replaceAt(int index, T elem) { |
||||||
|
mDataManager.replaceAt(index, elem); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void replaceAll(List<T> elements) { |
||||||
|
mDataManager.replaceAll(elements); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void remove(T elem) { |
||||||
|
mDataManager.remove(elem); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeItems(List<T> elements) { |
||||||
|
mDataManager.removeItems(elements); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeItems(List<T> elements, boolean isSuccessive) { |
||||||
|
mDataManager.removeItems(elements, isSuccessive); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeAt(int index) { |
||||||
|
mDataManager.removeAt(index); |
||||||
|
} |
||||||
|
|
||||||
|
@Nullable |
||||||
|
@Override |
||||||
|
public T getItem(int position) { |
||||||
|
return mDataManager.getItem(position); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public final int getDataSize() { |
||||||
|
return mDataManager.getDataSize(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean contains(T elem) { |
||||||
|
return mDataManager.contains(elem); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void clear() { |
||||||
|
mDataManager.clear(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setDataSource(List<T> elements, boolean notifyDataSetChanged) { |
||||||
|
mDataManager.setDataSource(elements, notifyDataSetChanged); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<T> getItems() { |
||||||
|
return mDataManager.getItems(); |
||||||
|
} |
||||||
|
|
||||||
|
protected final void setHeaderSize(HeaderSize headerSize) { |
||||||
|
mDataManager.setHeaderSize(headerSize); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isEmpty() { |
||||||
|
return mDataManager.isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int indexItem(T t) { |
||||||
|
return mDataManager.indexItem(t); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,272 @@ |
|||||||
|
package com.android.base.adapter.recycler; |
||||||
|
|
||||||
|
|
||||||
|
import com.android.base.adapter.DataManager; |
||||||
|
import com.android.base.utils.common.Checker; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import androidx.recyclerview.widget.RecyclerView; |
||||||
|
|
||||||
|
|
||||||
|
final class RecyclerDataManagerImpl<T> implements DataManager<T> { |
||||||
|
|
||||||
|
private List<T> mData; |
||||||
|
private RecyclerView.Adapter mAdapter; |
||||||
|
private HeaderSize mHeaderSize; |
||||||
|
|
||||||
|
RecyclerDataManagerImpl(List<T> tList, RecyclerView.Adapter adapter) { |
||||||
|
mData = tList; |
||||||
|
mAdapter = adapter; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isEmpty() { |
||||||
|
return getDataSize() == 0; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void add(T element) { |
||||||
|
addAt(getDataSize(), element); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addAt(int location, T element) { |
||||||
|
if (mData != null) { |
||||||
|
if (mData.isEmpty()) { |
||||||
|
mData.add(element); |
||||||
|
notifyItemInserted(getHeaderSize()); |
||||||
|
} else { |
||||||
|
int size = mData.size(); |
||||||
|
int lastIndex = (location >= size ? size : location) + getHeaderSize(); |
||||||
|
mData.add(location, element); |
||||||
|
notifyItemInserted(lastIndex); |
||||||
|
} |
||||||
|
} else { |
||||||
|
mData = new ArrayList<>(); |
||||||
|
mData.add(element); |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addItems(List<T> elements) { |
||||||
|
addItemsAt(getDataSize(), elements); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addItemsChecked(List<T> elements) { |
||||||
|
if (Checker.isEmpty(elements)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (mData == null) { |
||||||
|
addItems(elements); |
||||||
|
return; |
||||||
|
} |
||||||
|
boolean hasRemovedElements = false; |
||||||
|
for (T element : elements) { |
||||||
|
if (element == null) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (mData.contains(element)) { |
||||||
|
mData.remove(element); |
||||||
|
if (!hasRemovedElements) { |
||||||
|
hasRemovedElements = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (hasRemovedElements) { |
||||||
|
mData.addAll(elements); |
||||||
|
notifyDataSetChanged(); |
||||||
|
} else { |
||||||
|
addItems(elements); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addItemsAt(int location, List<T> elements) { |
||||||
|
if (Checker.isEmpty(elements)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (this.mData != null) { |
||||||
|
|
||||||
|
if (mData.isEmpty()) { |
||||||
|
mData.addAll(elements); |
||||||
|
notifyItemRangeInserted(getHeaderSize(), elements.size()); |
||||||
|
} else { |
||||||
|
int size = mData.size(); |
||||||
|
int lastIndex = (location >= size ? size : location) + getHeaderSize(); |
||||||
|
this.mData.addAll(location, elements); |
||||||
|
notifyItemRangeInserted(lastIndex, elements.size()); |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
this.mData = new ArrayList<>(elements.size()); |
||||||
|
mData.addAll(elements); |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void replace(T oldElem, T newElem) { |
||||||
|
if (mData != null && mData.contains(oldElem)) { |
||||||
|
replaceAt(mData.indexOf(oldElem), newElem); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void replaceAt(int index, T element) { |
||||||
|
if (getDataSize() > index) { |
||||||
|
mData.set(index, element); |
||||||
|
notifyItemChanged(index + getHeaderSize()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void replaceAll(List<T> elements) { |
||||||
|
if (mData == null) { |
||||||
|
mData = new ArrayList<>(); |
||||||
|
} |
||||||
|
mData.clear(); |
||||||
|
if (elements != null) { |
||||||
|
mData.addAll(elements); |
||||||
|
} |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setDataSource(List<T> newDataSource, boolean notifyDataSetChanged) { |
||||||
|
mData = newDataSource; |
||||||
|
if (notifyDataSetChanged) { |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void remove(T element) { |
||||||
|
if (mData == null || mData.isEmpty()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (mData.contains(element)) { |
||||||
|
int index = mData.indexOf(element) + getHeaderSize(); |
||||||
|
mData.remove(element); |
||||||
|
notifyItemRemoved(index); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeAt(int index) { |
||||||
|
if (getDataSize() > index) { |
||||||
|
mData.remove(index); |
||||||
|
notifyItemRemoved(index + getHeaderSize()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeItems(List<T> elements) { |
||||||
|
if (!Checker.isEmpty(elements) && mData != null && mData.containsAll(elements)) { |
||||||
|
mData.removeAll(elements); |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeItems(List<T> elements, boolean isSuccessive) { |
||||||
|
if (Checker.isEmpty(elements) || Checker.isEmpty(mData)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (!isSuccessive) { |
||||||
|
removeItems(elements); |
||||||
|
return; |
||||||
|
} |
||||||
|
T data = elements.get(0); |
||||||
|
int index = mData.indexOf(data); |
||||||
|
if (index == -1) { |
||||||
|
removeItems(elements); |
||||||
|
} else { |
||||||
|
mData.removeAll(elements); |
||||||
|
notifyItemRangeRemoved(index, elements.size()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public T getItem(int position) { |
||||||
|
position = position - getHeaderSize(); |
||||||
|
if (position >= 0 && getDataSize() > position) { |
||||||
|
return mData.get(position); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public final int getDataSize() { |
||||||
|
return mData == null ? 0 : mData.size(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean contains(T element) { |
||||||
|
return mData != null && mData.contains(element); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void clear() { |
||||||
|
if (mData != null) { |
||||||
|
mData.clear(); |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int indexItem(T t) { |
||||||
|
List<T> items = getItems(); |
||||||
|
if (items == null) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
return items.indexOf(t); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<T> getItems() { |
||||||
|
return mData; |
||||||
|
} |
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Size
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
private int getHeaderSize() { |
||||||
|
return mHeaderSize == null ? 0 : mHeaderSize.getHeaderSize(); |
||||||
|
} |
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Adapter Call
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
private void notifyItemChanged(int position) { |
||||||
|
mAdapter.notifyItemChanged(position); |
||||||
|
} |
||||||
|
|
||||||
|
private void notifyDataSetChanged() { |
||||||
|
mAdapter.notifyDataSetChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
private void notifyItemInserted(int position) { |
||||||
|
mAdapter.notifyItemInserted(position); |
||||||
|
} |
||||||
|
|
||||||
|
private void notifyItemRangeInserted(int position, int size) { |
||||||
|
mAdapter.notifyItemRangeInserted(position, size); |
||||||
|
} |
||||||
|
|
||||||
|
private void notifyItemRemoved(int index) { |
||||||
|
mAdapter.notifyItemRemoved(index); |
||||||
|
} |
||||||
|
|
||||||
|
private void notifyItemRangeRemoved(int index, int size) { |
||||||
|
mAdapter.notifyItemRangeRemoved(index, size); |
||||||
|
} |
||||||
|
|
||||||
|
void setHeaderSize(HeaderSize headerSize) { |
||||||
|
mHeaderSize = headerSize; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
package com.android.base.adapter.recycler |
||||||
|
|
||||||
|
import android.content.Context |
||||||
|
import android.view.LayoutInflater |
||||||
|
import android.view.View |
||||||
|
import android.view.ViewGroup |
||||||
|
|
||||||
|
/** |
||||||
|
* A simple way to build a simple list. If you want to build a multi type list, perhaps you need to use [MultiTypeAdapter]. |
||||||
|
* |
||||||
|
* @author Ztiany |
||||||
|
* Email: ztiany3@gmail.com |
||||||
|
* Date : 2019-01-15 11:41 |
||||||
|
*/ |
||||||
|
abstract class SimpleRecyclerAdapter<T>(context: Context, data: List<T>? = null) : RecyclerAdapter<T, KtViewHolder>(context, data) { |
||||||
|
|
||||||
|
private var layoutInflater: LayoutInflater = LayoutInflater.from(context) |
||||||
|
|
||||||
|
final override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): KtViewHolder { |
||||||
|
val layout = provideLayout(parent, viewType) |
||||||
|
val itemView = if (layout is Int) { |
||||||
|
layoutInflater.inflate(layout, parent, false) |
||||||
|
} else |
||||||
|
layout as View |
||||||
|
return KtViewHolder(itemView).apply { |
||||||
|
onViewHolderCreated(this) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected open fun onViewHolderCreated(viewHolder: KtViewHolder) = Unit |
||||||
|
|
||||||
|
/**provide a layout id or a View*/ |
||||||
|
abstract fun provideLayout(parent: ViewGroup, viewType: Int): Any |
||||||
|
|
||||||
|
override fun onBindViewHolder(viewHolder: KtViewHolder, position: Int) { |
||||||
|
bind(viewHolder, getItem(position) ?: throw NullPointerException("SimpleRecyclerAdapter onBindViewHolder getItem return null")) |
||||||
|
} |
||||||
|
|
||||||
|
protected abstract fun bind(viewHolder: KtViewHolder, item: T) |
||||||
|
|
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
package com.android.base.adapter.recycler; |
||||||
|
|
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
import com.android.base.adapter.ItemHelper; |
||||||
|
|
||||||
|
|
||||||
|
public class SmartViewHolder extends ViewHolder { |
||||||
|
|
||||||
|
private final ItemHelper mHelper; |
||||||
|
|
||||||
|
public SmartViewHolder(View itemView) { |
||||||
|
super(itemView); |
||||||
|
mHelper = new ItemHelper(itemView); |
||||||
|
} |
||||||
|
|
||||||
|
public ItemHelper helper() { |
||||||
|
return mHelper; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
package com.android.base.adapter.recycler; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
import androidx.annotation.IdRes; |
||||||
|
import androidx.recyclerview.widget.RecyclerView; |
||||||
|
|
||||||
|
|
||||||
|
public class ViewHolder extends RecyclerView.ViewHolder { |
||||||
|
|
||||||
|
public ViewHolder(View itemView) { |
||||||
|
super(itemView); |
||||||
|
} |
||||||
|
|
||||||
|
protected Context getContext() { |
||||||
|
return itemView.getContext(); |
||||||
|
} |
||||||
|
|
||||||
|
public <V extends View> V findView(@IdRes int viewId) { |
||||||
|
return itemView.findViewById(viewId); |
||||||
|
} |
||||||
|
|
||||||
|
public ViewHolder setItemClickListener(View.OnClickListener onClickListener) { |
||||||
|
itemView.setOnClickListener(onClickListener); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
package com.android.base.app |
||||||
|
|
||||||
|
import android.app.Activity |
||||||
|
import android.content.Context |
||||||
|
import android.os.Bundle |
||||||
|
import androidx.fragment.app.Fragment |
||||||
|
import androidx.fragment.app.FragmentActivity |
||||||
|
import androidx.fragment.app.FragmentManager |
||||||
|
import com.android.base.app.activity.ActivityDelegateOwner |
||||||
|
import com.android.base.app.dagger.Injectable |
||||||
|
import com.android.base.app.fragment.delegates.FragmentDelegateOwner |
||||||
|
import com.android.base.interfaces.ActivityLifecycleCallbacksAdapter |
||||||
|
import dagger.android.AndroidInjection |
||||||
|
import dagger.android.support.AndroidSupportInjection |
||||||
|
|
||||||
|
internal class AndroidComponentLifecycleInjector : ActivityLifecycleCallbacksAdapter { |
||||||
|
|
||||||
|
private var isAutoInjectEnable = false |
||||||
|
|
||||||
|
var delegateInjector: DelegateInjector? = null |
||||||
|
|
||||||
|
fun enableAutoInject() { |
||||||
|
isAutoInjectEnable = true |
||||||
|
} |
||||||
|
|
||||||
|
override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) { |
||||||
|
handleAutoInjectActivity(activity) |
||||||
|
handleAutoInstallActivityDelegate(activity) |
||||||
|
if (activity is FragmentActivity) { |
||||||
|
injectFragmentLifecycle(activity) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun injectFragmentLifecycle(activity: FragmentActivity) { |
||||||
|
activity.supportFragmentManager.registerFragmentLifecycleCallbacks(object : FragmentManager.FragmentLifecycleCallbacks() { |
||||||
|
override fun onFragmentAttached(fm: FragmentManager, fragment: Fragment, context: Context) { |
||||||
|
handleAutoInjectFragment(fragment) |
||||||
|
handleAutoInstallFragmentDelegate(fragment) |
||||||
|
} |
||||||
|
}, true) |
||||||
|
} |
||||||
|
|
||||||
|
private fun handleAutoInstallFragmentDelegate(fragment: Fragment) { |
||||||
|
if (fragment is FragmentDelegateOwner) { |
||||||
|
delegateInjector?.injectFragmentDelegate(fragment) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun handleAutoInstallActivityDelegate(activity: Activity?) { |
||||||
|
if (activity is ActivityDelegateOwner) { |
||||||
|
delegateInjector?.injectActivityDelegate(activity) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun handleAutoInjectActivity(activity: Activity?) { |
||||||
|
if (isAutoInjectEnable && activity is Injectable) { |
||||||
|
AndroidInjection.inject(activity) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun handleAutoInjectFragment(fragment: Fragment) { |
||||||
|
if (isAutoInjectEnable && fragment is Injectable) { |
||||||
|
AndroidSupportInjection.inject(fragment) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,80 @@ |
|||||||
|
package com.android.base.app |
||||||
|
|
||||||
|
import android.app.Activity |
||||||
|
import android.app.Application |
||||||
|
import android.content.Context |
||||||
|
import android.content.IntentFilter |
||||||
|
import android.content.res.Configuration |
||||||
|
import android.net.ConnectivityManager |
||||||
|
import com.android.base.app.utils.CrashHandler |
||||||
|
import com.android.base.receiver.NetStateReceiver |
||||||
|
import com.android.base.utils.BaseUtils |
||||||
|
import com.blankj.utilcode.util.AppUtils |
||||||
|
import com.blankj.utilcode.util.Utils.OnAppStatusChangedListener |
||||||
|
import io.reactivex.processors.BehaviorProcessor |
||||||
|
import timber.log.Timber |
||||||
|
import java.util.concurrent.atomic.AtomicBoolean |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Ztiany |
||||||
|
* Email: ztiany3@gmail.com |
||||||
|
* Date : 2018-10-12 18:19 |
||||||
|
*/ |
||||||
|
internal class ApplicationDelegate internal constructor(private val androidComponentLifecycleInjector: AndroidComponentLifecycleInjector) { |
||||||
|
|
||||||
|
lateinit var application: Application |
||||||
|
|
||||||
|
private lateinit var crashHandler: CrashHandler |
||||||
|
|
||||||
|
/** 获取可观察的 app 生命周期 */ |
||||||
|
val appStatus: BehaviorProcessor<Boolean> = BehaviorProcessor.create() |
||||||
|
|
||||||
|
private val onCreateCalled = AtomicBoolean(false) |
||||||
|
private val onAttachBaseCalled = AtomicBoolean(false) |
||||||
|
|
||||||
|
fun attachBaseContext(base: Context) { |
||||||
|
check(onAttachBaseCalled.compareAndSet(false, true)) { "Can only be called once" } |
||||||
|
} |
||||||
|
|
||||||
|
fun onCreate(application: Application) { |
||||||
|
check(onCreateCalled.compareAndSet(false, true)) { "Can only be called once" } |
||||||
|
this.application = application |
||||||
|
//工具类初始化 |
||||||
|
BaseUtils.init(application) |
||||||
|
//异常日志记录 |
||||||
|
crashHandler = CrashHandler.register(application) |
||||||
|
//网络状态 |
||||||
|
application.registerReceiver(NetStateReceiver(), IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)) |
||||||
|
//App前台后台 |
||||||
|
listenActivityLifecycleCallbacks() |
||||||
|
//声明周期回调 |
||||||
|
application.registerActivityLifecycleCallbacks(androidComponentLifecycleInjector) |
||||||
|
} |
||||||
|
|
||||||
|
fun onTerminate() {} |
||||||
|
|
||||||
|
fun onConfigurationChanged(newConfig: Configuration) {} |
||||||
|
|
||||||
|
fun onTrimMemory(level: Int) {} |
||||||
|
|
||||||
|
fun onLowMemory() {} |
||||||
|
|
||||||
|
private fun listenActivityLifecycleCallbacks() { |
||||||
|
AppUtils.registerAppStatusChangedListener(object : OnAppStatusChangedListener { |
||||||
|
override fun onBackground(activity: Activity?) { |
||||||
|
Timber.d("app进入后台") |
||||||
|
appStatus.onNext(false) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onForeground(activity: Activity?) { |
||||||
|
Timber.d("app进入前台") |
||||||
|
appStatus.onNext(true) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
internal fun setCrashProcessor(crashProcessor: CrashProcessor) { |
||||||
|
crashHandler.setCrashProcessor(crashProcessor) |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,55 @@ |
|||||||
|
package com.android.base.app |
||||||
|
|
||||||
|
import android.app.Application |
||||||
|
import android.content.Context |
||||||
|
import android.content.res.Configuration |
||||||
|
import dagger.android.DispatchingAndroidInjector |
||||||
|
import dagger.android.HasAndroidInjector |
||||||
|
import javax.inject.Inject |
||||||
|
|
||||||
|
open class BaseAppContext : Application() { |
||||||
|
|
||||||
|
override fun attachBaseContext(base: Context) { |
||||||
|
super.attachBaseContext(base) |
||||||
|
Sword.applicationDelegate.attachBaseContext(base) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onCreate() { |
||||||
|
super.onCreate() |
||||||
|
Sword.applicationDelegate.onCreate(this) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onLowMemory() { |
||||||
|
super.onLowMemory() |
||||||
|
Sword.applicationDelegate.onLowMemory() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onTrimMemory(level: Int) { |
||||||
|
super.onTrimMemory(level) |
||||||
|
Sword.applicationDelegate.onTrimMemory(level) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onConfigurationChanged(newConfig: Configuration) { |
||||||
|
super.onConfigurationChanged(newConfig) |
||||||
|
Sword.applicationDelegate.onConfigurationChanged(newConfig) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onTerminate() { |
||||||
|
super.onTerminate() |
||||||
|
Sword.applicationDelegate.onTerminate() |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
*@author Ztiany |
||||||
|
* Email: ztiany3@gmail.com |
||||||
|
* Date : 2019-10-12 10:59 |
||||||
|
*/ |
||||||
|
open class InjectorBaseAppContext : BaseAppContext(), HasAndroidInjector { |
||||||
|
|
||||||
|
@Inject lateinit var androidInjector: DispatchingAndroidInjector<Any> |
||||||
|
|
||||||
|
override fun androidInjector() = androidInjector |
||||||
|
|
||||||
|
} |
@ -0,0 +1,133 @@ |
|||||||
|
package com.android.base.app |
||||||
|
|
||||||
|
import android.app.Activity |
||||||
|
import android.content.Context |
||||||
|
import com.android.base.app.activity.ActivityDelegateOwner |
||||||
|
import com.android.base.app.fragment.animator.FragmentAnimator |
||||||
|
import com.android.base.app.fragment.delegates.FragmentDelegateOwner |
||||||
|
import com.android.base.app.fragment.tools.FragmentConfig |
||||||
|
import com.android.base.app.ui.LoadingView |
||||||
|
import com.android.base.app.ui.PageNumber |
||||||
|
import com.android.base.app.ui.RefreshLoadViewFactory |
||||||
|
import com.android.base.app.ui.RefreshLoadViewFactory.Factory |
||||||
|
import com.android.base.app.ui.RefreshViewFactory |
||||||
|
import com.android.base.receiver.NetworkState |
||||||
|
import com.blankj.utilcode.util.ActivityUtils |
||||||
|
import com.blankj.utilcode.util.AppUtils |
||||||
|
import io.reactivex.Flowable |
||||||
|
import io.reactivex.plugins.RxJavaPlugins |
||||||
|
import timber.log.Timber |
||||||
|
|
||||||
|
/** |
||||||
|
* useful tools for android development, just like a sword. |
||||||
|
* |
||||||
|
* @author Ztiany |
||||||
|
* Email: ztiany3@gmail.com |
||||||
|
* Date : 2018-04-16 17:12 |
||||||
|
*/ |
||||||
|
object Sword { |
||||||
|
|
||||||
|
private val androidComponentLifecycleInjector = AndroidComponentLifecycleInjector() |
||||||
|
|
||||||
|
/** Application lifecycle delegate */ |
||||||
|
internal val applicationDelegate = ApplicationDelegate(androidComponentLifecycleInjector) |
||||||
|
|
||||||
|
/** 错误类型分类器 */ |
||||||
|
var errorClassifier: ErrorClassifier? = null |
||||||
|
|
||||||
|
/** dialog 最小展示时间 */ |
||||||
|
var minimumShowingDialogMills: Long = 0 |
||||||
|
|
||||||
|
/** 用于创建 LoadingView*/ |
||||||
|
var loadingViewFactory: ((Context) -> LoadingView)? = null |
||||||
|
|
||||||
|
/**网络状态监听器*/ |
||||||
|
fun networkState(): Flowable<NetworkState> = NetworkState.observableState() |
||||||
|
|
||||||
|
fun setCrashProcessor(crashProcessor: CrashProcessor): Sword { |
||||||
|
applicationDelegate.setCrashProcessor(crashProcessor) |
||||||
|
return this |
||||||
|
} |
||||||
|
|
||||||
|
fun setDefaultPageStart(pageStart: Int): Sword { |
||||||
|
PageNumber.setDefaultPageStart(pageStart) |
||||||
|
return this |
||||||
|
} |
||||||
|
|
||||||
|
fun setDefaultPageSize(defaultPageSize: Int): Sword { |
||||||
|
PageNumber.setDefaultPageSize(defaultPageSize) |
||||||
|
return this |
||||||
|
} |
||||||
|
|
||||||
|
/** 设置一个默认的布局 id,在使用 Fragments 中相关方法时,如果没有传入特定的容器 id 时,则使用设置的默认布局 id。 */ |
||||||
|
fun setDefaultFragmentContainerId(defaultContainerId: Int): Sword { |
||||||
|
FragmentConfig.setDefaultContainerId(defaultContainerId) |
||||||
|
return this |
||||||
|
} |
||||||
|
|
||||||
|
/**设置默认的 Fragment 转场动画*/ |
||||||
|
fun setDefaultFragmentAnimator(animator: FragmentAnimator?): Sword { |
||||||
|
FragmentConfig.setDefaultFragmentAnimator(animator) |
||||||
|
return this |
||||||
|
} |
||||||
|
|
||||||
|
/** RxJava2的一个重要的设计理念是:不吃掉任何一个异常。产生的问题是,当RxJava2“downStream”取消订阅后,“upStream”仍有可能抛出异常,这时由于已经取消订阅, |
||||||
|
* “downStream”无法处理异常,此时的异常无人处理,便会导致程序崩溃,解决方案:在Application设置RxJavaPlugin的ErrorHandler。 |
||||||
|
* refer: [RxJava2使用过程中遇到的坑](https://github.com/qqiabc521/blog/issues/3) */ |
||||||
|
fun setupRxJavaErrorHandler(): Sword { |
||||||
|
RxJavaPlugins.setErrorHandler { |
||||||
|
Timber.d("RxJavaPlugins error handler receives error: $it") |
||||||
|
} |
||||||
|
return this |
||||||
|
} |
||||||
|
|
||||||
|
fun enableAutoInject(): Sword { |
||||||
|
androidComponentLifecycleInjector.enableAutoInject() |
||||||
|
return this |
||||||
|
} |
||||||
|
|
||||||
|
fun setDelegateInjector(delegateInjector: DelegateInjector): Sword { |
||||||
|
androidComponentLifecycleInjector.delegateInjector = delegateInjector |
||||||
|
return this |
||||||
|
} |
||||||
|
|
||||||
|
/** 获取可观察的 app 生命周期,发射 tue 表示 app 切换到前台,发射 false 表示 app 切换到后台 */ |
||||||
|
val appState: Flowable<Boolean> |
||||||
|
get() = applicationDelegate.appStatus |
||||||
|
|
||||||
|
/** 获取当前 resume 的 Activity */ |
||||||
|
val topActivity: Activity? |
||||||
|
get() = ActivityUtils.getTopActivity() |
||||||
|
|
||||||
|
/** App是否在前台运行 */ |
||||||
|
val isForeground: Boolean |
||||||
|
get() = AppUtils.isAppForeground() |
||||||
|
|
||||||
|
fun registerRefreshLoadViewFactory(factory: Factory): Sword { |
||||||
|
RefreshLoadViewFactory.registerFactory(factory) |
||||||
|
return this |
||||||
|
} |
||||||
|
|
||||||
|
fun registerRefreshViewFactory(factory: RefreshViewFactory.Factory): Sword { |
||||||
|
RefreshViewFactory.registerFactory(factory) |
||||||
|
return this |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
interface CrashProcessor { |
||||||
|
fun uncaughtException(thread: Thread, ex: Throwable) |
||||||
|
} |
||||||
|
|
||||||
|
interface ErrorClassifier { |
||||||
|
fun isNetworkError(throwable: Throwable): Boolean |
||||||
|
fun isServerError(throwable: Throwable): Boolean |
||||||
|
} |
||||||
|
|
||||||
|
interface DelegateInjector { |
||||||
|
|
||||||
|
fun injectFragmentDelegate(fragment: FragmentDelegateOwner) |
||||||
|
|
||||||
|
fun injectActivityDelegate(activity: ActivityDelegateOwner) |
||||||
|
|
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2018 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.android.base.app.aac |
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment |
||||||
|
import androidx.fragment.app.FragmentActivity |
||||||
|
import androidx.lifecycle.DefaultLifecycleObserver |
||||||
|
import androidx.lifecycle.Lifecycle |
||||||
|
import androidx.lifecycle.LifecycleOwner |
||||||
|
import kotlin.properties.ReadWriteProperty |
||||||
|
import kotlin.reflect.KProperty |
||||||
|
|
||||||
|
class AutoClearedValue<T : Any>( |
||||||
|
lifecycle: Lifecycle, |
||||||
|
private val _event: Lifecycle.Event, |
||||||
|
private val onCleared: (() -> Unit)? |
||||||
|
) : ReadWriteProperty<Fragment, T> { |
||||||
|
|
||||||
|
private var _value: T? = null |
||||||
|
|
||||||
|
init { |
||||||
|
lifecycle.addObserver(object : DefaultLifecycleObserver { |
||||||
|
|
||||||
|
override fun onPause(owner: LifecycleOwner) { |
||||||
|
clearValue(Lifecycle.Event.ON_PAUSE) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onStop(owner: LifecycleOwner) { |
||||||
|
clearValue(Lifecycle.Event.ON_STOP) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onDestroy(owner: LifecycleOwner) { |
||||||
|
clearValue(Lifecycle.Event.ON_DESTROY) |
||||||
|
} |
||||||
|
|
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
override fun getValue(thisRef: Fragment, property: KProperty<*>): T { |
||||||
|
return _value |
||||||
|
?: throw IllegalStateException("should never call auto-cleared-value get when it might not be available") |
||||||
|
} |
||||||
|
|
||||||
|
override fun setValue(thisRef: Fragment, property: KProperty<*>, value: T) { |
||||||
|
_value = value |
||||||
|
} |
||||||
|
|
||||||
|
private fun clearValue(event: Lifecycle.Event) { |
||||||
|
if (_event == event) { |
||||||
|
_value = null |
||||||
|
onCleared?.invoke() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
fun <T : Any> Fragment.autoCleared( |
||||||
|
event: Lifecycle.Event = Lifecycle.Event.ON_DESTROY, |
||||||
|
onCleared: (() -> Unit)? = null |
||||||
|
) = AutoClearedValue<T>(this.lifecycle, event, onCleared) |
||||||
|
|
||||||
|
fun <T : Any> FragmentActivity.autoCleared( |
||||||
|
event: Lifecycle.Event = Lifecycle.Event.ON_DESTROY, |
||||||
|
onCleared: (() -> Unit)? = null |
||||||
|
) = AutoClearedValue<T>(this.lifecycle, event, onCleared) |
@ -0,0 +1,23 @@ |
|||||||
|
package com.android.base.app.aac |
||||||
|
|
||||||
|
import androidx.lifecycle.MutableLiveData |
||||||
|
|
||||||
|
fun <T> MutableLiveData<T>.init(t: T): MutableLiveData<T> { |
||||||
|
this.postValue(t) |
||||||
|
return this |
||||||
|
} |
||||||
|
|
||||||
|
fun <T> MutableLiveData<T>.touchOff() { |
||||||
|
this.postValue(this.value) |
||||||
|
} |
||||||
|
|
||||||
|
fun <T> MutableLiveData<List<T>>.append(list: List<T>) { |
||||||
|
val value = this.value |
||||||
|
if (value == null) { |
||||||
|
this.postValue(list) |
||||||
|
} else { |
||||||
|
val mutableList = value.toMutableList() |
||||||
|
mutableList.addAll(list) |
||||||
|
this.postValue(mutableList) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,116 @@ |
|||||||
|
package com.android.base.app.aac; |
||||||
|
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.ListIterator; |
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
import androidx.annotation.Nullable; |
||||||
|
import androidx.lifecycle.LifecycleOwner; |
||||||
|
import androidx.lifecycle.MediatorLiveData; |
||||||
|
import androidx.lifecycle.Observer; |
||||||
|
import timber.log.Timber; |
||||||
|
|
||||||
|
public class SingleLiveData<T> extends MediatorLiveData<T> { |
||||||
|
|
||||||
|
private int mVersion = 0; |
||||||
|
|
||||||
|
private final List<WeakReference<ObserverWrapper<? super T>>> mWrapperObserverList = new ArrayList<>(); |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setValue(T value) { |
||||||
|
mVersion++; |
||||||
|
super.setValue(value); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) { |
||||||
|
super.observe(owner, getOrNewObserver(observer, mVersion)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void observeForever(@NonNull Observer<? super T> observer) { |
||||||
|
super.observeForever(getOrNewObserver(observer, mVersion)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
public void removeObserver(@NonNull Observer<? super T> observer) { |
||||||
|
if (observer instanceof ObserverWrapper) { |
||||||
|
super.removeObserver(observer); |
||||||
|
removeWrapper((ObserverWrapper) observer); |
||||||
|
Timber.d("removeObserver() called with: observer = wrapper = [" + observer + "]"); |
||||||
|
} else { |
||||||
|
ObserverWrapper<? super T> wrapper = findWrapper(observer); |
||||||
|
Timber.d("removeObserver() called with: observer = [" + observer + "], wrapper = [" + wrapper + "]"); |
||||||
|
super.removeObserver(wrapper); |
||||||
|
removeWrapper(wrapper); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void removeWrapper(ObserverWrapper observer) { |
||||||
|
ListIterator<WeakReference<ObserverWrapper<? super T>>> iterator = mWrapperObserverList.listIterator(); |
||||||
|
while (iterator.hasNext()) { |
||||||
|
WeakReference<ObserverWrapper<? super T>> next = iterator.next(); |
||||||
|
ObserverWrapper<? super T> item = next.get(); |
||||||
|
if (item == observer) { |
||||||
|
iterator.remove(); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private ObserverWrapper<? super T> findWrapper(Observer<? super T> observer) { |
||||||
|
ListIterator<WeakReference<ObserverWrapper<? super T>>> iterator = mWrapperObserverList.listIterator(); |
||||||
|
|
||||||
|
ObserverWrapper<? super T> target = null; |
||||||
|
|
||||||
|
while (iterator.hasNext()) { |
||||||
|
WeakReference<ObserverWrapper<? super T>> next = iterator.next(); |
||||||
|
ObserverWrapper<? super T> item = next.get(); |
||||||
|
if (item == null) { |
||||||
|
iterator.remove(); |
||||||
|
} else if (item.mOrigin == observer) { |
||||||
|
target = item; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return target; |
||||||
|
} |
||||||
|
|
||||||
|
private Observer<? super T> getOrNewObserver(@NonNull Observer<? super T> observer, int observerVersion) { |
||||||
|
ObserverWrapper<? super T> wrapper = findWrapper(observer); |
||||||
|
|
||||||
|
if (wrapper == null) { |
||||||
|
wrapper = new ObserverWrapper<>(observerVersion, observer); |
||||||
|
mWrapperObserverList.add(new WeakReference<>(wrapper)); |
||||||
|
} |
||||||
|
|
||||||
|
Timber.d("getOrNewObserver() called with: observer = [" + observer + "], observerVersion = [" + observerVersion + "], wrapper = [" + wrapper + "]"); |
||||||
|
|
||||||
|
return wrapper; |
||||||
|
} |
||||||
|
|
||||||
|
private class ObserverWrapper<E> implements Observer<E> { |
||||||
|
|
||||||
|
private final int mObserverVersion; |
||||||
|
|
||||||
|
private final Observer<E> mOrigin; |
||||||
|
|
||||||
|
private ObserverWrapper(int observerVersion, Observer<E> origin) { |
||||||
|
mObserverVersion = observerVersion; |
||||||
|
mOrigin = origin; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onChanged(@Nullable E t) { |
||||||
|
if (mObserverVersion < mVersion && mOrigin != null) { |
||||||
|
mOrigin.onChanged(t); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,62 @@ |
|||||||
|
package com.android.base.app.aac; |
||||||
|
|
||||||
|
import android.util.Log; |
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean; |
||||||
|
|
||||||
|
import androidx.annotation.MainThread; |
||||||
|
import androidx.annotation.NonNull; |
||||||
|
import androidx.annotation.Nullable; |
||||||
|
import androidx.lifecycle.LifecycleOwner; |
||||||
|
import androidx.lifecycle.MutableLiveData; |
||||||
|
import androidx.lifecycle.Observer; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* A lifecycle-aware observable that sends only new updates after subscription, used for events like |
||||||
|
* navigation and Snackbar messages. |
||||||
|
* <p> |
||||||
|
* This avoids a common problem with events: on configuration change (like rotation) an update |
||||||
|
* can be emitted if the observer is active. This LiveData only calls the observable if there's an |
||||||
|
* explicit call to setValue() or call(). |
||||||
|
* <p> |
||||||
|
* Note that only one observer is going to be notified of changes. |
||||||
|
* |
||||||
|
* @see <a href='https://github.com/googlesamples/android-architecture/blob/dev-todo-mvvm-live/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/SingleLiveEvent.java'>SingleLiveEvent</a>
|
||||||
|
*/ |
||||||
|
public class SingleLiveEvent<T> extends MutableLiveData<T> { |
||||||
|
|
||||||
|
private static final String TAG = "SingleLiveEvent"; |
||||||
|
|
||||||
|
private final AtomicBoolean mPending = new AtomicBoolean(false); |
||||||
|
|
||||||
|
@MainThread |
||||||
|
public void observe(@NonNull LifecycleOwner owner, @NonNull final Observer<? super T> observer) { |
||||||
|
|
||||||
|
if (hasActiveObservers()) { |
||||||
|
Log.w(TAG, "Multiple observers registered but only one will be notified of changes."); |
||||||
|
} |
||||||
|
|
||||||
|
// Observe the internal MutableLiveData
|
||||||
|
super.observe(owner, t -> { |
||||||
|
if (mPending.compareAndSet(true, false)) { |
||||||
|
observer.onChanged(t); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@MainThread |
||||||
|
public void setValue(@Nullable T t) { |
||||||
|
mPending.set(true); |
||||||
|
super.setValue(t); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Used for cases where T is Void, to make calls cleaner. |
||||||
|
*/ |
||||||
|
@MainThread |
||||||
|
public void call() { |
||||||
|
setValue(null); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
package com.android.base.app.activity; |
||||||
|
|
||||||
|
import android.app.Activity; |
||||||
|
import android.content.Intent; |
||||||
|
import android.os.Bundle; |
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
import androidx.annotation.Nullable; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Activity生命周期代理 |
||||||
|
* |
||||||
|
* @author Ztiany |
||||||
|
* Date : 2016-05-06 15:04 |
||||||
|
* Email: 1169654504@qq.com |
||||||
|
*/ |
||||||
|
@SuppressWarnings("unused") |
||||||
|
public interface ActivityDelegate<T extends Activity> { |
||||||
|
|
||||||
|
default void onAttachedToActivity(@NonNull T activity) { |
||||||
|
} |
||||||
|
|
||||||
|
default void onDetachedFromActivity() { |
||||||
|
} |
||||||
|
|
||||||
|
default void onCreateBeforeSetContentView(@Nullable Bundle savedInstanceState) { |
||||||
|
} |
||||||
|
|
||||||
|
default void onCreateAfterSetContentView(@Nullable Bundle savedInstanceState) { |
||||||
|
} |
||||||
|
|
||||||
|
default void onSaveInstanceState(Bundle savedInstanceState) { |
||||||
|
} |
||||||
|
|
||||||
|
default void onStart() { |
||||||
|
} |
||||||
|
|
||||||
|
default void onResume() { |
||||||
|
} |
||||||
|
|
||||||
|
default void onPause() { |
||||||
|
} |
||||||
|
|
||||||
|
default void onStop() { |
||||||
|
} |
||||||
|
|
||||||
|
default void onDestroy() { |
||||||
|
} |
||||||
|
|
||||||
|
default void onActivityResult(int requestCode, int resultCode, Intent data) { |
||||||
|
} |
||||||
|
|
||||||
|
default void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { |
||||||
|
} |
||||||
|
|
||||||
|
default void onRestoreInstanceState(Bundle savedInstanceState) { |
||||||
|
} |
||||||
|
|
||||||
|
default void onRestart() { |
||||||
|
} |
||||||
|
|
||||||
|
default void onPostCreate(@Nullable Bundle savedInstanceState) { |
||||||
|
} |
||||||
|
|
||||||
|
default void onResumeFragments() { |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
package com.android.base.app.activity; |
||||||
|
|
||||||
|
|
||||||
|
import com.github.dmstocking.optional.java.util.function.Predicate; |
||||||
|
|
||||||
|
import androidx.annotation.UiThread; |
||||||
|
|
||||||
|
@UiThread |
||||||
|
@SuppressWarnings("unused") |
||||||
|
public interface ActivityDelegateOwner { |
||||||
|
|
||||||
|
void addDelegate(ActivityDelegate fragmentDelegate); |
||||||
|
|
||||||
|
boolean removeDelegate(ActivityDelegate fragmentDelegate); |
||||||
|
|
||||||
|
ActivityDelegate findDelegate(Predicate<ActivityDelegate> predicate); |
||||||
|
|
||||||
|
ActivityState getStatus(); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,138 @@ |
|||||||
|
package com.android.base.app.activity; |
||||||
|
|
||||||
|
import android.content.Intent; |
||||||
|
import android.os.Bundle; |
||||||
|
|
||||||
|
import com.github.dmstocking.optional.java.util.function.Predicate; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import androidx.annotation.Nullable; |
||||||
|
import androidx.annotation.UiThread; |
||||||
|
import androidx.appcompat.app.AppCompatActivity; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Ztiany |
||||||
|
* Email: 1169654504@qq.com |
||||||
|
* Date : 2016-12-20 11:43 |
||||||
|
*/ |
||||||
|
@UiThread |
||||||
|
final class ActivityDelegates { |
||||||
|
|
||||||
|
private final List<ActivityDelegate> mDelegates; |
||||||
|
private AppCompatActivity mBaseActivity; |
||||||
|
|
||||||
|
ActivityDelegates(AppCompatActivity baseActivity) { |
||||||
|
mDelegates = new ArrayList<>(4); |
||||||
|
mBaseActivity = baseActivity; |
||||||
|
} |
||||||
|
|
||||||
|
void callOnCreateBeforeSetContentView(@Nullable Bundle savedInstanceState) { |
||||||
|
for (ActivityDelegate activityDelegate : mDelegates) { |
||||||
|
activityDelegate.onCreateBeforeSetContentView(savedInstanceState); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void callOnCreateAfterSetContentView(@Nullable Bundle savedInstanceState) { |
||||||
|
for (ActivityDelegate activityDelegate : mDelegates) { |
||||||
|
activityDelegate.onCreateAfterSetContentView(savedInstanceState); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void callOnRestoreInstanceState(Bundle savedInstanceState) { |
||||||
|
for (ActivityDelegate activityDelegate : mDelegates) { |
||||||
|
activityDelegate.onRestoreInstanceState(savedInstanceState); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void callOnPostCreate(@Nullable Bundle savedInstanceState) { |
||||||
|
for (ActivityDelegate activityDelegate : mDelegates) { |
||||||
|
activityDelegate.onPostCreate(savedInstanceState); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void callOnSaveInstanceState(Bundle outState) { |
||||||
|
for (ActivityDelegate activityDelegate : mDelegates) { |
||||||
|
activityDelegate.onSaveInstanceState(outState); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void callOnDestroy() { |
||||||
|
for (ActivityDelegate activityDelegate : mDelegates) { |
||||||
|
activityDelegate.onDestroy(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void callOnStop() { |
||||||
|
for (ActivityDelegate activityDelegate : mDelegates) { |
||||||
|
activityDelegate.onStop(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void callOnPause() { |
||||||
|
for (ActivityDelegate activityDelegate : mDelegates) { |
||||||
|
activityDelegate.onPause(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void callOnResume() { |
||||||
|
for (ActivityDelegate activityDelegate : mDelegates) { |
||||||
|
activityDelegate.onResume(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void callOnStart() { |
||||||
|
for (ActivityDelegate activityDelegate : mDelegates) { |
||||||
|
activityDelegate.onStart(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void callOnRestart() { |
||||||
|
for (ActivityDelegate activityDelegate : mDelegates) { |
||||||
|
activityDelegate.onRestart(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void callOnActivityResult(int requestCode, int resultCode, Intent data) { |
||||||
|
for (ActivityDelegate activityDelegate : mDelegates) { |
||||||
|
activityDelegate.onActivityResult(requestCode, resultCode, data); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void callOnRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { |
||||||
|
for (ActivityDelegate activityDelegate : mDelegates) { |
||||||
|
activityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void callOnResumeFragments() { |
||||||
|
for (ActivityDelegate activityDelegate : mDelegates) { |
||||||
|
activityDelegate.onResumeFragments(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
void addActivityDelegate(ActivityDelegate activityDelegate) { |
||||||
|
mDelegates.add(activityDelegate); |
||||||
|
activityDelegate.onAttachedToActivity(mBaseActivity); |
||||||
|
} |
||||||
|
|
||||||
|
boolean removeActivityDelegate(ActivityDelegate activityDelegate) { |
||||||
|
boolean remove = mDelegates.remove(activityDelegate); |
||||||
|
if (remove) { |
||||||
|
activityDelegate.onDetachedFromActivity(); |
||||||
|
} |
||||||
|
return remove; |
||||||
|
} |
||||||
|
|
||||||
|
ActivityDelegate findDelegate(Predicate<ActivityDelegate> predicate) { |
||||||
|
for (ActivityDelegate delegate : mDelegates) { |
||||||
|
if (predicate.test(delegate)) { |
||||||
|
return delegate; |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
package com.android.base.app.activity |
||||||
|
|
||||||
|
enum class ActivityState { |
||||||
|
INITIALIZED, |
||||||
|
CREATE, |
||||||
|
START, |
||||||
|
RESUME, |
||||||
|
PAUSE, |
||||||
|
STOP, |
||||||
|
DESTROY |
||||||
|
} |
@ -0,0 +1,72 @@ |
|||||||
|
package com.android.base.app.activity; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment; |
||||||
|
import androidx.fragment.app.FragmentActivity; |
||||||
|
import androidx.fragment.app.FragmentManager; |
||||||
|
|
||||||
|
/** |
||||||
|
* <pre> |
||||||
|
* 1.Fragment需要自己处理BackPress事件,如果不处理,就交给子Fragment处理。都不处理则由Activity处理 |
||||||
|
* 2.BackPress的传递由低层往深层传递,同一层级的中外层中的 Fragment优先处理。 |
||||||
|
* 3.在Fragment中嵌套使用Fragment时,请使用getSupportChildFragmentManager |
||||||
|
* </pre> |
||||||
|
*/ |
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
public class BackHandlerHelper { |
||||||
|
|
||||||
|
/** |
||||||
|
* 将back事件分发给 FragmentManager 中管理的子Fragment,如果该 FragmentManager 中的所有Fragment都 |
||||||
|
* 没有处理back事件,则尝试 FragmentManager.popBackStack() |
||||||
|
* |
||||||
|
* @return 如果处理了back键则返回 <b>true</b> |
||||||
|
* @see #handleBackPress(Fragment) |
||||||
|
* @see #handleBackPress(FragmentActivity) |
||||||
|
*/ |
||||||
|
public static boolean handleBackPress(FragmentManager fragmentManager) { |
||||||
|
List<Fragment> fragments = fragmentManager.getFragments(); |
||||||
|
for (int i = fragments.size() - 1; i >= 0; i--) { |
||||||
|
Fragment child = fragments.get(i); |
||||||
|
if (isFragmentBackHandled(child)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 将back事件分发给Fragment中的子Fragment, |
||||||
|
* 该方法调用了 {@link #handleBackPress(FragmentManager)} |
||||||
|
* |
||||||
|
* @return 如果处理了back键则返回 <b>true</b> |
||||||
|
*/ |
||||||
|
public static boolean handleBackPress(Fragment fragment) { |
||||||
|
return handleBackPress(fragment.getChildFragmentManager()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 将back事件分发给Activity中的子Fragment, |
||||||
|
* 该方法调用了 {@link #handleBackPress(FragmentManager)} |
||||||
|
* |
||||||
|
* @return 如果处理了back键则返回 <b>true</b> |
||||||
|
*/ |
||||||
|
public static boolean handleBackPress(FragmentActivity fragmentActivity) { |
||||||
|
return handleBackPress(fragmentActivity.getSupportFragmentManager()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 判断Fragment是否处理了Back键 |
||||||
|
* |
||||||
|
* @return 如果处理了back键则返回 <b>true</b> |
||||||
|
*/ |
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
public static boolean isFragmentBackHandled(Fragment fragment) { |
||||||
|
return fragment != null |
||||||
|
&& fragment.isVisible() |
||||||
|
&& fragment.getUserVisibleHint() //getUserVisibleHint默认情况下为true,在ViewPager中会被使用到。
|
||||||
|
&& fragment instanceof OnBackPressListener |
||||||
|
&& ((OnBackPressListener) fragment).onBackPressed(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,191 @@ |
|||||||
|
package com.android.base.app.activity |
||||||
|
|
||||||
|
import android.content.Intent |
||||||
|
import android.os.Bundle |
||||||
|
import android.view.View |
||||||
|
import androidx.annotation.UiThread |
||||||
|
import androidx.appcompat.app.AppCompatActivity |
||||||
|
import androidx.fragment.app.Fragment |
||||||
|
import com.android.base.rx.autodispose.AutoDisposeLifecycleOwnerEx |
||||||
|
import com.android.base.utils.android.compat.AndroidVersion |
||||||
|
import com.github.dmstocking.optional.java.util.function.Predicate |
||||||
|
import timber.log.Timber |
||||||
|
|
||||||
|
/** |
||||||
|
* 基础 BaseActivity 封装: |
||||||
|
* |
||||||
|
* 1. 封装通用流程。 |
||||||
|
* 2. [onBackPressed] 事件分发,优先交给 [Fragment] 处理。 |
||||||
|
* 3. 提供 RxJava 的生命周期绑定。 |
||||||
|
* |
||||||
|
* @author Ztiany |
||||||
|
* Date : 2016-05-04 15:40 |
||||||
|
* Email: 1169654504@qq.com |
||||||
|
*/ |
||||||
|
abstract class BaseActivity : AppCompatActivity(), ActivityDelegateOwner, AutoDisposeLifecycleOwnerEx { |
||||||
|
|
||||||
|
@Suppress("LeakingThis") |
||||||
|
private val activityDelegates = ActivityDelegates(this) |
||||||
|
|
||||||
|
private var activityState = ActivityState.INITIALIZED |
||||||
|
|
||||||
|
private fun tag() = this.javaClass.simpleName |
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) { |
||||||
|
Timber.tag(tag()).d("---->onCreate before call super") |
||||||
|
initialize(savedInstanceState) |
||||||
|
activityDelegates.callOnCreateBeforeSetContentView(savedInstanceState) |
||||||
|
super.onCreate(savedInstanceState) |
||||||
|
Timber.tag(tag()).d("---->onCreate after call super bundle = $savedInstanceState") |
||||||
|
|
||||||
|
when (val layout = layout()) { |
||||||
|
is View -> setContentView(layout) |
||||||
|
is Int -> setContentView(layout) |
||||||
|
null -> Timber.d("layout() return null layout") |
||||||
|
else -> throw IllegalArgumentException("layout() return type no support, layout = $layout") |
||||||
|
} |
||||||
|
|
||||||
|
activityState = ActivityState.CREATE |
||||||
|
activityDelegates.callOnCreateAfterSetContentView(savedInstanceState) |
||||||
|
setupView(savedInstanceState) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onRestart() { |
||||||
|
Timber.tag(tag()).d("---->onRestart before call super") |
||||||
|
super.onRestart() |
||||||
|
Timber.tag(tag()).d("---->onRestart after call super ") |
||||||
|
activityDelegates.callOnRestart() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onStart() { |
||||||
|
Timber.tag(tag()).d("---->onStart before call super") |
||||||
|
super.onStart() |
||||||
|
Timber.tag(tag()).d("---->onStart after call super") |
||||||
|
activityState = ActivityState.START |
||||||
|
activityDelegates.callOnStart() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onResume() { |
||||||
|
Timber.tag(tag()).d("---->onResume before call super") |
||||||
|
super.onResume() |
||||||
|
Timber.tag(tag()).d("---->onResume after call super") |
||||||
|
activityState = ActivityState.RESUME |
||||||
|
activityDelegates.callOnResume() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onPause() { |
||||||
|
Timber.tag(tag()).d("---->onPause before call super") |
||||||
|
activityState = ActivityState.PAUSE |
||||||
|
activityDelegates.callOnPause() |
||||||
|
super.onPause() |
||||||
|
Timber.tag(tag()).d("---->onPause after call super ") |
||||||
|
} |
||||||
|
|
||||||
|
override fun onStop() { |
||||||
|
Timber.tag(tag()).d("---->onStop before call super") |
||||||
|
activityState = ActivityState.STOP |
||||||
|
activityDelegates.callOnStop() |
||||||
|
super.onStop() |
||||||
|
Timber.tag(tag()).d("---->onStop after call super") |
||||||
|
} |
||||||
|
|
||||||
|
override fun onDestroy() { |
||||||
|
Timber.tag(tag()).d("---->onDestroy before call super") |
||||||
|
activityState = ActivityState.DESTROY |
||||||
|
activityDelegates.callOnDestroy() |
||||||
|
super.onDestroy() |
||||||
|
Timber.tag(tag()).d("---->onDestroy after call super") |
||||||
|
} |
||||||
|
|
||||||
|
override fun onPostCreate(savedInstanceState: Bundle?) { |
||||||
|
super.onPostCreate(savedInstanceState) |
||||||
|
activityDelegates.callOnPostCreate(savedInstanceState) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) { |
||||||
|
super.onSaveInstanceState(outState) |
||||||
|
activityDelegates.callOnSaveInstanceState(outState) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onRestoreInstanceState(savedInstanceState: Bundle) { |
||||||
|
super.onRestoreInstanceState(savedInstanceState) |
||||||
|
activityDelegates.callOnRestoreInstanceState(savedInstanceState) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { |
||||||
|
super.onActivityResult(requestCode, resultCode, data) |
||||||
|
activityDelegates.callOnActivityResult(requestCode, resultCode, data) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { |
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults) |
||||||
|
activityDelegates.callOnRequestPermissionsResult(requestCode, permissions, grantResults) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onResumeFragments() { |
||||||
|
super.onResumeFragments() |
||||||
|
activityDelegates.callOnResumeFragments() |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////// |
||||||
|
// interface impl |
||||||
|
/////////////////////////////////////////////////////////////////////////// |
||||||
|
@UiThread |
||||||
|
final override fun addDelegate(activityDelegate: ActivityDelegate<*>) { |
||||||
|
activityDelegates.addActivityDelegate(activityDelegate) |
||||||
|
} |
||||||
|
|
||||||
|
@UiThread |
||||||
|
final override fun removeDelegate(activityDelegate: ActivityDelegate<*>): Boolean { |
||||||
|
return activityDelegates.removeActivityDelegate(activityDelegate) |
||||||
|
} |
||||||
|
|
||||||
|
@UiThread |
||||||
|
final override fun findDelegate(predicate: Predicate<ActivityDelegate<*>?>?): ActivityDelegate<*>? { |
||||||
|
return activityDelegates.findDelegate(predicate) |
||||||
|
} |
||||||
|
|
||||||
|
override fun getStatus(): ActivityState { |
||||||
|
return activityState |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Before call super.onCreate and setContentView |
||||||
|
* |
||||||
|
* @param savedInstanceState state |
||||||
|
*/ |
||||||
|
protected open fun initialize(savedInstanceState: Bundle?) {} |
||||||
|
|
||||||
|
/** |
||||||
|
* provide a layoutId (int) or layout (View) |
||||||
|
* |
||||||
|
* @return layoutId |
||||||
|
*/ |
||||||
|
protected abstract fun layout(): Any? |
||||||
|
|
||||||
|
/** |
||||||
|
* after setContentView |
||||||
|
*/ |
||||||
|
protected abstract fun setupView(savedInstanceState: Bundle?) |
||||||
|
|
||||||
|
override fun onBackPressed() { |
||||||
|
if (BackHandlerHelper.handleBackPress(this)) { |
||||||
|
Timber.d("onBackPressed() called but child fragment handle it") |
||||||
|
} else { |
||||||
|
superOnBackPressed() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected open fun superOnBackPressed() { |
||||||
|
super.onBackPressed() |
||||||
|
} |
||||||
|
|
||||||
|
override fun isDestroyed(): Boolean { |
||||||
|
return if (AndroidVersion.atLeast(17)) { |
||||||
|
super.isDestroyed() |
||||||
|
} else { |
||||||
|
status === ActivityState.DESTROY |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
package com.android.base.app.activity; |
||||||
|
|
||||||
|
import com.android.base.app.dagger.Injectable; |
||||||
|
|
||||||
|
import javax.inject.Inject; |
||||||
|
|
||||||
|
import dagger.android.AndroidInjector; |
||||||
|
import dagger.android.DispatchingAndroidInjector; |
||||||
|
import dagger.android.HasAndroidInjector; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Ztiany |
||||||
|
* Email: 1169654504@qq.com |
||||||
|
* Date : 2018-11-01 |
||||||
|
*/ |
||||||
|
public abstract class InjectorBaseActivity extends BaseActivity implements Injectable, HasAndroidInjector { |
||||||
|
|
||||||
|
@Inject |
||||||
|
DispatchingAndroidInjector<Object> androidInjector; |
||||||
|
|
||||||
|
@Override |
||||||
|
public AndroidInjector<Object> androidInjector() { |
||||||
|
return androidInjector; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
package com.android.base.app.activity; |
||||||
|
|
||||||
|
/** |
||||||
|
* Activity的返回键监听 |
||||||
|
*/ |
||||||
|
public interface OnBackPressListener { |
||||||
|
|
||||||
|
/** |
||||||
|
* @return true 表示Fragment处理back press,false表示由Activity处理 |
||||||
|
*/ |
||||||
|
boolean onBackPressed(); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
package com.android.base.app.dagger; |
||||||
|
|
||||||
|
import java.lang.annotation.Documented; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
|
||||||
|
import javax.inject.Scope; |
||||||
|
|
||||||
|
|
||||||
|
@Scope |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@Documented |
||||||
|
public @interface ActivityScope { |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
package com.android.base.app.dagger; |
||||||
|
|
||||||
|
import java.lang.annotation.Documented; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
|
||||||
|
import javax.inject.Qualifier; |
||||||
|
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME; |
||||||
|
|
||||||
|
|
||||||
|
@Qualifier |
||||||
|
@Documented |
||||||
|
@Retention(RUNTIME) |
||||||
|
public @interface ContextType { |
||||||
|
|
||||||
|
String ACTIVITY = "Activity"; |
||||||
|
String CONTEXT = "Context"; |
||||||
|
String APPLICATION = "Application"; |
||||||
|
|
||||||
|
String value() default APPLICATION; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
package com.android.base.app.dagger; |
||||||
|
|
||||||
|
import java.lang.annotation.Documented; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
|
||||||
|
import javax.inject.Scope; |
||||||
|
|
||||||
|
|
||||||
|
@Scope |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@Documented |
||||||
|
public @interface FragmentScope { |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
package com.android.base.app.dagger; |
||||||
|
|
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import javax.inject.Provider; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Ztiany |
||||||
|
* Email: ztiany3@gmail.com |
||||||
|
* Date : 2017-06-21 14:58 |
||||||
|
*/ |
||||||
|
public interface HasViewInjector { |
||||||
|
|
||||||
|
Map<Class<? extends View>, Provider<ViewComponentBuilder>> viewInjectors(); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
package com.android.base.app.dagger; |
||||||
|
|
||||||
|
/** |
||||||
|
* 当 Activity 实现此接口时,如果需要为内部的 Fragment 提供注入容器,需要实现 HasSupportFragmentInjector,具体如下面代码: |
||||||
|
* |
||||||
|
* <pre>{@code |
||||||
|
* |
||||||
|
* class InjectableActivity implements Injectable, HasSupportFragmentInjector{ |
||||||
|
* @Inject |
||||||
|
* DispatchingAndroidInjector<Fragment> fragmentInjector; |
||||||
|
* |
||||||
|
* public AndroidInjector<Fragment> supportFragmentInjector() { |
||||||
|
* return fragmentInjector; |
||||||
|
* } |
||||||
|
* } |
||||||
|
* } |
||||||
|
* |
||||||
|
* 标记接口,用于标记此类需要被注入依赖。 |
||||||
|
* |
||||||
|
* @author Ztiany |
||||||
|
* Email: ztiany3@gmail.com |
||||||
|
* Date : 2018-12-11 12:38 |
||||||
|
*/ |
||||||
|
public interface Injectable { |
||||||
|
|
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
package com.android.base.app.dagger; |
||||||
|
|
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
import dagger.MembersInjector; |
||||||
|
|
||||||
|
/** |
||||||
|
* how to use it? refer https://github.com/Ztiany/notes/blob/master/Android/00-Code/Dagger2AndroidInjection-v2.24/README.md.
|
||||||
|
* |
||||||
|
* @param <A> |
||||||
|
*/ |
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
public interface ViewComponent<A extends View> extends MembersInjector<A> { |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
package com.android.base.app.dagger; |
||||||
|
|
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
import dagger.BindsInstance; |
||||||
|
|
||||||
|
|
||||||
|
public interface ViewComponentBuilder<T extends View, C extends ViewComponent<T>> { |
||||||
|
|
||||||
|
@BindsInstance |
||||||
|
ViewComponentBuilder bindInstance(T t); |
||||||
|
|
||||||
|
C build(); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
package com.android.base.app.dagger; |
||||||
|
|
||||||
|
import android.app.Application; |
||||||
|
import android.content.Context; |
||||||
|
import android.util.Log; |
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import javax.inject.Provider; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Ztiany |
||||||
|
* Email: ztiany3@gmail.com |
||||||
|
* Date : 2017-06-21 14:55 |
||||||
|
*/ |
||||||
|
@SuppressWarnings("unused") |
||||||
|
public class ViewInjection { |
||||||
|
|
||||||
|
private static final String TAG = ViewInjection.class.getSimpleName(); |
||||||
|
|
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
public static void inject(View view) { |
||||||
|
|
||||||
|
if (null == view) { |
||||||
|
throw new NullPointerException(); |
||||||
|
} |
||||||
|
|
||||||
|
HasViewInjector hasViewInjector = findHasViewInjectors(view); |
||||||
|
|
||||||
|
Log.d(TAG, String.format("An injector for %s was found in %s", view.getClass().getCanonicalName(), hasViewInjector.getClass().getCanonicalName())); |
||||||
|
|
||||||
|
Map<Class<? extends View>, Provider<ViewComponentBuilder>> viewInjectors = hasViewInjector.viewInjectors(); |
||||||
|
Provider<ViewComponentBuilder> provider = viewInjectors.get(view.getClass()); |
||||||
|
|
||||||
|
if (provider != null) { |
||||||
|
ViewComponentBuilder viewComponentBuilder = provider.get(); |
||||||
|
viewComponentBuilder |
||||||
|
.bindInstance(view) |
||||||
|
.build() |
||||||
|
.injectMembers(view); |
||||||
|
} else { |
||||||
|
throw new NullPointerException("ViewInjection fail "); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private static HasViewInjector findHasViewInjectors(View view) { |
||||||
|
Context context = view.getContext(); |
||||||
|
if (context instanceof HasViewInjector) { |
||||||
|
return (HasViewInjector) context; |
||||||
|
} |
||||||
|
Application application = (Application) context.getApplicationContext(); |
||||||
|
if (application instanceof HasViewInjector) { |
||||||
|
return (HasViewInjector) application; |
||||||
|
} |
||||||
|
throw new IllegalArgumentException(String.format("No injector was found for %s", view.getClass().getCanonicalName())); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
package com.android.base.app.dagger; |
||||||
|
|
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
import dagger.MapKey; |
||||||
|
import dagger.internal.Beta; |
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.METHOD; |
||||||
|
|
||||||
|
@Beta |
||||||
|
@MapKey |
||||||
|
@Target(METHOD) |
||||||
|
@SuppressWarnings("unused") |
||||||
|
public @interface ViewKey { |
||||||
|
Class<? extends View> value(); |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
package com.android.base.app.dagger; |
||||||
|
|
||||||
|
|
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import javax.inject.Inject; |
||||||
|
import javax.inject.Provider; |
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
import androidx.lifecycle.ViewModel; |
||||||
|
import androidx.lifecycle.ViewModelProvider; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@link androidx.lifecycle.AbstractSavedStateViewModelFactory} is not supported. |
||||||
|
* |
||||||
|
* @author Ztiany |
||||||
|
* Email: ztiany3@gmail.com |
||||||
|
* Date : 2018-11-05 14:31 |
||||||
|
*/ |
||||||
|
public class ViewModelFactory implements ViewModelProvider.Factory { |
||||||
|
|
||||||
|
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> mCreatorMap; |
||||||
|
|
||||||
|
@Inject |
||||||
|
ViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> classProviderMap) { |
||||||
|
mCreatorMap = classProviderMap; |
||||||
|
} |
||||||
|
|
||||||
|
@NonNull |
||||||
|
@Override |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { |
||||||
|
Provider<ViewModel> viewModelProvider = mCreatorMap.get(modelClass); |
||||||
|
if (viewModelProvider == null) { |
||||||
|
for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : mCreatorMap.entrySet()) { |
||||||
|
if (modelClass.isAssignableFrom(entry.getKey())) { |
||||||
|
viewModelProvider = entry.getValue(); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (viewModelProvider != null) { |
||||||
|
return (T) viewModelProvider.get(); |
||||||
|
} |
||||||
|
throw new NullPointerException("can not find provider for " + modelClass); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
package com.android.base.app.dagger; |
||||||
|
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel; |
||||||
|
import dagger.MapKey; |
||||||
|
import kotlin.annotation.AnnotationTarget; |
||||||
|
import kotlin.annotation.MustBeDocumented; |
||||||
|
import kotlin.annotation.Retention; |
||||||
|
import kotlin.annotation.Target; |
||||||
|
|
||||||
|
@MustBeDocumented |
||||||
|
@Target(allowedTargets = AnnotationTarget.FUNCTION) |
||||||
|
@Retention() |
||||||
|
@MapKey |
||||||
|
public @interface ViewModelKey { |
||||||
|
|
||||||
|
Class<? extends ViewModel> value(); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
package com.android.base.app.dagger; |
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModelProvider; |
||||||
|
import dagger.Binds; |
||||||
|
import dagger.Module; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@link androidx.lifecycle.AbstractSavedStateViewModelFactory} is not supported. |
||||||
|
* |
||||||
|
* @author Ztiany |
||||||
|
* Email: ztiany3@gmail.com |
||||||
|
* Date : 2018-11-05 16:20 |
||||||
|
*/ |
||||||
|
@Module |
||||||
|
public abstract class ViewModelModule { |
||||||
|
|
||||||
|
@Binds |
||||||
|
@ActivityScope |
||||||
|
abstract ViewModelProvider.Factory provideViewModelFactory(ViewModelFactory viewModelFactory); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
package com.android.base.app.dagger; |
||||||
|
|
||||||
|
import java.lang.annotation.Documented; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
|
||||||
|
import javax.inject.Scope; |
||||||
|
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Ztiany |
||||||
|
* Email: ztiany3@gmail.com |
||||||
|
* Date : 2017-05-23 09:59 |
||||||
|
*/ |
||||||
|
@Scope |
||||||
|
@Documented |
||||||
|
@Retention(RUNTIME) |
||||||
|
public @interface ViewScope { |
||||||
|
} |
@ -0,0 +1,277 @@ |
|||||||
|
package com.android.base.app.fragment |
||||||
|
|
||||||
|
import android.content.Context |
||||||
|
import android.content.Intent |
||||||
|
import android.os.Bundle |
||||||
|
import android.view.LayoutInflater |
||||||
|
import android.view.View |
||||||
|
import android.view.ViewGroup |
||||||
|
import androidx.annotation.StringRes |
||||||
|
import androidx.annotation.UiThread |
||||||
|
import androidx.appcompat.app.AppCompatDialogFragment |
||||||
|
import com.android.base.app.Sword |
||||||
|
import com.android.base.app.activity.BackHandlerHelper |
||||||
|
import com.android.base.app.activity.OnBackPressListener |
||||||
|
import com.android.base.app.fragment.delegates.FragmentDelegate |
||||||
|
import com.android.base.app.fragment.delegates.FragmentDelegateOwner |
||||||
|
import com.android.base.app.ui.LoadingView |
||||||
|
import com.android.base.rx.autodispose.AutoDisposeLifecycleOwnerEx |
||||||
|
import com.github.dmstocking.optional.java.util.function.Predicate |
||||||
|
import timber.log.Timber |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Ztiany |
||||||
|
* date : 2016-03-19 23:09 |
||||||
|
* email: 1169654504@qq.com |
||||||
|
* @see [BaseFragment] |
||||||
|
*/ |
||||||
|
open class BaseDialogFragment : AppCompatDialogFragment(), LoadingView, OnBackPressListener, FragmentDelegateOwner, AutoDisposeLifecycleOwnerEx { |
||||||
|
|
||||||
|
private var loadingView: LoadingView? = null |
||||||
|
|
||||||
|
private var layoutView: View? = null |
||||||
|
|
||||||
|
/** just for cache*/ |
||||||
|
private var cachedView: View? = null |
||||||
|
|
||||||
|
@Suppress("LeakingThis") |
||||||
|
private val fragmentDelegates = FragmentDelegates(this) |
||||||
|
|
||||||
|
private var recentShowingDialogTime: Long = 0 |
||||||
|
|
||||||
|
private var _state: Bundle? = null |
||||||
|
|
||||||
|
protected val state: Bundle |
||||||
|
get() { |
||||||
|
var state = _state |
||||||
|
if (state == null) { |
||||||
|
state = Bundle() |
||||||
|
_state = state |
||||||
|
} |
||||||
|
return state |
||||||
|
} |
||||||
|
|
||||||
|
private fun tag() = this.javaClass.simpleName |
||||||
|
|
||||||
|
override fun onAttach(context: Context) { |
||||||
|
super.onAttach(context) |
||||||
|
Timber.tag(tag()).d("onAttach() called with: context = [$context]") |
||||||
|
fragmentDelegates.onAttach(context) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) { |
||||||
|
super.onCreate(savedInstanceState) |
||||||
|
_state = getInstanceState(savedInstanceState) |
||||||
|
Timber.tag(tag()).d("-->onCreate savedInstanceState = $savedInstanceState") |
||||||
|
fragmentDelegates.onCreate(savedInstanceState) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { |
||||||
|
if (cachedView == null) { |
||||||
|
val layout = provideLayout() ?: return null |
||||||
|
if (layout is Int) { |
||||||
|
return inflater.inflate(layout, container, false).also { cachedView = it } |
||||||
|
} |
||||||
|
if (layout is View) { |
||||||
|
cachedView = layout |
||||||
|
return layout |
||||||
|
} |
||||||
|
throw IllegalArgumentException("Here you should provide a layout id or a View") |
||||||
|
} |
||||||
|
|
||||||
|
Timber.tag(tag()).d("mCachedView.parent: " + cachedView?.parent) |
||||||
|
|
||||||
|
cachedView?.run { |
||||||
|
val viewParent = parent |
||||||
|
if (viewParent != null && viewParent is ViewGroup) { |
||||||
|
viewParent.removeView(this) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return cachedView |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 使用此方法提供的布局,将只会被缓存起来,即此方法将只会被调用一次。 |
||||||
|
* |
||||||
|
* @return provide a layout id or a View |
||||||
|
*/ |
||||||
|
protected open fun provideLayout(): Any? = null |
||||||
|
|
||||||
|
final override fun onViewCreated(view: View, savedInstanceState: Bundle?) { |
||||||
|
super.onViewCreated(view, savedInstanceState) |
||||||
|
Timber.tag(tag()).d("-->onViewCreated savedInstanceState = %s", savedInstanceState) |
||||||
|
if (layoutView !== view) { |
||||||
|
layoutView = view |
||||||
|
internalOnViewPrepared(view, savedInstanceState) |
||||||
|
onViewPrepared(view, savedInstanceState) |
||||||
|
} |
||||||
|
fragmentDelegates.onViewCreated(view, savedInstanceState) |
||||||
|
} |
||||||
|
|
||||||
|
internal open fun internalOnViewPrepared(view: View, savedInstanceState: Bundle?) {} |
||||||
|
|
||||||
|
/** |
||||||
|
* View is prepared, If [androidx.fragment.app.Fragment.onCreateView] return same layout, it will be called once |
||||||
|
* |
||||||
|
* @param view view of fragment |
||||||
|
*/ |
||||||
|
protected open fun onViewPrepared(view: View, savedInstanceState: Bundle?) {} |
||||||
|
|
||||||
|
override fun onActivityCreated(savedInstanceState: Bundle?) { |
||||||
|
super.onActivityCreated(savedInstanceState) |
||||||
|
Timber.tag(tag()).d("-->onActivityCreated savedInstanceState = $savedInstanceState") |
||||||
|
fragmentDelegates.onActivityCreated(savedInstanceState) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onStart() { |
||||||
|
super.onStart() |
||||||
|
Timber.tag(tag()).d("-->onStart") |
||||||
|
fragmentDelegates.onStart() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onResume() { |
||||||
|
super.onResume() |
||||||
|
Timber.tag(tag()).d("-->onResume") |
||||||
|
fragmentDelegates.onResume() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onPause() { |
||||||
|
Timber.tag(tag()).d("-->onPause") |
||||||
|
fragmentDelegates.onPause() |
||||||
|
super.onPause() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onStop() { |
||||||
|
Timber.tag(tag()).d("-->onStop") |
||||||
|
fragmentDelegates.onStop() |
||||||
|
super.onStop() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onDestroyView() { |
||||||
|
Timber.tag(tag()).d("-->onDestroyView") |
||||||
|
fragmentDelegates.onDestroyView() |
||||||
|
super.onDestroyView() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onDestroy() { |
||||||
|
Timber.tag(tag()).d("-->onDestroy") |
||||||
|
fragmentDelegates.onDestroy() |
||||||
|
super.onDestroy() |
||||||
|
dismissLoadingDialog() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onDetach() { |
||||||
|
Timber.tag(tag()).d("-->onDetach") |
||||||
|
fragmentDelegates.onDetach() |
||||||
|
super.onDetach() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) { |
||||||
|
fragmentDelegates.onSaveInstanceState(outState) |
||||||
|
saveInstanceState(outState, _state) |
||||||
|
super.onSaveInstanceState(outState) |
||||||
|
} |
||||||
|
|
||||||
|
override fun setUserVisibleHint(isVisibleToUser: Boolean) { |
||||||
|
super.setUserVisibleHint(isVisibleToUser) |
||||||
|
Timber.tag(tag()).d("-->setUserVisibleHint ==$isVisibleToUser") |
||||||
|
fragmentDelegates.setUserVisibleHint(isVisibleToUser) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onHiddenChanged(hidden: Boolean) { |
||||||
|
super.onHiddenChanged(hidden) |
||||||
|
Timber.tag(tag()).d("-->onHiddenChanged = $hidden") |
||||||
|
fragmentDelegates.onHiddenChanged(hidden) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { |
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults) |
||||||
|
fragmentDelegates.onRequestPermissionsResult(requestCode, permissions, grantResults) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { |
||||||
|
super.onActivityResult(requestCode, resultCode, data) |
||||||
|
fragmentDelegates.onActivityResult(requestCode, resultCode, data) |
||||||
|
} |
||||||
|
|
||||||
|
@UiThread |
||||||
|
override fun addDelegate(fragmentDelegate: FragmentDelegate<*>?) { |
||||||
|
fragmentDelegates.addDelegate(fragmentDelegate) |
||||||
|
} |
||||||
|
|
||||||
|
@UiThread |
||||||
|
override fun removeDelegate(fragmentDelegate: FragmentDelegate<*>?): Boolean { |
||||||
|
return fragmentDelegates.removeDelegate(fragmentDelegate) |
||||||
|
} |
||||||
|
|
||||||
|
override fun findDelegate(predicate: Predicate<FragmentDelegate<*>?>?): FragmentDelegate<*>? { |
||||||
|
return fragmentDelegates.findDelegate(predicate) |
||||||
|
} |
||||||
|
|
||||||
|
final override fun onBackPressed(): Boolean { |
||||||
|
return handleBackPress() || BackHandlerHelper.handleBackPress(this) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Fragment需要自己处理BackPress事件,如果不处理,就交给子Fragment处理。都不处理则由Activity处理 |
||||||
|
*/ |
||||||
|
protected open fun handleBackPress(): Boolean { |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
private fun loadingView(): LoadingView { |
||||||
|
val loadingViewImpl = loadingView |
||||||
|
return if (loadingViewImpl != null) { |
||||||
|
loadingViewImpl |
||||||
|
} else { |
||||||
|
loadingView = onCreateLoadingView() ?: Sword.loadingViewFactory?.invoke(requireContext()) |
||||||
|
loadingView ?: throw NullPointerException("you need to config LoadingViewFactory in Sword or implement onCreateLoadingView.") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected open fun onCreateLoadingView(): LoadingView? { |
||||||
|
return null |
||||||
|
} |
||||||
|
|
||||||
|
override fun showLoadingDialog() { |
||||||
|
recentShowingDialogTime = System.currentTimeMillis() |
||||||
|
loadingView().showLoadingDialog(true) |
||||||
|
} |
||||||
|
|
||||||
|
override fun showLoadingDialog(cancelable: Boolean) { |
||||||
|
recentShowingDialogTime = System.currentTimeMillis() |
||||||
|
loadingView().showLoadingDialog(cancelable) |
||||||
|
} |
||||||
|
|
||||||
|
override fun showLoadingDialog(message: CharSequence, cancelable: Boolean) { |
||||||
|
recentShowingDialogTime = System.currentTimeMillis() |
||||||
|
loadingView().showLoadingDialog(message, cancelable) |
||||||
|
} |
||||||
|
|
||||||
|
override fun showLoadingDialog(@StringRes messageId: Int, cancelable: Boolean) { |
||||||
|
recentShowingDialogTime = System.currentTimeMillis() |
||||||
|
loadingView().showLoadingDialog(messageId, cancelable) |
||||||
|
} |
||||||
|
|
||||||
|
override fun dismissLoadingDialog() { |
||||||
|
loadingView().dismissLoadingDialog() |
||||||
|
} |
||||||
|
|
||||||
|
override fun dismissLoadingDialog(minimumMills: Long, onDismiss: () -> Unit) { |
||||||
|
dismissDialog(recentShowingDialogTime, minimumMills, onDismiss) |
||||||
|
} |
||||||
|
|
||||||
|
override fun isLoadingDialogShowing(): Boolean { |
||||||
|
return loadingView().isLoadingDialogShowing() |
||||||
|
} |
||||||
|
|
||||||
|
override fun showMessage(message: CharSequence) { |
||||||
|
loadingView().showMessage(message) |
||||||
|
} |
||||||
|
|
||||||
|
override fun showMessage(@StringRes messageId: Int) { |
||||||
|
loadingView().showMessage(messageId) |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,295 @@ |
|||||||
|
package com.android.base.app.fragment |
||||||
|
|
||||||
|
import android.content.Context |
||||||
|
import android.content.Intent |
||||||
|
import android.os.Bundle |
||||||
|
import android.view.LayoutInflater |
||||||
|
import android.view.View |
||||||
|
import android.view.ViewGroup |
||||||
|
import android.view.animation.Animation |
||||||
|
import androidx.annotation.StringRes |
||||||
|
import androidx.annotation.UiThread |
||||||
|
import androidx.fragment.app.Fragment |
||||||
|
import com.android.base.app.Sword |
||||||
|
import com.android.base.app.activity.BackHandlerHelper |
||||||
|
import com.android.base.app.activity.OnBackPressListener |
||||||
|
import com.android.base.app.fragment.animator.FragmentAnimatorHelper |
||||||
|
import com.android.base.app.fragment.delegates.FragmentDelegate |
||||||
|
import com.android.base.app.fragment.delegates.FragmentDelegateOwner |
||||||
|
import com.android.base.app.fragment.tools.FragmentConfig |
||||||
|
import com.android.base.app.ui.LoadingView |
||||||
|
import com.android.base.rx.autodispose.AutoDisposeLifecycleOwnerEx |
||||||
|
import com.github.dmstocking.optional.java.util.function.Predicate |
||||||
|
import timber.log.Timber |
||||||
|
|
||||||
|
/** |
||||||
|
*基础 BaseFragment 封装: |
||||||
|
* |
||||||
|
* 1. RxJava 生命周期绑定。 |
||||||
|
* 2. 返回键监听。 |
||||||
|
* 3. 显示 LoadingDialog 和 Message。 |
||||||
|
* 4. 可以添加生命周期代理。 |
||||||
|
* |
||||||
|
* @author Ztiany |
||||||
|
* date : 2016-03-19 23:09 |
||||||
|
* email: 1169654504@qq.com |
||||||
|
*/ |
||||||
|
open class BaseFragment : Fragment(), LoadingView, OnBackPressListener, FragmentDelegateOwner, AutoDisposeLifecycleOwnerEx { |
||||||
|
|
||||||
|
private var loadingView: LoadingView? = null |
||||||
|
|
||||||
|
private var layoutView: View? = null |
||||||
|
|
||||||
|
/** just for cache*/ |
||||||
|
private var cachedView: View? = null |
||||||
|
|
||||||
|
private var fragmentAnimatorHelper: FragmentAnimatorHelper? = null |
||||||
|
|
||||||
|
@Suppress("LeakingThis") |
||||||
|
private val fragmentDelegates = FragmentDelegates(this) |
||||||
|
|
||||||
|
private var recentShowingDialogTime: Long = 0 |
||||||
|
|
||||||
|
private var _state: Bundle? = null |
||||||
|
|
||||||
|
protected val state: Bundle |
||||||
|
get() { |
||||||
|
var state = _state |
||||||
|
if (state == null) { |
||||||
|
state = Bundle() |
||||||
|
_state = state |
||||||
|
} |
||||||
|
return state |
||||||
|
} |
||||||
|
|
||||||
|
private fun tag() = this.javaClass.simpleName |
||||||
|
|
||||||
|
override fun onAttach(context: Context) { |
||||||
|
super.onAttach(context) |
||||||
|
Timber.tag(tag()).d("onAttach() called with: context = [$context]") |
||||||
|
fragmentDelegates.onAttach(context) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) { |
||||||
|
super.onCreate(savedInstanceState) |
||||||
|
_state = getInstanceState(savedInstanceState) |
||||||
|
Timber.tag(tag()).d("-->onCreate savedInstanceState = $savedInstanceState") |
||||||
|
fragmentDelegates.onCreate(savedInstanceState) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { |
||||||
|
if (cachedView == null) { |
||||||
|
val layout = provideLayout() ?: return null |
||||||
|
if (layout is Int) { |
||||||
|
return inflater.inflate(layout, container, false).also { cachedView = it } |
||||||
|
} |
||||||
|
if (layout is View) { |
||||||
|
cachedView = layout |
||||||
|
return layout |
||||||
|
} |
||||||
|
throw IllegalArgumentException("Here you should provide a layout id or a View") |
||||||
|
} |
||||||
|
|
||||||
|
Timber.tag(tag()).d("mCachedView.parent: " + cachedView?.parent) |
||||||
|
|
||||||
|
cachedView?.run { |
||||||
|
val viewParent = parent |
||||||
|
if (viewParent != null && viewParent is ViewGroup) { |
||||||
|
viewParent.removeView(this) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return cachedView |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 使用此方法提供的布局,将只会被缓存起来,即此方法将只会被调用一次。 |
||||||
|
* |
||||||
|
* @return provide a layout id or a View |
||||||
|
*/ |
||||||
|
protected open fun provideLayout(): Any? = null |
||||||
|
|
||||||
|
final override fun onViewCreated(view: View, savedInstanceState: Bundle?) { |
||||||
|
super.onViewCreated(view, savedInstanceState) |
||||||
|
Timber.tag(tag()).d("-->onViewCreated savedInstanceState = %s", savedInstanceState) |
||||||
|
if (layoutView !== view) { |
||||||
|
layoutView = view |
||||||
|
internalOnViewPrepared(view, savedInstanceState) |
||||||
|
onViewPrepared(view, savedInstanceState) |
||||||
|
} |
||||||
|
fragmentDelegates.onViewCreated(view, savedInstanceState) |
||||||
|
} |
||||||
|
|
||||||
|
internal open fun internalOnViewPrepared(view: View, savedInstanceState: Bundle?) {} |
||||||
|
|
||||||
|
/** |
||||||
|
* View is prepared, If [androidx.fragment.app.Fragment.onCreateView] return same layout, it will be called once |
||||||
|
* |
||||||
|
* @param view view of fragment |
||||||
|
*/ |
||||||
|
protected open fun onViewPrepared(view: View, savedInstanceState: Bundle?) {} |
||||||
|
|
||||||
|
override fun onActivityCreated(savedInstanceState: Bundle?) { |
||||||
|
super.onActivityCreated(savedInstanceState) |
||||||
|
Timber.tag(tag()).d("-->onActivityCreated savedInstanceState = $savedInstanceState") |
||||||
|
fragmentDelegates.onActivityCreated(savedInstanceState) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onStart() { |
||||||
|
super.onStart() |
||||||
|
Timber.tag(tag()).d("-->onStart") |
||||||
|
fragmentDelegates.onStart() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onResume() { |
||||||
|
super.onResume() |
||||||
|
Timber.tag(tag()).d("-->onResume") |
||||||
|
fragmentDelegates.onResume() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onPause() { |
||||||
|
Timber.tag(tag()).d("-->onPause") |
||||||
|
fragmentDelegates.onPause() |
||||||
|
super.onPause() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onStop() { |
||||||
|
Timber.tag(tag()).d("-->onStop") |
||||||
|
fragmentDelegates.onStop() |
||||||
|
super.onStop() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onDestroyView() { |
||||||
|
Timber.tag(tag()).d("-->onDestroyView") |
||||||
|
fragmentDelegates.onDestroyView() |
||||||
|
super.onDestroyView() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onDestroy() { |
||||||
|
Timber.tag(tag()).d("-->onDestroy") |
||||||
|
fragmentDelegates.onDestroy() |
||||||
|
super.onDestroy() |
||||||
|
dismissLoadingDialog() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onDetach() { |
||||||
|
Timber.tag(tag()).d("-->onDetach") |
||||||
|
fragmentDelegates.onDetach() |
||||||
|
super.onDetach() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) { |
||||||
|
fragmentDelegates.onSaveInstanceState(outState) |
||||||
|
saveInstanceState(outState, _state) |
||||||
|
super.onSaveInstanceState(outState) |
||||||
|
} |
||||||
|
|
||||||
|
override fun setUserVisibleHint(isVisibleToUser: Boolean) { |
||||||
|
super.setUserVisibleHint(isVisibleToUser) |
||||||
|
Timber.tag(tag()).d("-->setUserVisibleHint ==$isVisibleToUser") |
||||||
|
fragmentDelegates.setUserVisibleHint(isVisibleToUser) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onHiddenChanged(hidden: Boolean) { |
||||||
|
super.onHiddenChanged(hidden) |
||||||
|
Timber.tag(tag()).d("-->onHiddenChanged = $hidden") |
||||||
|
fragmentDelegates.onHiddenChanged(hidden) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { |
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults) |
||||||
|
fragmentDelegates.onRequestPermissionsResult(requestCode, permissions, grantResults) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { |
||||||
|
super.onActivityResult(requestCode, resultCode, data) |
||||||
|
fragmentDelegates.onActivityResult(requestCode, resultCode, data) |
||||||
|
} |
||||||
|
|
||||||
|
@UiThread |
||||||
|
override fun addDelegate(fragmentDelegate: FragmentDelegate<*>?) { |
||||||
|
fragmentDelegates.addDelegate(fragmentDelegate) |
||||||
|
} |
||||||
|
|
||||||
|
@UiThread |
||||||
|
override fun removeDelegate(fragmentDelegate: FragmentDelegate<*>?): Boolean { |
||||||
|
return fragmentDelegates.removeDelegate(fragmentDelegate) |
||||||
|
} |
||||||
|
|
||||||
|
override fun findDelegate(predicate: Predicate<FragmentDelegate<*>?>?): FragmentDelegate<*>? { |
||||||
|
return fragmentDelegates.findDelegate(predicate) |
||||||
|
} |
||||||
|
|
||||||
|
final override fun onBackPressed(): Boolean { |
||||||
|
return handleBackPress() || BackHandlerHelper.handleBackPress(this) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Fragment需要自己处理BackPress事件,如果不处理,就交给子Fragment处理。都不处理则由Activity处理 |
||||||
|
*/ |
||||||
|
protected open fun handleBackPress(): Boolean { |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
private fun loadingView(): LoadingView { |
||||||
|
val loadingViewImpl = loadingView |
||||||
|
return if (loadingViewImpl != null) { |
||||||
|
loadingViewImpl |
||||||
|
} else { |
||||||
|
loadingView = onCreateLoadingView() ?: Sword.loadingViewFactory?.invoke(requireContext()) |
||||||
|
loadingView ?: throw NullPointerException("you need to config LoadingViewFactory in Sword or implement onCreateLoadingView.") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected open fun onCreateLoadingView(): LoadingView? { |
||||||
|
return null |
||||||
|
} |
||||||
|
|
||||||
|
override fun showLoadingDialog() { |
||||||
|
recentShowingDialogTime = System.currentTimeMillis() |
||||||
|
loadingView().showLoadingDialog(true) |
||||||
|
} |
||||||
|
|
||||||
|
override fun showLoadingDialog(cancelable: Boolean) { |
||||||
|
recentShowingDialogTime = System.currentTimeMillis() |
||||||
|
loadingView().showLoadingDialog(cancelable) |
||||||
|
} |
||||||
|
|
||||||
|
override fun showLoadingDialog(message: CharSequence, cancelable: Boolean) { |
||||||
|
recentShowingDialogTime = System.currentTimeMillis() |
||||||
|
loadingView().showLoadingDialog(message, cancelable) |
||||||
|
} |
||||||
|
|
||||||
|
override fun showLoadingDialog(@StringRes messageId: Int, cancelable: Boolean) { |
||||||
|
recentShowingDialogTime = System.currentTimeMillis() |
||||||
|
loadingView().showLoadingDialog(messageId, cancelable) |
||||||
|
} |
||||||
|
|
||||||
|
override fun dismissLoadingDialog() { |
||||||
|
loadingView().dismissLoadingDialog() |
||||||
|
} |
||||||
|
|
||||||
|
override fun dismissLoadingDialog(minimumMills: Long, onDismiss: () -> Unit) { |
||||||
|
dismissDialog(recentShowingDialogTime, minimumMills, onDismiss) |
||||||
|
} |
||||||
|
|
||||||
|
override fun isLoadingDialogShowing(): Boolean { |
||||||
|
return loadingView().isLoadingDialogShowing() |
||||||
|
} |
||||||
|
|
||||||
|
override fun showMessage(message: CharSequence) { |
||||||
|
loadingView().showMessage(message) |
||||||
|
} |
||||||
|
|
||||||
|
override fun showMessage(@StringRes messageId: Int) { |
||||||
|
loadingView().showMessage(messageId) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? { |
||||||
|
if (fragmentAnimatorHelper == null) { |
||||||
|
fragmentAnimatorHelper = FragmentAnimatorHelper(requireContext(), FragmentConfig.defaultFragmentAnimator()) |
||||||
|
} |
||||||
|
return fragmentAnimatorHelper?.onCreateAnimation(transit, enter) |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,89 @@ |
|||||||
|
package com.android.base.app.fragment |
||||||
|
|
||||||
|
import android.os.Bundle |
||||||
|
import androidx.recyclerview.widget.RecyclerView.Adapter |
||||||
|
import com.android.base.adapter.DataManager |
||||||
|
import com.android.base.app.ui.AutoPageNumber |
||||||
|
import com.android.base.app.ui.PageNumber |
||||||
|
import com.android.base.app.ui.RefreshListLayout |
||||||
|
import com.ztiany.loadmore.adapter.ILoadMore |
||||||
|
import com.ztiany.loadmore.adapter.OnLoadMoreListener |
||||||
|
import com.ztiany.loadmore.adapter.WrapperAdapter |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Ztiany |
||||||
|
* date : 2016-03-19 23:09 |
||||||
|
* email: 1169654504@qq.com |
||||||
|
* @see [BaseListFragment] |
||||||
|
*/ |
||||||
|
abstract class BaseListDialogFragment<T> : BaseStateDialogFragment(), RefreshListLayout<T> { |
||||||
|
|
||||||
|
/**加载更多*/ |
||||||
|
private var loadMore: ILoadMore? = null |
||||||
|
|
||||||
|
/**列表数据管理*/ |
||||||
|
private lateinit var dataManager: DataManager<T> |
||||||
|
|
||||||
|
/**分页页码*/ |
||||||
|
private var pageNumber: PageNumber? = null |
||||||
|
|
||||||
|
override fun onActivityCreated(savedInstanceState: Bundle?) { |
||||||
|
super.onActivityCreated(savedInstanceState) |
||||||
|
if (!::dataManager.isInitialized) { |
||||||
|
throw NullPointerException("you need set DataManager") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected fun setDataManager(dataManager: DataManager<T>) { |
||||||
|
this.dataManager = dataManager |
||||||
|
} |
||||||
|
|
||||||
|
protected fun setupLoadMore(recyclerAdapter: Adapter<*>, pageNumber: PageNumber = AutoPageNumber(this, dataManager)): Adapter<*> { |
||||||
|
this.pageNumber = pageNumber |
||||||
|
|
||||||
|
return WrapperAdapter.wrap(recyclerAdapter).apply { |
||||||
|
setOnLoadMoreListener(object : OnLoadMoreListener { |
||||||
|
override fun onLoadMore() { |
||||||
|
this@BaseListDialogFragment.onLoadMore() |
||||||
|
} |
||||||
|
|
||||||
|
override fun canLoadMore(): Boolean { |
||||||
|
return !isRefreshing |
||||||
|
} |
||||||
|
}) |
||||||
|
loadMore = this |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/**call by [.onRefresh] or [.onLoadMore], you can get current loading type from [.isRefreshing] or [.isLoadingMore].*/ |
||||||
|
protected open fun onStartLoad() {} |
||||||
|
|
||||||
|
override fun onRefresh() = onStartLoad() |
||||||
|
|
||||||
|
protected fun onLoadMore() = onStartLoad() |
||||||
|
|
||||||
|
override fun canRefresh() = !isLoadingMore |
||||||
|
|
||||||
|
override fun replaceData(data: List<T>) = dataManager.replaceAll(data) |
||||||
|
|
||||||
|
override fun addData(data: List<T>) = dataManager.addItems(data) |
||||||
|
|
||||||
|
override fun isEmpty(): Boolean = dataManager.isEmpty |
||||||
|
|
||||||
|
override fun isLoadingMore(): Boolean = loadMore != null && loadMore?.isLoadingMore ?: false |
||||||
|
|
||||||
|
override fun getPager(): PageNumber = pageNumber |
||||||
|
?: throw NullPointerException("you need to call setupLoadMore to init pageNumber") |
||||||
|
|
||||||
|
fun loadMoreController(): ILoadMore = loadMore |
||||||
|
?: throw NullPointerException("you need to call setupLoadMore to init loadMoreController") |
||||||
|
|
||||||
|
override fun loadMoreCompleted(hasMore: Boolean) { |
||||||
|
loadMore?.loadCompleted(hasMore) |
||||||
|
} |
||||||
|
|
||||||
|
override fun loadMoreFailed() { |
||||||
|
loadMore?.loadFail() |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,89 @@ |
|||||||
|
package com.android.base.app.fragment |
||||||
|
|
||||||
|
import android.os.Bundle |
||||||
|
import androidx.recyclerview.widget.RecyclerView.Adapter |
||||||
|
import com.android.base.adapter.DataManager |
||||||
|
import com.android.base.app.ui.AutoPageNumber |
||||||
|
import com.android.base.app.ui.PageNumber |
||||||
|
import com.android.base.app.ui.RefreshListLayout |
||||||
|
import com.ztiany.loadmore.adapter.ILoadMore |
||||||
|
import com.ztiany.loadmore.adapter.OnLoadMoreListener |
||||||
|
import com.ztiany.loadmore.adapter.WrapperAdapter |
||||||
|
|
||||||
|
/** |
||||||
|
* 通用的基于 RecyclerView 的列表界面,支持下拉刷新和加载更多。 |
||||||
|
* |
||||||
|
* @param <T> 当前列表使用的数据类型 |
||||||
|
* @author Ztiany |
||||||
|
* date : 2016-03-19 23:09 |
||||||
|
* email: 1169654504@qq.com |
||||||
|
*/ |
||||||
|
abstract class BaseListFragment<T> : BaseStateFragment(), RefreshListLayout<T> { |
||||||
|
|
||||||
|
/**加载更多*/ |
||||||
|
private var loadMore: ILoadMore? = null |
||||||
|
|
||||||
|
/**列表数据管理*/ |
||||||
|
private lateinit var dataManager: DataManager<T> |
||||||
|
|
||||||
|
/**分页页码*/ |
||||||
|
private var pageNumber: PageNumber? = null |
||||||
|
|
||||||
|
override fun onActivityCreated(savedInstanceState: Bundle?) { |
||||||
|
super.onActivityCreated(savedInstanceState) |
||||||
|
if (!::dataManager.isInitialized) { |
||||||
|
throw NullPointerException("you need set DataManager") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected fun setDataManager(dataManager: DataManager<T>) { |
||||||
|
this.dataManager = dataManager |
||||||
|
} |
||||||
|
|
||||||
|
protected fun setupLoadMore(recyclerAdapter: Adapter<*>, pageNumber: PageNumber = AutoPageNumber(this, dataManager)): Adapter<*> { |
||||||
|
this.pageNumber = pageNumber |
||||||
|
|
||||||
|
return WrapperAdapter.wrap(recyclerAdapter).apply { |
||||||
|
setOnLoadMoreListener(object : OnLoadMoreListener { |
||||||
|
override fun onLoadMore() { |
||||||
|
this@BaseListFragment.onLoadMore() |
||||||
|
} |
||||||
|
|
||||||
|
override fun canLoadMore(): Boolean { |
||||||
|
return !isRefreshing |
||||||
|
} |
||||||
|
}) |
||||||
|
loadMore = this |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/**call by [.onRefresh] or [.onLoadMore], you can get current loading type from [.isRefreshing] or [.isLoadingMore].*/ |
||||||
|
protected open fun onStartLoad() {} |
||||||
|
|
||||||
|
override fun onRefresh() = onStartLoad() |
||||||
|
|
||||||
|
protected fun onLoadMore() = onStartLoad() |
||||||
|
|
||||||
|
override fun canRefresh() = !isLoadingMore |
||||||
|
|
||||||
|
override fun replaceData(data: List<T>)= dataManager.replaceAll(data) |
||||||
|
|
||||||
|
override fun addData(data: List<T>) = dataManager.addItems(data) |
||||||
|
|
||||||
|
override fun isEmpty(): Boolean = dataManager.isEmpty |
||||||
|
|
||||||
|
override fun isLoadingMore(): Boolean = loadMore != null && loadMore?.isLoadingMore ?: false |
||||||
|
|
||||||
|
override fun getPager(): PageNumber = pageNumber ?: throw NullPointerException("you need to call setupLoadMore to init pageNumber") |
||||||
|
|
||||||
|
fun loadMoreController(): ILoadMore = loadMore ?: throw NullPointerException("you need to call setupLoadMore to init loadMoreController") |
||||||
|
|
||||||
|
override fun loadMoreCompleted(hasMore: Boolean) { |
||||||
|
loadMore?.loadCompleted(hasMore) |
||||||
|
} |
||||||
|
|
||||||
|
override fun loadMoreFailed() { |
||||||
|
loadMore?.loadFail() |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,119 @@ |
|||||||
|
package com.android.base.app.fragment |
||||||
|
|
||||||
|
import android.os.Bundle |
||||||
|
import android.view.View |
||||||
|
import com.android.base.adapter.DataManager |
||||||
|
import com.android.base.app.ui.AutoPageNumber |
||||||
|
import com.android.base.app.ui.RefreshListLayout |
||||||
|
import com.android.base.app.ui.StateLayoutConfig |
||||||
|
|
||||||
|
/** |
||||||
|
* 区别于 [BaseListFragment] 只能支持 RecyclerView。[BaseListFragment] 采用包装 [androidx.recyclerview.widget.RecyclerView.Adapter] 的方式, |
||||||
|
* 在底部添加 load more view 的 item,来实现加载更多。[BaseListV2DialogFragment] 没有采用此种方式,所以你使用的 RefreshView 应该是支持这下来刷新和加载更多功能的。 |
||||||
|
* |
||||||
|
* 在调用 [BaseListV2DialogFragment] 的 [onActivityCreated] 之前,你应该设置好 [dataManager]。 |
||||||
|
* |
||||||
|
*@author Ztiany |
||||||
|
* Email: ztiany3@gmail.com |
||||||
|
* Date : 2019-03-26 15:06 |
||||||
|
*/ |
||||||
|
abstract class BaseListV2DialogFragment<T> : BaseFragment(), RefreshListLayout<T> { |
||||||
|
|
||||||
|
private lateinit var stateLayout: RefreshLoadMoreStateLayoutImpl |
||||||
|
|
||||||
|
protected open lateinit var dataManager: DataManager<T> |
||||||
|
|
||||||
|
override fun internalOnViewPrepared(view: View, savedInstanceState: Bundle?) { |
||||||
|
stateLayout = RefreshLoadMoreStateLayoutImpl.init(view) |
||||||
|
stateLayout.refreshView.setRefreshHandler { |
||||||
|
onRefresh() |
||||||
|
} |
||||||
|
stateLayout.refreshView.setLoadMoreHandler { |
||||||
|
onLoadMore() |
||||||
|
} |
||||||
|
stateLayout.setStateRetryListener(this::onRetry) |
||||||
|
} |
||||||
|
|
||||||
|
protected open fun onRetry(state: Int) { |
||||||
|
if (!isRefreshing) { |
||||||
|
autoRefresh() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected open fun onRefresh() = onStartLoad() |
||||||
|
|
||||||
|
protected open fun onLoadMore() = onStartLoad() |
||||||
|
|
||||||
|
/** call by [onRefresh] or [onLoadMore], you can get current loading type from [isRefreshing] or [isLoadingMore]. */ |
||||||
|
protected open fun onStartLoad() {} |
||||||
|
|
||||||
|
override fun onActivityCreated(savedInstanceState: Bundle?) { |
||||||
|
super.onActivityCreated(savedInstanceState) |
||||||
|
if (!::dataManager.isInitialized) { |
||||||
|
throw NullPointerException("you need set DataManager") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onDestroyView() { |
||||||
|
super.onDestroyView() |
||||||
|
refreshCompleted() |
||||||
|
} |
||||||
|
|
||||||
|
override fun replaceData(data: MutableList<T>?) { |
||||||
|
dataManager.replaceAll(data) |
||||||
|
} |
||||||
|
|
||||||
|
override fun addData(data: MutableList<T>?) { |
||||||
|
dataManager.addItems(data) |
||||||
|
} |
||||||
|
|
||||||
|
fun setRefreshEnable(enable: Boolean) = stateLayout.refreshView.setRefreshEnable(enable) |
||||||
|
|
||||||
|
fun setLoadMoreEnable(enable: Boolean) = stateLayout.refreshView.setLoadMoreEnable(enable) |
||||||
|
|
||||||
|
override fun getPager() = AutoPageNumber(this, dataManager) |
||||||
|
|
||||||
|
override fun isEmpty() = dataManager.isEmpty |
||||||
|
|
||||||
|
override fun loadMoreCompleted(hasMore: Boolean) = stateLayout.refreshView.loadMoreCompleted(hasMore) |
||||||
|
|
||||||
|
override fun loadMoreFailed() = stateLayout.refreshView.loadMoreFailed() |
||||||
|
|
||||||
|
override fun isRefreshing() = stateLayout.refreshView.isRefreshing |
||||||
|
|
||||||
|
override fun showContentLayout() = stateLayout.showContentLayout() |
||||||
|
|
||||||
|
override fun showLoadingLayout() = stateLayout.showLoadingLayout() |
||||||
|
|
||||||
|
override fun refreshCompleted() = stateLayout.refreshView.refreshCompleted() |
||||||
|
|
||||||
|
override fun showEmptyLayout() = stateLayout.showEmptyLayout() |
||||||
|
|
||||||
|
override fun showErrorLayout() = stateLayout.showErrorLayout() |
||||||
|
|
||||||
|
override fun showRequesting() = stateLayout.showRequesting() |
||||||
|
|
||||||
|
override fun showBlank() = stateLayout.showBlank() |
||||||
|
|
||||||
|
override fun getStateLayoutConfig() = stateLayout.stateLayoutConfig |
||||||
|
|
||||||
|
override fun autoRefresh() = stateLayout.refreshView.autoRefresh() |
||||||
|
|
||||||
|
override fun showNetErrorLayout() = stateLayout.showNetErrorLayout() |
||||||
|
|
||||||
|
override fun showServerErrorLayout() = stateLayout.showServerErrorLayout() |
||||||
|
|
||||||
|
override fun isLoadingMore() = stateLayout.refreshView.isLoadingMore |
||||||
|
|
||||||
|
override fun currentStatus() = stateLayout.currentStatus() |
||||||
|
|
||||||
|
companion object { |
||||||
|
const val CONTENT = StateLayoutConfig.CONTENT |
||||||
|
const val LOADING = StateLayoutConfig.LOADING |
||||||
|
const val ERROR = StateLayoutConfig.ERROR |
||||||
|
const val EMPTY = StateLayoutConfig.EMPTY |
||||||
|
const val NET_ERROR = StateLayoutConfig.NET_ERROR |
||||||
|
const val SERVER_ERROR = StateLayoutConfig.SERVER_ERROR |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,115 @@ |
|||||||
|
package com.android.base.app.fragment |
||||||
|
|
||||||
|
import android.os.Bundle |
||||||
|
import android.view.View |
||||||
|
import com.android.base.adapter.DataManager |
||||||
|
import com.android.base.app.ui.AutoPageNumber |
||||||
|
import com.android.base.app.ui.RefreshListLayout |
||||||
|
import com.android.base.app.ui.StateLayoutConfig |
||||||
|
|
||||||
|
/** |
||||||
|
*@author Ztiany |
||||||
|
* Email: ztiany3@gmail.com |
||||||
|
* Date : 2019-03-26 15:06 |
||||||
|
*@see [BaseListV2Fragment] |
||||||
|
*/ |
||||||
|
abstract class BaseListV2Fragment<T> : BaseDialogFragment(), RefreshListLayout<T> { |
||||||
|
|
||||||
|
private lateinit var stateLayout: RefreshLoadMoreStateLayoutImpl |
||||||
|
|
||||||
|
protected open lateinit var dataManager: DataManager<T> |
||||||
|
|
||||||
|
override fun internalOnViewPrepared(view: View, savedInstanceState: Bundle?) { |
||||||
|
stateLayout = RefreshLoadMoreStateLayoutImpl.init(view) |
||||||
|
stateLayout.refreshView.setRefreshHandler { |
||||||
|
onRefresh() |
||||||
|
} |
||||||
|
stateLayout.refreshView.setLoadMoreHandler { |
||||||
|
onLoadMore() |
||||||
|
} |
||||||
|
stateLayout.setStateRetryListener(this::onRetry) |
||||||
|
} |
||||||
|
|
||||||
|
protected open fun onRetry(state: Int) { |
||||||
|
if (!isRefreshing) { |
||||||
|
autoRefresh() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected open fun onRefresh() = onStartLoad() |
||||||
|
|
||||||
|
protected open fun onLoadMore() = onStartLoad() |
||||||
|
|
||||||
|
/** call by [onRefresh] or [onLoadMore], you can get current loading type from [isRefreshing] or [isLoadingMore]. */ |
||||||
|
protected open fun onStartLoad() {} |
||||||
|
|
||||||
|
override fun onActivityCreated(savedInstanceState: Bundle?) { |
||||||
|
super.onActivityCreated(savedInstanceState) |
||||||
|
if (!::dataManager.isInitialized) { |
||||||
|
throw NullPointerException("you need set DataManager") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onDestroyView() { |
||||||
|
super.onDestroyView() |
||||||
|
refreshCompleted() |
||||||
|
} |
||||||
|
|
||||||
|
override fun replaceData(data: MutableList<T>?) { |
||||||
|
dataManager.replaceAll(data) |
||||||
|
} |
||||||
|
|
||||||
|
override fun addData(data: MutableList<T>?) { |
||||||
|
dataManager.addItems(data) |
||||||
|
} |
||||||
|
|
||||||
|
fun setRefreshEnable(enable: Boolean) = stateLayout.refreshView.setRefreshEnable(enable) |
||||||
|
|
||||||
|
fun setLoadMoreEnable(enable: Boolean) = stateLayout.refreshView.setLoadMoreEnable(enable) |
||||||
|
|
||||||
|
override fun getPager() = AutoPageNumber(this, dataManager) |
||||||
|
|
||||||
|
override fun isEmpty() = dataManager.isEmpty |
||||||
|
|
||||||
|
override fun loadMoreCompleted(hasMore: Boolean) = stateLayout.refreshView.loadMoreCompleted(hasMore) |
||||||
|
|
||||||
|
override fun loadMoreFailed() = stateLayout.refreshView.loadMoreFailed() |
||||||
|
|
||||||
|
override fun isRefreshing() = stateLayout.refreshView.isRefreshing |
||||||
|
|
||||||
|
override fun showContentLayout() = stateLayout.showContentLayout() |
||||||
|
|
||||||
|
override fun showLoadingLayout() = stateLayout.showLoadingLayout() |
||||||
|
|
||||||
|
override fun refreshCompleted() = stateLayout.refreshView.refreshCompleted() |
||||||
|
|
||||||
|
override fun showEmptyLayout() = stateLayout.showEmptyLayout() |
||||||
|
|
||||||
|
override fun showErrorLayout() = stateLayout.showErrorLayout() |
||||||
|
|
||||||
|
override fun showRequesting() = stateLayout.showRequesting() |
||||||
|
|
||||||
|
override fun showBlank() = stateLayout.showBlank() |
||||||
|
|
||||||
|
override fun getStateLayoutConfig() = stateLayout.stateLayoutConfig |
||||||
|
|
||||||
|
override fun autoRefresh() = stateLayout.refreshView.autoRefresh() |
||||||
|
|
||||||
|
override fun showNetErrorLayout() = stateLayout.showNetErrorLayout() |
||||||
|
|
||||||
|
override fun showServerErrorLayout() = stateLayout.showServerErrorLayout() |
||||||
|
|
||||||
|
override fun isLoadingMore() = stateLayout.refreshView.isLoadingMore |
||||||
|
|
||||||
|
override fun currentStatus() = stateLayout.currentStatus() |
||||||
|
|
||||||
|
companion object { |
||||||
|
const val CONTENT = StateLayoutConfig.CONTENT |
||||||
|
const val LOADING = StateLayoutConfig.LOADING |
||||||
|
const val ERROR = StateLayoutConfig.ERROR |
||||||
|
const val EMPTY = StateLayoutConfig.EMPTY |
||||||
|
const val NET_ERROR = StateLayoutConfig.NET_ERROR |
||||||
|
const val SERVER_ERROR = StateLayoutConfig.SERVER_ERROR |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,94 @@ |
|||||||
|
package com.android.base.app.fragment |
||||||
|
|
||||||
|
import android.os.Bundle |
||||||
|
import android.view.View |
||||||
|
import com.android.base.app.ui.RefreshStateLayout |
||||||
|
import com.android.base.app.ui.RefreshView |
||||||
|
import com.android.base.app.ui.StateLayoutConfig |
||||||
|
import com.android.base.utils.common.ifNonNull |
||||||
|
import com.android.base.utils.common.otherwise |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Ztiany |
||||||
|
* date : 2016-03-19 23:09 |
||||||
|
* email: 1169654504@qq.com |
||||||
|
* @see BaseStateFragment |
||||||
|
*/ |
||||||
|
abstract class BaseStateDialogFragment : BaseDialogFragment(), RefreshStateLayout { |
||||||
|
|
||||||
|
private lateinit var stateLayout: RefreshableStateLayoutImpl |
||||||
|
|
||||||
|
override fun internalOnViewPrepared(view: View, savedInstanceState: Bundle?) { |
||||||
|
stateLayout = RefreshableStateLayoutImpl.init(view) |
||||||
|
stateLayout.setRefreshHandler(object : RefreshView.RefreshHandler() { |
||||||
|
override fun onRefresh() { |
||||||
|
this@BaseStateDialogFragment.onRefresh() |
||||||
|
} |
||||||
|
|
||||||
|
override fun canRefresh(): Boolean { |
||||||
|
return this@BaseStateDialogFragment.canRefresh() |
||||||
|
} |
||||||
|
}) |
||||||
|
stateLayout.setStateRetryListenerUnchecked { state -> onRetry(state) } |
||||||
|
} |
||||||
|
|
||||||
|
override fun onDestroyView() { |
||||||
|
super.onDestroyView() |
||||||
|
refreshCompleted() |
||||||
|
} |
||||||
|
|
||||||
|
internal open fun canRefresh() = true |
||||||
|
|
||||||
|
private val refreshView: RefreshView? |
||||||
|
get() = stateLayout.refreshView |
||||||
|
|
||||||
|
protected open fun onRetry(@StateLayoutConfig.RetryableState state: Int) { |
||||||
|
refreshView.ifNonNull { |
||||||
|
if (!this.isRefreshing) { |
||||||
|
autoRefresh() |
||||||
|
} |
||||||
|
} otherwise { |
||||||
|
onRefresh() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected open fun onRefresh() {} |
||||||
|
|
||||||
|
fun setRefreshEnable(enable: Boolean) = refreshView?.setRefreshEnable(enable) |
||||||
|
|
||||||
|
override fun getStateLayoutConfig(): StateLayoutConfig = stateLayout.stateLayoutConfig |
||||||
|
|
||||||
|
override fun isRefreshing() = stateLayout.isRefreshing |
||||||
|
|
||||||
|
override fun refreshCompleted() = stateLayout.refreshCompleted() |
||||||
|
|
||||||
|
override fun autoRefresh() = stateLayout.autoRefresh() |
||||||
|
|
||||||
|
override fun showContentLayout() = stateLayout.showContentLayout() |
||||||
|
|
||||||
|
override fun showLoadingLayout() = stateLayout.showLoadingLayout() |
||||||
|
|
||||||
|
override fun showEmptyLayout() = stateLayout.showEmptyLayout() |
||||||
|
|
||||||
|
override fun showErrorLayout() = stateLayout.showErrorLayout() |
||||||
|
|
||||||
|
override fun showRequesting() = stateLayout.showRequesting() |
||||||
|
|
||||||
|
override fun showBlank() = stateLayout.showBlank() |
||||||
|
|
||||||
|
override fun showNetErrorLayout() = stateLayout.showNetErrorLayout() |
||||||
|
|
||||||
|
override fun showServerErrorLayout() = stateLayout.showServerErrorLayout() |
||||||
|
|
||||||
|
override fun currentStatus() = stateLayout.currentStatus() |
||||||
|
|
||||||
|
companion object { |
||||||
|
const val CONTENT = StateLayoutConfig.CONTENT |
||||||
|
const val LOADING = StateLayoutConfig.LOADING |
||||||
|
const val ERROR = StateLayoutConfig.ERROR |
||||||
|
const val EMPTY = StateLayoutConfig.EMPTY |
||||||
|
const val NET_ERROR = StateLayoutConfig.NET_ERROR |
||||||
|
const val SERVER_ERROR = StateLayoutConfig.SERVER_ERROR |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,101 @@ |
|||||||
|
package com.android.base.app.fragment |
||||||
|
|
||||||
|
import android.os.Bundle |
||||||
|
import android.view.View |
||||||
|
import com.android.base.R |
||||||
|
import com.android.base.app.ui.RefreshStateLayout |
||||||
|
import com.android.base.app.ui.RefreshView |
||||||
|
import com.android.base.app.ui.RefreshView.RefreshHandler |
||||||
|
import com.android.base.app.ui.StateLayoutConfig |
||||||
|
import com.android.base.app.ui.StateLayoutConfig.RetryableState |
||||||
|
import com.android.base.utils.common.ifNonNull |
||||||
|
import com.android.base.utils.common.otherwise |
||||||
|
|
||||||
|
/** |
||||||
|
* 1. 支持显示{CONTENT, LOADING, ERROR, EMPTY}等状态布局、支持下拉刷新 |
||||||
|
* 2. 使用的布局中必须有一个id = [R.id.base_state_layout] 的 Layout,确保 Layout 实现了[com.android.base.app.ui.StateLayout] |
||||||
|
* 3. [RefreshView] (下拉刷新)的 id 必须设置为 :[R.id.base_refresh_layout],没有添加则表示不需要下拉刷新功能 |
||||||
|
* 4. 默认所有重试和下拉刷新都会调用 [onRefresh],子类可以修改该行为 |
||||||
|
* |
||||||
|
* @author Ztiany |
||||||
|
* date : 2016-03-19 23:09 |
||||||
|
* email: 1169654504@qq.com |
||||||
|
*/ |
||||||
|
abstract class BaseStateFragment : BaseFragment(), RefreshStateLayout { |
||||||
|
|
||||||
|
private lateinit var stateLayout: RefreshableStateLayoutImpl |
||||||
|
|
||||||
|
override fun internalOnViewPrepared(view: View, savedInstanceState: Bundle?) { |
||||||
|
stateLayout = RefreshableStateLayoutImpl.init(view) |
||||||
|
stateLayout.setRefreshHandler(object : RefreshHandler() { |
||||||
|
override fun onRefresh() { |
||||||
|
this@BaseStateFragment.onRefresh() |
||||||
|
} |
||||||
|
|
||||||
|
override fun canRefresh(): Boolean { |
||||||
|
return this@BaseStateFragment.canRefresh() |
||||||
|
} |
||||||
|
}) |
||||||
|
stateLayout.setStateRetryListenerUnchecked { state -> onRetry(state) } |
||||||
|
} |
||||||
|
|
||||||
|
override fun onDestroyView() { |
||||||
|
super.onDestroyView() |
||||||
|
refreshCompleted() |
||||||
|
} |
||||||
|
|
||||||
|
internal open fun canRefresh() = true |
||||||
|
|
||||||
|
private val refreshView: RefreshView? |
||||||
|
get() = stateLayout.refreshView |
||||||
|
|
||||||
|
protected open fun onRetry(@RetryableState state: Int) { |
||||||
|
refreshView.ifNonNull { |
||||||
|
if (!this.isRefreshing) { |
||||||
|
autoRefresh() |
||||||
|
} |
||||||
|
} otherwise { |
||||||
|
onRefresh() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected open fun onRefresh() {} |
||||||
|
|
||||||
|
fun setRefreshEnable(enable: Boolean) = refreshView?.setRefreshEnable(enable) |
||||||
|
|
||||||
|
override fun getStateLayoutConfig(): StateLayoutConfig = stateLayout.stateLayoutConfig |
||||||
|
|
||||||
|
override fun isRefreshing() = stateLayout.isRefreshing |
||||||
|
|
||||||
|
override fun refreshCompleted() = stateLayout.refreshCompleted() |
||||||
|
|
||||||
|
override fun autoRefresh() = stateLayout.autoRefresh() |
||||||
|
|
||||||
|
override fun showContentLayout() = stateLayout.showContentLayout() |
||||||
|
|
||||||
|
override fun showLoadingLayout() = stateLayout.showLoadingLayout() |
||||||
|
|
||||||
|
override fun showEmptyLayout() = stateLayout.showEmptyLayout() |
||||||
|
|
||||||
|
override fun showErrorLayout() = stateLayout.showErrorLayout() |
||||||
|
|
||||||
|
override fun showRequesting() = stateLayout.showRequesting() |
||||||
|
|
||||||
|
override fun showBlank() = stateLayout.showBlank() |
||||||
|
|
||||||
|
override fun showNetErrorLayout() = stateLayout.showNetErrorLayout() |
||||||
|
|
||||||
|
override fun showServerErrorLayout() = stateLayout.showServerErrorLayout() |
||||||
|
|
||||||
|
override fun currentStatus() = stateLayout.currentStatus() |
||||||
|
|
||||||
|
companion object { |
||||||
|
const val CONTENT = StateLayoutConfig.CONTENT |
||||||
|
const val LOADING = StateLayoutConfig.LOADING |
||||||
|
const val ERROR = StateLayoutConfig.ERROR |
||||||
|
const val EMPTY = StateLayoutConfig.EMPTY |
||||||
|
const val NET_ERROR = StateLayoutConfig.NET_ERROR |
||||||
|
const val SERVER_ERROR = StateLayoutConfig.SERVER_ERROR |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,169 @@ |
|||||||
|
package com.android.base.app.fragment; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.content.Intent; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
import com.android.base.app.fragment.delegates.FragmentDelegate; |
||||||
|
import com.android.base.app.fragment.delegates.FragmentDelegateOwner; |
||||||
|
import com.github.dmstocking.optional.java.util.function.Predicate; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
import androidx.annotation.Nullable; |
||||||
|
import androidx.annotation.UiThread; |
||||||
|
import androidx.fragment.app.Fragment; |
||||||
|
|
||||||
|
|
||||||
|
@UiThread |
||||||
|
final class FragmentDelegates implements FragmentDelegate<Fragment>, FragmentDelegateOwner { |
||||||
|
|
||||||
|
private final Fragment mDelegateOwner; |
||||||
|
private List<FragmentDelegate> mDelegates = new ArrayList<>(4); |
||||||
|
|
||||||
|
<T extends Fragment & FragmentDelegateOwner> FragmentDelegates(T delegateOwner) { |
||||||
|
mDelegateOwner = delegateOwner; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onAttach(Context context) { |
||||||
|
for (FragmentDelegate fragmentDelegate : mDelegates) { |
||||||
|
fragmentDelegate.onAttach(context); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) { |
||||||
|
for (FragmentDelegate fragmentDelegate : mDelegates) { |
||||||
|
fragmentDelegate.onCreate(savedInstanceState); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { |
||||||
|
for (FragmentDelegate fragmentDelegate : mDelegates) { |
||||||
|
fragmentDelegate.onViewCreated(view, savedInstanceState); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onActivityCreated(@Nullable Bundle savedInstanceState) { |
||||||
|
for (FragmentDelegate fragmentDelegate : mDelegates) { |
||||||
|
fragmentDelegate.onActivityCreated(savedInstanceState); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onStart() { |
||||||
|
for (FragmentDelegate fragmentDelegate : mDelegates) { |
||||||
|
fragmentDelegate.onStart(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onResume() { |
||||||
|
for (FragmentDelegate fragmentDelegate : mDelegates) { |
||||||
|
fragmentDelegate.onResume(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onPause() { |
||||||
|
for (FragmentDelegate fragmentDelegate : mDelegates) { |
||||||
|
fragmentDelegate.onPause(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onStop() { |
||||||
|
for (FragmentDelegate fragmentDelegate : mDelegates) { |
||||||
|
fragmentDelegate.onStop(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onDestroy() { |
||||||
|
for (FragmentDelegate fragmentDelegate : mDelegates) { |
||||||
|
fragmentDelegate.onDestroy(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onDestroyView() { |
||||||
|
for (FragmentDelegate fragmentDelegate : mDelegates) { |
||||||
|
fragmentDelegate.onDestroyView(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onDetach() { |
||||||
|
for (FragmentDelegate fragmentDelegate : mDelegates) { |
||||||
|
fragmentDelegate.onDetach(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setUserVisibleHint(boolean isVisibleToUser) { |
||||||
|
for (FragmentDelegate fragmentDelegate : mDelegates) { |
||||||
|
fragmentDelegate.setUserVisibleHint(isVisibleToUser); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onSaveInstanceState(Bundle savedInstanceState) { |
||||||
|
for (FragmentDelegate fragmentDelegate : mDelegates) { |
||||||
|
fragmentDelegate.onSaveInstanceState(savedInstanceState); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onHiddenChanged(boolean hidden) { |
||||||
|
for (FragmentDelegate fragmentDelegate : mDelegates) { |
||||||
|
fragmentDelegate.onHiddenChanged(hidden); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) { |
||||||
|
for (FragmentDelegate fragmentDelegate : mDelegates) { |
||||||
|
fragmentDelegate.onActivityResult(requestCode, resultCode, data); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { |
||||||
|
for (FragmentDelegate fragmentDelegate : mDelegates) { |
||||||
|
fragmentDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
public void addDelegate(FragmentDelegate fragmentDelegate) { |
||||||
|
mDelegates.add(fragmentDelegate); |
||||||
|
fragmentDelegate.onAttachToFragment(mDelegateOwner); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean removeDelegate(FragmentDelegate fragmentDelegate) { |
||||||
|
boolean remove = mDelegates.remove(fragmentDelegate); |
||||||
|
if (remove) { |
||||||
|
fragmentDelegate.onDetachFromFragment(); |
||||||
|
} |
||||||
|
return remove; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public FragmentDelegate findDelegate(Predicate<FragmentDelegate> predicate) { |
||||||
|
for (FragmentDelegate delegate : mDelegates) { |
||||||
|
if (predicate.test(delegate)) { |
||||||
|
return delegate; |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,85 @@ |
|||||||
|
package com.android.base.app.fragment |
||||||
|
|
||||||
|
import android.graphics.drawable.Drawable |
||||||
|
import android.view.View |
||||||
|
import com.android.base.app.ui.* |
||||||
|
import com.android.base.widget.StateProcessor |
||||||
|
|
||||||
|
internal class RefreshLoadMoreStateLayoutImpl private constructor(layout: View) : StateLayout, StateLayoutConfig { |
||||||
|
|
||||||
|
companion object { |
||||||
|
fun init(view: View): RefreshLoadMoreStateLayoutImpl { |
||||||
|
return RefreshLoadMoreStateLayoutImpl(view) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private var _multiStateView: StateLayout? = layout.findViewById<View>(CommonId.STATE_ID) as? StateLayout |
||||||
|
private var _refreshView: RefreshLoadMoreView |
||||||
|
|
||||||
|
val refreshView: RefreshLoadMoreView |
||||||
|
get() = _refreshView |
||||||
|
|
||||||
|
init { |
||||||
|
val refreshLayout = layout.findViewById<View>(CommonId.REFRESH_ID) |
||||||
|
?: throw NullPointerException("You need to provide a refreshable layout with id R.id.base_refresh_layout in your xml.") |
||||||
|
|
||||||
|
_refreshView = RefreshLoadViewFactory.createRefreshView(refreshLayout) |
||||||
|
} |
||||||
|
|
||||||
|
override fun showLoadingLayout() = checkMultiStateView().showLoadingLayout() |
||||||
|
override fun showContentLayout() = checkMultiStateView().showContentLayout() |
||||||
|
override fun showEmptyLayout() = checkMultiStateView().showEmptyLayout() |
||||||
|
override fun showErrorLayout() = checkMultiStateView().showErrorLayout() |
||||||
|
override fun showRequesting() = checkMultiStateView().showRequesting() |
||||||
|
override fun showBlank() = checkMultiStateView().showBlank() |
||||||
|
override fun showNetErrorLayout() = checkMultiStateView().showNetErrorLayout() |
||||||
|
override fun showServerErrorLayout() = checkMultiStateView().showServerErrorLayout() |
||||||
|
override fun currentStatus() = checkMultiStateView().currentStatus() |
||||||
|
|
||||||
|
override fun getStateLayoutConfig(): StateLayoutConfig = checkMultiStateView().stateLayoutConfig |
||||||
|
|
||||||
|
override fun setStateMessage(state: Int, message: CharSequence?): StateLayoutConfig { |
||||||
|
checkMultiStateView().stateLayoutConfig.setStateMessage(state, message) |
||||||
|
return checkMultiStateView().stateLayoutConfig |
||||||
|
} |
||||||
|
|
||||||
|
override fun setStateIcon(state: Int, drawable: Drawable?): StateLayoutConfig { |
||||||
|
checkMultiStateView().stateLayoutConfig.setStateIcon(state, drawable) |
||||||
|
return checkMultiStateView().stateLayoutConfig |
||||||
|
} |
||||||
|
|
||||||
|
override fun setStateIcon(state: Int, drawableId: Int): StateLayoutConfig { |
||||||
|
checkMultiStateView().stateLayoutConfig.setStateIcon(state, drawableId) |
||||||
|
return checkMultiStateView().stateLayoutConfig |
||||||
|
} |
||||||
|
|
||||||
|
override fun setStateAction(state: Int, actionText: CharSequence?): StateLayoutConfig { |
||||||
|
checkMultiStateView().stateLayoutConfig.setStateAction(state, actionText) |
||||||
|
return checkMultiStateView().stateLayoutConfig |
||||||
|
} |
||||||
|
|
||||||
|
override fun setStateRetryListener(retryActionListener: OnRetryActionListener?): StateLayoutConfig { |
||||||
|
checkMultiStateView().stateLayoutConfig.setStateRetryListener(retryActionListener) |
||||||
|
return checkMultiStateView().stateLayoutConfig |
||||||
|
} |
||||||
|
|
||||||
|
override fun disableOperationWhenRequesting(disable: Boolean): StateLayoutConfig { |
||||||
|
checkMultiStateView().stateLayoutConfig.disableOperationWhenRequesting(disable) |
||||||
|
return checkMultiStateView().stateLayoutConfig |
||||||
|
} |
||||||
|
|
||||||
|
override fun getProcessor(): StateProcessor { |
||||||
|
return checkMultiStateView().stateLayoutConfig.processor |
||||||
|
} |
||||||
|
|
||||||
|
override fun setMessageGravity(state: Int, gravity: Int): StateLayoutConfig { |
||||||
|
checkMultiStateView().stateLayoutConfig.setMessageGravity(state, gravity) |
||||||
|
return this |
||||||
|
} |
||||||
|
|
||||||
|
private fun checkMultiStateView(): StateLayout { |
||||||
|
return _multiStateView |
||||||
|
?: throw IllegalStateException("Calling this function requires defining a view that implements StateLayout in the Layout") |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,207 @@ |
|||||||
|
package com.android.base.app.fragment; |
||||||
|
|
||||||
|
import android.graphics.drawable.Drawable; |
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
import com.android.base.app.ui.OnRetryActionListener; |
||||||
|
import com.android.base.app.ui.RefreshStateLayout; |
||||||
|
import com.android.base.app.ui.RefreshView; |
||||||
|
import com.android.base.app.ui.RefreshViewFactory; |
||||||
|
import com.android.base.app.ui.StateLayout; |
||||||
|
import com.android.base.app.ui.StateLayoutConfig; |
||||||
|
import com.android.base.widget.StateProcessor; |
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes; |
||||||
|
|
||||||
|
import static com.android.base.app.ui.CommonId.REFRESH_ID; |
||||||
|
import static com.android.base.app.ui.CommonId.STATE_ID; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Ztiany |
||||||
|
*/ |
||||||
|
final class RefreshableStateLayoutImpl implements RefreshStateLayout, StateLayoutConfig { |
||||||
|
|
||||||
|
private StateLayout mMultiStateView; |
||||||
|
private RefreshView mRefreshView; |
||||||
|
private RefreshView.RefreshHandler mRefreshHandler; |
||||||
|
|
||||||
|
static RefreshableStateLayoutImpl init(View layoutView) { |
||||||
|
return new RefreshableStateLayoutImpl(layoutView); |
||||||
|
} |
||||||
|
|
||||||
|
private RefreshableStateLayoutImpl(View layoutView) { |
||||||
|
setupBaseUiLogic(layoutView); |
||||||
|
setupRefreshLogic(layoutView); |
||||||
|
} |
||||||
|
|
||||||
|
RefreshView getRefreshView() { |
||||||
|
return mRefreshView; |
||||||
|
} |
||||||
|
|
||||||
|
void setRefreshHandler(RefreshView.RefreshHandler refreshHandler) { |
||||||
|
mRefreshHandler = refreshHandler; |
||||||
|
} |
||||||
|
|
||||||
|
void setStateRetryListenerUnchecked(OnRetryActionListener retryActionListener) { |
||||||
|
if (mMultiStateView != null) { |
||||||
|
setStateRetryListener(retryActionListener); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("all") |
||||||
|
private void setupBaseUiLogic(View layoutView) { |
||||||
|
mMultiStateView = (StateLayout) layoutView.findViewById(STATE_ID); |
||||||
|
} |
||||||
|
|
||||||
|
private void setupRefreshLogic(View layoutView) { |
||||||
|
View refreshLayout = layoutView.findViewById(REFRESH_ID); |
||||||
|
if (refreshLayout == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
mRefreshView = RefreshViewFactory.createRefreshView(refreshLayout); |
||||||
|
mRefreshView.setRefreshHandler(new RefreshView.RefreshHandler() { |
||||||
|
@Override |
||||||
|
public boolean canRefresh() { |
||||||
|
return mRefreshHandler.canRefresh(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onRefresh() { |
||||||
|
mRefreshHandler.onRefresh(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public final void autoRefresh() { |
||||||
|
if (mRefreshView != null) { |
||||||
|
mRefreshView.autoRefresh(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void showLoadingLayout() { |
||||||
|
checkMultiStateView().showLoadingLayout(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void showContentLayout() { |
||||||
|
refreshCompleted(); |
||||||
|
checkMultiStateView().showContentLayout(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void showEmptyLayout() { |
||||||
|
refreshCompleted(); |
||||||
|
checkMultiStateView().showEmptyLayout(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void showErrorLayout() { |
||||||
|
refreshCompleted(); |
||||||
|
checkMultiStateView().showErrorLayout(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void showRequesting() { |
||||||
|
checkMultiStateView().showRequesting(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void showBlank() { |
||||||
|
checkMultiStateView().showBlank(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void showNetErrorLayout() { |
||||||
|
refreshCompleted(); |
||||||
|
checkMultiStateView().showNetErrorLayout(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void showServerErrorLayout() { |
||||||
|
refreshCompleted(); |
||||||
|
checkMultiStateView().showServerErrorLayout(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public StateLayoutConfig getStateLayoutConfig() { |
||||||
|
checkMultiStateView(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int currentStatus() { |
||||||
|
return mMultiStateView.currentStatus(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void refreshCompleted() { |
||||||
|
if (mRefreshView != null) { |
||||||
|
mRefreshView.refreshCompleted(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isRefreshing() { |
||||||
|
if (mRefreshView != null) { |
||||||
|
return mRefreshView.isRefreshing(); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public StateLayoutConfig setStateMessage(@RetryableState int state, CharSequence message) { |
||||||
|
checkMultiStateView().getStateLayoutConfig().setStateMessage(state, message); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public StateLayoutConfig setMessageGravity(int state, int gravity) { |
||||||
|
checkMultiStateView().getStateLayoutConfig().setMessageGravity(state, gravity); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public StateLayoutConfig setStateIcon(@RetryableState int state, Drawable drawable) { |
||||||
|
checkMultiStateView().getStateLayoutConfig().setStateIcon(state, drawable); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public StateLayoutConfig setStateIcon(@RetryableState int state, @DrawableRes int drawableId) { |
||||||
|
checkMultiStateView().getStateLayoutConfig().setStateIcon(state, drawableId); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public StateLayoutConfig setStateAction(@RetryableState int state, CharSequence actionText) { |
||||||
|
checkMultiStateView().getStateLayoutConfig().setStateAction(state, actionText); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public StateLayoutConfig setStateRetryListener(OnRetryActionListener retryActionListener) { |
||||||
|
checkMultiStateView().getStateLayoutConfig().setStateRetryListener(retryActionListener); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public StateLayoutConfig disableOperationWhenRequesting(boolean disable) { |
||||||
|
checkMultiStateView().getStateLayoutConfig().disableOperationWhenRequesting(disable); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public StateProcessor getProcessor() { |
||||||
|
return checkMultiStateView().getStateLayoutConfig().getProcessor(); |
||||||
|
} |
||||||
|
|
||||||
|
private StateLayout checkMultiStateView() { |
||||||
|
if (mMultiStateView == null) { |
||||||
|
throw new IllegalStateException("Calling this function requires defining a view that implements StateLayout in the Layout"); |
||||||
|
} |
||||||
|
return mMultiStateView; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,103 @@ |
|||||||
|
package com.android.base.app.fragment.animator |
||||||
|
|
||||||
|
import android.view.animation.* |
||||||
|
import com.android.base.R |
||||||
|
import com.android.base.utils.BaseUtils |
||||||
|
|
||||||
|
interface FragmentAnimator { |
||||||
|
fun makeOpenEnter(): Animation? |
||||||
|
fun makeOpenExit(): Animation? |
||||||
|
fun makeCloseEnter(): Animation? |
||||||
|
fun makeCloseExit(): Animation? |
||||||
|
} |
||||||
|
|
||||||
|
class DefaultVerticalAnimator : FragmentAnimator { |
||||||
|
override fun makeOpenEnter(): Animation? { |
||||||
|
return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.v_fragment_open_enter) |
||||||
|
} |
||||||
|
|
||||||
|
override fun makeOpenExit(): Animation? { |
||||||
|
return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.v_fragment_open_exit) |
||||||
|
} |
||||||
|
|
||||||
|
override fun makeCloseEnter(): Animation? { |
||||||
|
return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.v_fragment_close_enter) |
||||||
|
} |
||||||
|
|
||||||
|
override fun makeCloseExit(): Animation? { |
||||||
|
return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.v_fragment_close_exit) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class DefaultHorizontalAnimator : FragmentAnimator { |
||||||
|
override fun makeOpenEnter(): Animation? { |
||||||
|
return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.h_fragment_open_enter) |
||||||
|
} |
||||||
|
|
||||||
|
override fun makeOpenExit(): Animation? { |
||||||
|
return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.h_fragment_open_exit) |
||||||
|
} |
||||||
|
|
||||||
|
override fun makeCloseEnter(): Animation? { |
||||||
|
return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.h_fragment_close_enter) |
||||||
|
} |
||||||
|
|
||||||
|
override fun makeCloseExit(): Animation? { |
||||||
|
return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.h_fragment_close_exit) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class DefaultNoAnimator : FragmentAnimator { |
||||||
|
override fun makeOpenEnter(): Animation? { |
||||||
|
return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.base_no_anim) |
||||||
|
} |
||||||
|
|
||||||
|
override fun makeOpenExit(): Animation? { |
||||||
|
return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.base_no_anim) |
||||||
|
} |
||||||
|
|
||||||
|
override fun makeCloseEnter(): Animation? { |
||||||
|
return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.base_no_anim) |
||||||
|
} |
||||||
|
|
||||||
|
override fun makeCloseExit(): Animation? { |
||||||
|
return AnimationUtils.loadAnimation(BaseUtils.getAppContext(), R.anim.base_no_anim) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
class DefaultScaleAnimator : FragmentAnimator { |
||||||
|
override fun makeOpenEnter(): Animation? { |
||||||
|
return makeOpenCloseAnimation(1.125f, 1.0f, 0f, 1f) |
||||||
|
} |
||||||
|
|
||||||
|
override fun makeOpenExit(): Animation? { |
||||||
|
return makeOpenCloseAnimation(1.0f, .975f, 1f, 0f) |
||||||
|
} |
||||||
|
|
||||||
|
override fun makeCloseEnter(): Animation? { |
||||||
|
return makeOpenCloseAnimation(.975f, 1.0f, 0f, 1f) |
||||||
|
} |
||||||
|
|
||||||
|
override fun makeCloseExit(): Animation? { |
||||||
|
return makeOpenCloseAnimation(1.0f, 1.075f, 1f, 0f) |
||||||
|
} |
||||||
|
|
||||||
|
companion object { |
||||||
|
private val DECELERATE_QUINT: Interpolator = DecelerateInterpolator(2.5f) |
||||||
|
private val DECELERATE_CUBIC: Interpolator = DecelerateInterpolator(1.5f) |
||||||
|
private const val ANIM_DUR = 220 |
||||||
|
private fun makeOpenCloseAnimation(startScale: Float, endScale: Float, startAlpha: Float, endAlpha: Float): Animation { |
||||||
|
val set = AnimationSet(false) |
||||||
|
val scale = ScaleAnimation(startScale, endScale, startScale, endScale, Animation.RELATIVE_TO_SELF, .5f, Animation.RELATIVE_TO_SELF, .5f) |
||||||
|
scale.interpolator = DECELERATE_QUINT |
||||||
|
scale.duration = ANIM_DUR.toLong() |
||||||
|
set.addAnimation(scale) |
||||||
|
val alpha = AlphaAnimation(startAlpha, endAlpha) |
||||||
|
alpha.interpolator = DECELERATE_CUBIC |
||||||
|
alpha.duration = ANIM_DUR.toLong() |
||||||
|
set.addAnimation(alpha) |
||||||
|
return set |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,103 @@ |
|||||||
|
package com.android.base.app.fragment.animator; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.view.animation.Animation; |
||||||
|
import android.view.animation.AnimationUtils; |
||||||
|
|
||||||
|
import com.android.base.R; |
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentTransaction; |
||||||
|
|
||||||
|
public final class FragmentAnimatorHelper { |
||||||
|
|
||||||
|
private Context context; |
||||||
|
private FragmentAnimator fragmentAnimator; |
||||||
|
|
||||||
|
public FragmentAnimatorHelper(Context context, FragmentAnimator fragmentAnimator) { |
||||||
|
this.context = context; |
||||||
|
this.fragmentAnimator = fragmentAnimator; |
||||||
|
} |
||||||
|
|
||||||
|
public void changeAnimation(FragmentAnimator fragmentAnimator) { |
||||||
|
this.fragmentAnimator = fragmentAnimator; |
||||||
|
} |
||||||
|
|
||||||
|
public Animation getNoneAnim() { |
||||||
|
return AnimationUtils.loadAnimation(context, R.anim.base_no_anim); |
||||||
|
} |
||||||
|
|
||||||
|
public Animation getNoneAnimFixed() { |
||||||
|
return new Animation() { |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
private Animation initEnterAnim() { |
||||||
|
if (fragmentAnimator == null) { |
||||||
|
return AnimationUtils.loadAnimation(context, R.anim.base_no_anim); |
||||||
|
} else { |
||||||
|
Animation animation = fragmentAnimator.makeOpenEnter(); |
||||||
|
if (animation == null) { |
||||||
|
return AnimationUtils.loadAnimation(context, R.anim.base_no_anim); |
||||||
|
} else { |
||||||
|
return animation; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private Animation initExitAnim() { |
||||||
|
if (fragmentAnimator == null) { |
||||||
|
return AnimationUtils.loadAnimation(context, R.anim.base_no_anim); |
||||||
|
} else { |
||||||
|
Animation animation = fragmentAnimator.makeOpenExit(); |
||||||
|
if (animation == null) { |
||||||
|
return AnimationUtils.loadAnimation(context, R.anim.base_no_anim); |
||||||
|
} else { |
||||||
|
return animation; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private Animation initPopEnterAnim() { |
||||||
|
if (fragmentAnimator == null) { |
||||||
|
return AnimationUtils.loadAnimation(context, R.anim.base_no_anim); |
||||||
|
} else { |
||||||
|
Animation animation = fragmentAnimator.makeCloseEnter(); |
||||||
|
if (animation == null) { |
||||||
|
return AnimationUtils.loadAnimation(context, R.anim.base_no_anim); |
||||||
|
} else { |
||||||
|
return animation; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private Animation initPopExitAnim() { |
||||||
|
if (fragmentAnimator == null) { |
||||||
|
return AnimationUtils.loadAnimation(context, R.anim.base_no_anim); |
||||||
|
} else { |
||||||
|
Animation animation = fragmentAnimator.makeCloseExit(); |
||||||
|
if (animation == null) { |
||||||
|
return AnimationUtils.loadAnimation(context, R.anim.base_no_anim); |
||||||
|
} else { |
||||||
|
return animation; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Animation onCreateAnimation(int transit, boolean enter) { |
||||||
|
if (transit == FragmentTransaction.TRANSIT_FRAGMENT_OPEN) { |
||||||
|
if (enter) { |
||||||
|
return initEnterAnim(); |
||||||
|
} else { |
||||||
|
return initExitAnim(); |
||||||
|
} |
||||||
|
} else if (transit == FragmentTransaction.TRANSIT_FRAGMENT_CLOSE) { |
||||||
|
if (enter) { |
||||||
|
return initPopEnterAnim(); |
||||||
|
} else { |
||||||
|
return initPopExitAnim(); |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,74 @@ |
|||||||
|
package com.android.base.app.fragment.delegates; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.content.Intent; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
import androidx.annotation.Nullable; |
||||||
|
import androidx.fragment.app.Fragment; |
||||||
|
|
||||||
|
public interface FragmentDelegate<T extends Fragment> { |
||||||
|
|
||||||
|
/** |
||||||
|
* 当该Delegate被添加到Fragment中 |
||||||
|
*/ |
||||||
|
default void onAttachToFragment(T fragment) { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 调用此方法时,清除Fragment的引用 |
||||||
|
*/ |
||||||
|
default void onDetachFromFragment() { |
||||||
|
} |
||||||
|
|
||||||
|
default void onAttach(Context context) { |
||||||
|
} |
||||||
|
|
||||||
|
default void onCreate(@Nullable Bundle savedInstanceState) { |
||||||
|
} |
||||||
|
|
||||||
|
default void onActivityCreated(@Nullable Bundle savedInstanceState) { |
||||||
|
} |
||||||
|
|
||||||
|
default void onSaveInstanceState(Bundle savedInstanceState) { |
||||||
|
} |
||||||
|
|
||||||
|
default void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { |
||||||
|
} |
||||||
|
|
||||||
|
default void onStart() { |
||||||
|
} |
||||||
|
|
||||||
|
default void onResume() { |
||||||
|
} |
||||||
|
|
||||||
|
default void onPause() { |
||||||
|
} |
||||||
|
|
||||||
|
default void onStop() { |
||||||
|
} |
||||||
|
|
||||||
|
default void onDestroy() { |
||||||
|
} |
||||||
|
|
||||||
|
default void onDestroyView() { |
||||||
|
} |
||||||
|
|
||||||
|
default void onDetach() { |
||||||
|
} |
||||||
|
|
||||||
|
default void setUserVisibleHint(boolean isVisibleToUser) { |
||||||
|
} |
||||||
|
|
||||||
|
default void onHiddenChanged(boolean hidden) { |
||||||
|
} |
||||||
|
|
||||||
|
default void onActivityResult(int requestCode, int resultCode, Intent data) { |
||||||
|
} |
||||||
|
|
||||||
|
default void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
package com.android.base.app.fragment.delegates; |
||||||
|
|
||||||
|
|
||||||
|
import com.github.dmstocking.optional.java.util.function.Predicate; |
||||||
|
|
||||||
|
import androidx.annotation.UiThread; |
||||||
|
|
||||||
|
@UiThread |
||||||
|
public interface FragmentDelegateOwner { |
||||||
|
|
||||||
|
void addDelegate(FragmentDelegate fragmentDelegate); |
||||||
|
|
||||||
|
boolean removeDelegate(FragmentDelegate fragmentDelegate); |
||||||
|
|
||||||
|
FragmentDelegate findDelegate(Predicate<FragmentDelegate> predicate); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
package com.android.base.app.fragment.delegates |
||||||
|
|
||||||
|
import android.os.Bundle |
||||||
|
import android.view.View |
||||||
|
import androidx.fragment.app.Fragment |
||||||
|
|
||||||
|
/** |
||||||
|
* 用于在ViewPager中实现懒加载的Fragment: |
||||||
|
* |
||||||
|
* - changed-1: Android Support 24 把 setUserVisibleHint 方法放到了Attach 之前调用了,所以请在在构造代码块中设置 LazyDelegate。 |
||||||
|
* |
||||||
|
* @author Ztiany |
||||||
|
* Date : Date : 2016-05-06 15:02 |
||||||
|
* Email: 1169654504@qq.com |
||||||
|
*/ |
||||||
|
open class LazyDelegate private constructor() : FragmentDelegate<Fragment?> { |
||||||
|
|
||||||
|
/** View是否准备好,如果不需要绑定view数据,只是加载网络数据,那么该字段可以去掉 */ |
||||||
|
private var isViewPrepared = false |
||||||
|
|
||||||
|
/** 滑动过来后,View是否可见 */ |
||||||
|
private var isViewVisible = false |
||||||
|
|
||||||
|
private var preparedListener: (() -> Unit)? = null |
||||||
|
|
||||||
|
/** |
||||||
|
* 在这里实现Fragment数据的缓加载. |
||||||
|
* |
||||||
|
* @param isVisibleToUser true 表用户可见,false 表不可见 |
||||||
|
*/ |
||||||
|
override fun setUserVisibleHint(isVisibleToUser: Boolean) { |
||||||
|
if (isVisibleToUser) { |
||||||
|
isViewVisible = true |
||||||
|
onVisible() |
||||||
|
} else { |
||||||
|
isViewVisible = false |
||||||
|
onInvisible() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { |
||||||
|
isViewPrepared = true |
||||||
|
} |
||||||
|
|
||||||
|
override fun onActivityCreated(savedInstanceState: Bundle?) { |
||||||
|
lazyLoad() |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 滑动过来后,界面可见时执行 |
||||||
|
*/ |
||||||
|
protected fun onVisible() { |
||||||
|
lazyLoad() |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 滑动过来后,界面不可见时执行 |
||||||
|
*/ |
||||||
|
protected fun onInvisible() {} |
||||||
|
|
||||||
|
private fun lazyLoad() { |
||||||
|
if (isViewPrepared && isViewVisible) { |
||||||
|
notifyLazyLoad() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 懒加载数据,并在此绑定View数据 |
||||||
|
*/ |
||||||
|
private fun notifyLazyLoad() { |
||||||
|
preparedListener?.invoke() |
||||||
|
} |
||||||
|
|
||||||
|
companion object { |
||||||
|
fun attach(delegateFragment: FragmentDelegateOwner, onPreparedListener: () -> Unit): LazyDelegate { |
||||||
|
val delegate = LazyDelegate() |
||||||
|
delegate.preparedListener = onPreparedListener |
||||||
|
delegateFragment.addDelegate(delegate) |
||||||
|
return delegate |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
class SimpleLazyLoadListener(private val onFirstLoad: () -> Unit) : (() -> Unit) { |
||||||
|
|
||||||
|
private var isCalled = false |
||||||
|
|
||||||
|
override fun invoke() { |
||||||
|
if (!isCalled) { |
||||||
|
onFirstLoad() |
||||||
|
isCalled = true |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,114 @@ |
|||||||
|
package com.android.base.app.fragment.tools |
||||||
|
|
||||||
|
import android.os.Binder |
||||||
|
import android.os.Bundle |
||||||
|
import androidx.core.app.BundleCompat |
||||||
|
import androidx.fragment.app.Fragment |
||||||
|
import kotlin.properties.ReadWriteProperty |
||||||
|
import kotlin.reflect.KProperty |
||||||
|
|
||||||
|
fun <T : Any> fragmentArgument(name: String? = null, defaultValue: T? = null): ReadWriteProperty<Fragment, T> { |
||||||
|
return FragmentArgumentDelegate(name, defaultValue) |
||||||
|
} |
||||||
|
|
||||||
|
fun <T : Any?> nullableFragmentArgument(name: String? = null): ReadWriteProperty<Fragment, T?> { |
||||||
|
return NullableFragmentArgumentDelegate(name) |
||||||
|
} |
||||||
|
|
||||||
|
internal class FragmentArgumentDelegate<T : Any>( |
||||||
|
/**value name*/ |
||||||
|
private val name: String? = null, |
||||||
|
/**default cannot be null*/ |
||||||
|
private val defaultValue: T? = null |
||||||
|
) : ReadWriteProperty<Fragment, T> { |
||||||
|
|
||||||
|
private lateinit var value: T |
||||||
|
|
||||||
|
override operator fun getValue(thisRef: Fragment, property: KProperty<*>): T { |
||||||
|
if (!::value.isInitialized) { |
||||||
|
|
||||||
|
val args = thisRef.arguments |
||||||
|
|
||||||
|
if (args == null) { |
||||||
|
if (defaultValue != null) { |
||||||
|
value = defaultValue |
||||||
|
} else { |
||||||
|
throw IllegalStateException("Cannot read property ${confirmPropertyName(name, property)} if no arguments have been set") |
||||||
|
} |
||||||
|
} else { |
||||||
|
@Suppress("UNCHECKED_CAST") |
||||||
|
value = args.get(confirmPropertyName(name, property)) as? T |
||||||
|
?: throw IllegalStateException("Property ${confirmPropertyName(name, property)} could not be read") |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return value |
||||||
|
} |
||||||
|
|
||||||
|
override operator fun setValue(thisRef: Fragment, property: KProperty<*>, value: T) { |
||||||
|
this.value = value |
||||||
|
saveValue(thisRef, name, property, value) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
internal class NullableFragmentArgumentDelegate<T : Any?>( |
||||||
|
/**value name*/ |
||||||
|
private val name: String? = null |
||||||
|
) : ReadWriteProperty<Fragment, T?> { |
||||||
|
|
||||||
|
private var value: T? = null |
||||||
|
|
||||||
|
override operator fun getValue(thisRef: Fragment, property: KProperty<*>): T? { |
||||||
|
if (value == null) { |
||||||
|
@Suppress("UNCHECKED_CAST") |
||||||
|
value = thisRef.arguments?.get(confirmPropertyName(name, property)) as? T |
||||||
|
} |
||||||
|
|
||||||
|
return value |
||||||
|
} |
||||||
|
|
||||||
|
override operator fun setValue(thisRef: Fragment, property: KProperty<*>, value: T?) { |
||||||
|
this.value = value |
||||||
|
saveValue(thisRef, name, property, value) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private fun confirmPropertyName(name: String?, property: KProperty<*>): String { |
||||||
|
return name ?: property.name |
||||||
|
} |
||||||
|
|
||||||
|
private fun saveValue(thisRef: Fragment, name: String?, property: KProperty<*>, value: Any?) { |
||||||
|
var args = thisRef.arguments |
||||||
|
val key = confirmPropertyName(name, property) |
||||||
|
|
||||||
|
if (value == null) { |
||||||
|
args?.remove(key) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
if (args == null) { |
||||||
|
args = Bundle() |
||||||
|
thisRef.arguments = args |
||||||
|
} |
||||||
|
|
||||||
|
when (value) { |
||||||
|
is String -> args.putString(key, value) |
||||||
|
is Int -> args.putInt(key, value) |
||||||
|
is Short -> args.putShort(key, value) |
||||||
|
is Long -> args.putLong(key, value) |
||||||
|
is Byte -> args.putByte(key, value) |
||||||
|
is ByteArray -> args.putByteArray(key, value) |
||||||
|
is Char -> args.putChar(key, value) |
||||||
|
is CharArray -> args.putCharArray(key, value) |
||||||
|
is CharSequence -> args.putCharSequence(key, value) |
||||||
|
is Float -> args.putFloat(key, value) |
||||||
|
is Bundle -> args.putBundle(key, value) |
||||||
|
is Binder -> BundleCompat.putBinder(args, key, value) |
||||||
|
is android.os.Parcelable -> args.putParcelable(key, value) |
||||||
|
is java.io.Serializable -> args.putSerializable(key, value) |
||||||
|
else -> throw IllegalStateException("Type \"$value\" of property ${property.name} is not supported") |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
package com.android.base.app.fragment.tools; |
||||||
|
|
||||||
|
import com.android.base.app.fragment.animator.FragmentAnimator; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Ztiany |
||||||
|
* Email: ztiany3@gmail.com |
||||||
|
* Date : 2019-03-05 15:25 |
||||||
|
*/ |
||||||
|
public class FragmentConfig { |
||||||
|
|
||||||
|
private static final int INVALIDATE_ID = -1; |
||||||
|
private static int sDefaultContainerId = INVALIDATE_ID; |
||||||
|
private static FragmentAnimator sFragmentAnimator = null; |
||||||
|
|
||||||
|
public static void setDefaultContainerId(int defaultContainerId) { |
||||||
|
sDefaultContainerId = defaultContainerId; |
||||||
|
} |
||||||
|
|
||||||
|
public static int defaultContainerId() { |
||||||
|
if (sDefaultContainerId == INVALIDATE_ID) { |
||||||
|
throw new IllegalStateException("sDefaultContainerId has not set"); |
||||||
|
} |
||||||
|
return sDefaultContainerId; |
||||||
|
} |
||||||
|
|
||||||
|
public static void setDefaultFragmentAnimator(FragmentAnimator animator) { |
||||||
|
sFragmentAnimator = animator; |
||||||
|
} |
||||||
|
|
||||||
|
public static FragmentAnimator defaultFragmentAnimator() { |
||||||
|
return sFragmentAnimator; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,133 @@ |
|||||||
|
package com.android.base.app.fragment.tools; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.os.Bundle; |
||||||
|
|
||||||
|
import java.lang.ref.WeakReference; |
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment; |
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess,unused") |
||||||
|
public class FragmentInfo { |
||||||
|
|
||||||
|
private final int mPageId; |
||||||
|
private final String mTag; |
||||||
|
private final Class<? extends Fragment> mClazz; |
||||||
|
private final int mTitleId; |
||||||
|
private final Bundle mArguments; |
||||||
|
private final boolean mIsToStack; |
||||||
|
private final String mStackName; |
||||||
|
private WeakReference<Fragment> mFragment; |
||||||
|
|
||||||
|
private FragmentInfo(int pageId, String tag, Class<? extends Fragment> clazz, int titleId, Bundle arguments, boolean toStack, String stackName) { |
||||||
|
mPageId = pageId; |
||||||
|
mTag = tag; |
||||||
|
mClazz = clazz; |
||||||
|
mTitleId = titleId; |
||||||
|
mArguments = arguments; |
||||||
|
mIsToStack = toStack; |
||||||
|
mStackName = stackName; |
||||||
|
} |
||||||
|
|
||||||
|
public Fragment getInstance() { |
||||||
|
return mFragment == null ? null : mFragment.get(); |
||||||
|
} |
||||||
|
|
||||||
|
public void setInstance(Fragment fragment) { |
||||||
|
mFragment = new WeakReference<>(fragment); |
||||||
|
} |
||||||
|
|
||||||
|
public Fragment newFragment(Context context) { |
||||||
|
return Fragment.instantiate(context, mClazz.getName(), mArguments); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isToStack() { |
||||||
|
return mIsToStack; |
||||||
|
} |
||||||
|
|
||||||
|
public String getStackName() { |
||||||
|
return mStackName; |
||||||
|
} |
||||||
|
|
||||||
|
public Bundle getArguments() { |
||||||
|
return mArguments; |
||||||
|
} |
||||||
|
|
||||||
|
public int getTitleId() { |
||||||
|
return mTitleId; |
||||||
|
} |
||||||
|
|
||||||
|
public int getPageId() { |
||||||
|
return mPageId; |
||||||
|
} |
||||||
|
|
||||||
|
public String getTag() { |
||||||
|
return mTag; |
||||||
|
} |
||||||
|
|
||||||
|
public Class<? extends Fragment> getClazz() { |
||||||
|
return mClazz; |
||||||
|
} |
||||||
|
|
||||||
|
public static PageBuilder builder() { |
||||||
|
return new PageBuilder(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public static class PageBuilder { |
||||||
|
|
||||||
|
private int mPagerId; |
||||||
|
private String mTag; |
||||||
|
private Class<? extends Fragment> mClazz; |
||||||
|
private int mTitleId; |
||||||
|
private Bundle mArguments; |
||||||
|
private boolean mIsToStack; |
||||||
|
private String mStackName; |
||||||
|
|
||||||
|
public FragmentInfo build() { |
||||||
|
return new FragmentInfo(mPagerId, mTag, mClazz, mTitleId, mArguments, mIsToStack, mStackName); |
||||||
|
} |
||||||
|
|
||||||
|
public PageBuilder pagerId(int pagerId) { |
||||||
|
mPagerId = pagerId; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public PageBuilder tag(String tag) { |
||||||
|
this.mTag = tag; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public PageBuilder clazz(Class<? extends Fragment> clazz) { |
||||||
|
mClazz = clazz; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public PageBuilder titleId(int titleId) { |
||||||
|
mTitleId = titleId; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public PageBuilder arguments(Bundle arguments) { |
||||||
|
mArguments = arguments; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public PageBuilder toStack(boolean toStack) { |
||||||
|
this.mIsToStack = toStack; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 如果需要加入到Stack,建议加上StackName。 |
||||||
|
* |
||||||
|
* @param stackName StackName |
||||||
|
* @return PageBuilder |
||||||
|
*/ |
||||||
|
public PageBuilder stackName(String stackName) { |
||||||
|
mStackName = stackName; |
||||||
|
return this; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,500 @@ |
|||||||
|
@file:JvmName("Fragments") |
||||||
|
|
||||||
|
package com.android.base.app.fragment.tools |
||||||
|
|
||||||
|
|
||||||
|
import android.view.View |
||||||
|
import androidx.annotation.NonNull |
||||||
|
import androidx.fragment.app.Fragment |
||||||
|
import androidx.fragment.app.FragmentActivity |
||||||
|
import androidx.fragment.app.FragmentManager |
||||||
|
import androidx.fragment.app.FragmentTransaction |
||||||
|
import com.android.base.app.activity.ActivityDelegate |
||||||
|
import com.android.base.app.activity.ActivityDelegateOwner |
||||||
|
import com.android.base.app.activity.ActivityState |
||||||
|
import com.android.base.app.fragment.delegates.FragmentDelegate |
||||||
|
import com.android.base.app.fragment.delegates.FragmentDelegateOwner |
||||||
|
import com.android.base.utils.common.javaClassName |
||||||
|
import kotlin.reflect.KClass |
||||||
|
|
||||||
|
/**被此 annotation 标注的方法,表示需要使用 [Fragment] 的全类名作为 [FragmentTransaction] 中相关方法的 flag 参数的实参,比如 add/replace 等*/ |
||||||
|
annotation class UsingFragmentClassNameAsFlag |
||||||
|
|
||||||
|
@JvmOverloads |
||||||
|
fun Fragment.exitFragment(immediate: Boolean = false) { |
||||||
|
activity.exitFragment(immediate) |
||||||
|
} |
||||||
|
|
||||||
|
@JvmOverloads |
||||||
|
fun FragmentActivity?.exitFragment(immediate: Boolean = false) { |
||||||
|
if (this == null) { |
||||||
|
return |
||||||
|
} |
||||||
|
val supportFragmentManager = this.supportFragmentManager |
||||||
|
val backStackEntryCount = supportFragmentManager.backStackEntryCount |
||||||
|
if (backStackEntryCount > 0) { |
||||||
|
if (immediate) { |
||||||
|
supportFragmentManager.popBackStackImmediate() |
||||||
|
} else { |
||||||
|
supportFragmentManager.popBackStack() |
||||||
|
} |
||||||
|
} else { |
||||||
|
this.supportFinishAfterTransition() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param clazz the interface container must implemented |
||||||
|
* @param <T> Type |
||||||
|
* @return the interface context must implemented |
||||||
|
*/ |
||||||
|
fun <T> Fragment.requireContainerImplement(clazz: Class<T>): T? { |
||||||
|
if (clazz.isInstance(parentFragment)) { |
||||||
|
return clazz.cast(parentFragment) |
||||||
|
} |
||||||
|
return if (clazz.isInstance(activity)) { |
||||||
|
clazz.cast(activity) |
||||||
|
} else { |
||||||
|
throw RuntimeException("use this Fragment:$this, Activity or Fragment must impl interface :$clazz") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param clazz the interface context must implemented |
||||||
|
* @param <T> Type |
||||||
|
* @return the interface context must implemented |
||||||
|
*/ |
||||||
|
fun <T> Fragment.requireContextImplement(clazz: Class<T>): T? { |
||||||
|
return if (!clazz.isInstance(activity)) { |
||||||
|
throw RuntimeException("use this Fragment:$this, Activity must impl interface :$clazz") |
||||||
|
} else { |
||||||
|
clazz.cast(activity) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param clazz the interface parent must implemented |
||||||
|
* @param <T> Type |
||||||
|
* @return the interface context must implemented |
||||||
|
*/ |
||||||
|
fun <T> Fragment.requireParentImplement(clazz: Class<T>): T? { |
||||||
|
return if (!clazz.isInstance(parentFragment)) { |
||||||
|
throw RuntimeException("use this Fragment:$this, ParentFragment must impl interface :$clazz") |
||||||
|
} else { |
||||||
|
clazz.cast(parentFragment) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** 使用 [clazz] 的全限定类名作为 tag 查找 Fragment */ |
||||||
|
fun <T : Fragment> FragmentManager.findFragmentByTag(clazz: KClass<T>): T? { |
||||||
|
@Suppress("UNCHECKED_CAST") |
||||||
|
return findFragmentByTag(clazz.java.name) as? T |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Fragment 出栈,包括 flag 对应的 Fragment,如果 flag 对应的 Fragment 不在栈中,此方法什么都不做。 |
||||||
|
*/ |
||||||
|
@UsingFragmentClassNameAsFlag |
||||||
|
fun FragmentManager.popBackUntil(flag: String, immediate: Boolean = false) { |
||||||
|
if (immediate) { |
||||||
|
popBackStackImmediate(flag, FragmentManager.POP_BACK_STACK_INCLUSIVE) |
||||||
|
} else { |
||||||
|
popBackStack(flag, FragmentManager.POP_BACK_STACK_INCLUSIVE) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Fragment 出栈,不包括 flag 对应的 Fragment,如果 flag 对应的 Fragment 不在栈中,此方法什么都不做。 |
||||||
|
*/ |
||||||
|
@UsingFragmentClassNameAsFlag |
||||||
|
fun FragmentManager.popBackTo(flag: String, immediate: Boolean = false) { |
||||||
|
if (immediate) { |
||||||
|
popBackStackImmediate(flag, 0) |
||||||
|
} else { |
||||||
|
popBackStack(flag, 0) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 回到对应的 Fragment,如果 Fragment 在栈中,则该 Fragment 回到栈顶,如果 Fragment 不在栈中,则做一次弹栈操作。 |
||||||
|
*/ |
||||||
|
@UsingFragmentClassNameAsFlag |
||||||
|
fun FragmentManager.backToFragment(flag: String, immediate: Boolean = false) { |
||||||
|
val target = findFragmentByTag(flag) |
||||||
|
|
||||||
|
if (target != null) { |
||||||
|
if (isFragmentInStack(target.javaClass)) { |
||||||
|
if (immediate) { |
||||||
|
popBackStackImmediate(flag, 0) |
||||||
|
} else { |
||||||
|
popBackStack(flag, 0) |
||||||
|
} |
||||||
|
} else { |
||||||
|
popBackStack() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun FragmentManager.clearBackStack(immediate: Boolean = false) { |
||||||
|
if (immediate) { |
||||||
|
this.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) |
||||||
|
} else { |
||||||
|
this.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@UsingFragmentClassNameAsFlag |
||||||
|
fun FragmentManager.isFragmentInStack(clazz: Class<out Fragment>): Boolean { |
||||||
|
val backStackEntryCount = backStackEntryCount |
||||||
|
if (backStackEntryCount == 0) { |
||||||
|
return false |
||||||
|
} |
||||||
|
for (i in 0 until backStackEntryCount) { |
||||||
|
if (clazz.name == getBackStackEntryAt(i).name) { |
||||||
|
return true |
||||||
|
} |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
inline fun FragmentManager.inTransaction(func: EnhanceFragmentTransaction.() -> Unit) { |
||||||
|
val fragmentTransaction = beginTransaction() |
||||||
|
EnhanceFragmentTransaction(this, fragmentTransaction).func() |
||||||
|
fragmentTransaction.commit() |
||||||
|
} |
||||||
|
|
||||||
|
inline fun FragmentActivity.inFragmentTransaction(func: EnhanceFragmentTransaction.() -> Unit) { |
||||||
|
val transaction = supportFragmentManager.beginTransaction() |
||||||
|
EnhanceFragmentTransaction(supportFragmentManager, transaction).func() |
||||||
|
transaction.commit() |
||||||
|
} |
||||||
|
|
||||||
|
fun <T> T.inSafelyFragmentTransaction( |
||||||
|
func: EnhanceFragmentTransaction.() -> Unit |
||||||
|
): Boolean where T : FragmentActivity, T : ActivityDelegateOwner { |
||||||
|
|
||||||
|
var delegate = findDelegate { |
||||||
|
it is SafelyFragmentTransactionActivityDelegate |
||||||
|
} as? SafelyFragmentTransactionActivityDelegate |
||||||
|
|
||||||
|
if (delegate == null) { |
||||||
|
delegate = SafelyFragmentTransactionActivityDelegate() |
||||||
|
addDelegate(delegate) |
||||||
|
} |
||||||
|
|
||||||
|
val transaction = supportFragmentManager.beginTransaction() |
||||||
|
|
||||||
|
EnhanceFragmentTransaction(supportFragmentManager, transaction).func() |
||||||
|
|
||||||
|
return delegate.safeCommit(this, transaction) |
||||||
|
} |
||||||
|
|
||||||
|
inline fun Fragment.inChildFragmentTransaction(func: EnhanceFragmentTransaction.() -> Unit) { |
||||||
|
val transaction = childFragmentManager.beginTransaction() |
||||||
|
EnhanceFragmentTransaction(childFragmentManager, transaction).func() |
||||||
|
transaction.commit() |
||||||
|
} |
||||||
|
|
||||||
|
fun <T> T.inSafelyChildFragmentTransaction( |
||||||
|
func: EnhanceFragmentTransaction.() -> Unit |
||||||
|
): Boolean where T : Fragment, T : FragmentDelegateOwner { |
||||||
|
|
||||||
|
var delegate: SafelyFragmentTransactionFragmentDelegate? = findDelegate { |
||||||
|
it is SafelyFragmentTransactionFragmentDelegate |
||||||
|
} as? SafelyFragmentTransactionFragmentDelegate |
||||||
|
|
||||||
|
if (delegate == null) { |
||||||
|
delegate = SafelyFragmentTransactionFragmentDelegate() |
||||||
|
addDelegate(delegate) |
||||||
|
} |
||||||
|
|
||||||
|
val transaction = childFragmentManager.beginTransaction() |
||||||
|
|
||||||
|
EnhanceFragmentTransaction(childFragmentManager, transaction).func() |
||||||
|
|
||||||
|
return delegate.safeCommit(this, transaction) |
||||||
|
} |
||||||
|
|
||||||
|
private class SafelyFragmentTransactionActivityDelegate : ActivityDelegate<FragmentActivity> { |
||||||
|
|
||||||
|
private val mPendingTransactions = mutableListOf<FragmentTransaction>() |
||||||
|
|
||||||
|
fun safeCommit(@NonNull activityDelegateOwner: ActivityDelegateOwner, @NonNull transaction: FragmentTransaction): Boolean { |
||||||
|
val status = activityDelegateOwner.status |
||||||
|
val isCommitterResumed = (status == ActivityState.CREATE || status == ActivityState.START || status == ActivityState.RESUME) |
||||||
|
|
||||||
|
return if (isCommitterResumed) { |
||||||
|
transaction.commit() |
||||||
|
false |
||||||
|
} else { |
||||||
|
mPendingTransactions.add(transaction) |
||||||
|
true |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onResumeFragments() { |
||||||
|
if (mPendingTransactions.isNotEmpty()) { |
||||||
|
mPendingTransactions.forEach { it.commit() } |
||||||
|
mPendingTransactions.clear() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private class SafelyFragmentTransactionFragmentDelegate : FragmentDelegate<Fragment> { |
||||||
|
|
||||||
|
private val pendingTransactions = mutableListOf<FragmentTransaction>() |
||||||
|
|
||||||
|
fun safeCommit(@NonNull fragment: Fragment, @NonNull transaction: FragmentTransaction): Boolean { |
||||||
|
return if (fragment.isResumed) { |
||||||
|
transaction.commit() |
||||||
|
false |
||||||
|
} else { |
||||||
|
pendingTransactions.add(transaction) |
||||||
|
true |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onResume() { |
||||||
|
if (pendingTransactions.isNotEmpty()) { |
||||||
|
pendingTransactions.forEach { it.commit() } |
||||||
|
pendingTransactions.clear() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
class EnhanceFragmentTransaction constructor( |
||||||
|
private val fragmentManager: FragmentManager, |
||||||
|
private val fragmentTransaction: FragmentTransaction |
||||||
|
) : FragmentTransaction() { |
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------------------ |
||||||
|
// extra functions |
||||||
|
//------------------------------------------------------------------------------------------------ |
||||||
|
|
||||||
|
/** |
||||||
|
* 把 [fragment] 添加到回退栈中,并 hide 其他 fragment, |
||||||
|
* 如果 [containerId]==0,则使用 [com.android.base.app.Sword.setDefaultFragmentContainerId] 中配置的 id, |
||||||
|
* 如果 [tag] ==null 则使用 fragment 对应 class 的全限定类名。 |
||||||
|
*/ |
||||||
|
fun addWithStack(containerId: Int = 0, fragment: Fragment, tag: String? = null, transition: Boolean = true): EnhanceFragmentTransaction { |
||||||
|
//set add to stack |
||||||
|
val nonnullTag = (tag ?: fragment.javaClassName()) |
||||||
|
addToBackStack(nonnullTag) |
||||||
|
//add |
||||||
|
fragmentTransaction.add(confirmLayoutId(containerId), fragment, nonnullTag) |
||||||
|
//hide top |
||||||
|
hideTopFragment() |
||||||
|
if (transition) { |
||||||
|
//set a transition |
||||||
|
setOpeningTransition() |
||||||
|
} |
||||||
|
return this |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* replace 方式把 [fragment] 添加到回退栈中,如果 [containerId]==0,则使用 [com.android.base.app.Sword.setDefaultFragmentContainerId] 中配置的 id, |
||||||
|
* 如果 [tag] ==null 则使用 fragment 对应 class 的全限定类名。 |
||||||
|
*/ |
||||||
|
fun replaceWithStack(containerId: Int = 0, fragment: Fragment, tag: String? = null, transition: Boolean = true): EnhanceFragmentTransaction { |
||||||
|
//set add to stack |
||||||
|
val nonnullTag = (tag ?: fragment.javaClassName()) |
||||||
|
addToBackStack(nonnullTag) |
||||||
|
//add |
||||||
|
fragmentTransaction.replace(confirmLayoutId(containerId), fragment, nonnullTag) |
||||||
|
//set a transition |
||||||
|
if (transition) { |
||||||
|
setOpeningTransition() |
||||||
|
} |
||||||
|
return this |
||||||
|
} |
||||||
|
|
||||||
|
private fun confirmLayoutId(layoutId: Int): Int { |
||||||
|
return if (layoutId == 0) { |
||||||
|
FragmentConfig.defaultContainerId() |
||||||
|
} else { |
||||||
|
layoutId |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 添加 [fragment],默认使用 [com.android.base.app.Sword.setDefaultFragmentContainerId] 中配置的 id,如果 [tag] 为null,则使用 [fragment] 的全限定类。 |
||||||
|
*/ |
||||||
|
fun addFragment(fragment: Fragment, tag: String? = null): FragmentTransaction { |
||||||
|
val nonnullTag = (tag ?: fragment.javaClassName()) |
||||||
|
return fragmentTransaction.add(FragmentConfig.defaultContainerId(), fragment, nonnullTag) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 替换为 [fragment],id 使用 [com.android.base.app.Sword.setDefaultFragmentContainerId] 中配置的 id,如果 [tag] 为null,则使用 [fragment] 的全限定类名。 |
||||||
|
*/ |
||||||
|
fun replaceFragment(fragment: Fragment, tag: String? = null, transition: Boolean = true): FragmentTransaction { |
||||||
|
val nonnullTag = (tag ?: fragment.javaClassName()) |
||||||
|
if (transition) { |
||||||
|
setOpeningTransition() |
||||||
|
} |
||||||
|
return fragmentTransaction.replace(FragmentConfig.defaultContainerId(), fragment, nonnullTag) |
||||||
|
} |
||||||
|
|
||||||
|
/**隐藏所有的 fragment */ |
||||||
|
private fun hideFragments() { |
||||||
|
for (fragment in fragmentManager.fragments) { |
||||||
|
if (fragment != null && fragment.isVisible) { |
||||||
|
fragmentTransaction.hide(fragment) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/**隐藏第一个可见的 fragment */ |
||||||
|
private fun hideTopFragment() { |
||||||
|
fragmentManager.fragments.lastOrNull { it.isVisible }?.let { |
||||||
|
fragmentTransaction.hide(it) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun setOpeningTransition(): FragmentTransaction { |
||||||
|
return fragmentTransaction.setTransition(TRANSIT_FRAGMENT_OPEN) |
||||||
|
} |
||||||
|
|
||||||
|
fun setClosingTransition(): FragmentTransaction { |
||||||
|
return fragmentTransaction.setTransition(TRANSIT_FRAGMENT_CLOSE) |
||||||
|
} |
||||||
|
|
||||||
|
fun setFadingTransition(): FragmentTransaction { |
||||||
|
return fragmentTransaction.setTransition(TRANSIT_FRAGMENT_FADE) |
||||||
|
} |
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------------------ |
||||||
|
// original functions |
||||||
|
//------------------------------------------------------------------------------------------------ |
||||||
|
override fun setBreadCrumbShortTitle(res: Int): FragmentTransaction { |
||||||
|
return fragmentTransaction.setBreadCrumbShortTitle(res) |
||||||
|
} |
||||||
|
|
||||||
|
override fun setBreadCrumbShortTitle(text: CharSequence?): FragmentTransaction { |
||||||
|
return fragmentTransaction.setBreadCrumbShortTitle(text) |
||||||
|
} |
||||||
|
|
||||||
|
override fun setPrimaryNavigationFragment(fragment: Fragment?): FragmentTransaction { |
||||||
|
return fragmentTransaction.setPrimaryNavigationFragment(fragment) |
||||||
|
} |
||||||
|
|
||||||
|
override fun runOnCommit(runnable: Runnable): FragmentTransaction { |
||||||
|
return fragmentTransaction.runOnCommit(runnable) |
||||||
|
} |
||||||
|
|
||||||
|
override fun add(fragment: Fragment, tag: String?): FragmentTransaction { |
||||||
|
return fragmentTransaction.add(fragment, tag) |
||||||
|
} |
||||||
|
|
||||||
|
override fun add(containerViewId: Int, fragment: Fragment): FragmentTransaction { |
||||||
|
return fragmentTransaction.add(containerViewId, fragment) |
||||||
|
} |
||||||
|
|
||||||
|
override fun add(containerViewId: Int, fragment: Fragment, tag: String?): FragmentTransaction { |
||||||
|
return fragmentTransaction.add(containerViewId, fragment, tag) |
||||||
|
} |
||||||
|
|
||||||
|
override fun hide(fragment: Fragment): FragmentTransaction { |
||||||
|
return fragmentTransaction.hide(fragment) |
||||||
|
} |
||||||
|
|
||||||
|
override fun replace(containerViewId: Int, fragment: Fragment): FragmentTransaction { |
||||||
|
return fragmentTransaction.replace(containerViewId, fragment) |
||||||
|
} |
||||||
|
|
||||||
|
override fun replace(containerViewId: Int, fragment: Fragment, tag: String?): FragmentTransaction { |
||||||
|
return fragmentTransaction.replace(containerViewId, fragment, tag) |
||||||
|
} |
||||||
|
|
||||||
|
override fun detach(fragment: Fragment): FragmentTransaction { |
||||||
|
return fragmentTransaction.detach(fragment) |
||||||
|
} |
||||||
|
|
||||||
|
@Deprecated("") |
||||||
|
override fun setAllowOptimization(allowOptimization: Boolean): FragmentTransaction { |
||||||
|
return fragmentTransaction.setAllowOptimization(allowOptimization) |
||||||
|
} |
||||||
|
|
||||||
|
override fun setCustomAnimations(enter: Int, exit: Int): FragmentTransaction { |
||||||
|
return fragmentTransaction.setCustomAnimations(enter, exit) |
||||||
|
} |
||||||
|
|
||||||
|
override fun setCustomAnimations(enter: Int, exit: Int, popEnter: Int, popExit: Int): FragmentTransaction { |
||||||
|
return fragmentTransaction.setCustomAnimations(enter, exit, popEnter, popExit) |
||||||
|
} |
||||||
|
|
||||||
|
override fun addToBackStack(name: String?): FragmentTransaction { |
||||||
|
return fragmentTransaction.addToBackStack(name) |
||||||
|
} |
||||||
|
|
||||||
|
override fun disallowAddToBackStack(): FragmentTransaction { |
||||||
|
return fragmentTransaction.disallowAddToBackStack() |
||||||
|
} |
||||||
|
|
||||||
|
override fun setTransitionStyle(styleRes: Int): FragmentTransaction { |
||||||
|
return fragmentTransaction.setTransitionStyle(styleRes) |
||||||
|
} |
||||||
|
|
||||||
|
override fun setTransition(transit: Int): FragmentTransaction { |
||||||
|
return fragmentTransaction.setTransition(transit) |
||||||
|
} |
||||||
|
|
||||||
|
override fun attach(fragment: Fragment): FragmentTransaction { |
||||||
|
return fragmentTransaction.attach(fragment) |
||||||
|
} |
||||||
|
|
||||||
|
override fun show(fragment: Fragment): FragmentTransaction { |
||||||
|
return fragmentTransaction.show(fragment) |
||||||
|
} |
||||||
|
|
||||||
|
override fun isEmpty(): Boolean { |
||||||
|
return fragmentTransaction.isEmpty |
||||||
|
} |
||||||
|
|
||||||
|
override fun remove(fragment: Fragment): FragmentTransaction { |
||||||
|
return fragmentTransaction.remove(fragment) |
||||||
|
} |
||||||
|
|
||||||
|
override fun isAddToBackStackAllowed(): Boolean { |
||||||
|
return fragmentTransaction.isAddToBackStackAllowed |
||||||
|
} |
||||||
|
|
||||||
|
override fun addSharedElement(sharedElement: View, name: String): FragmentTransaction { |
||||||
|
return fragmentTransaction.addSharedElement(sharedElement, name) |
||||||
|
} |
||||||
|
|
||||||
|
override fun setBreadCrumbTitle(res: Int): FragmentTransaction { |
||||||
|
return fragmentTransaction.setBreadCrumbTitle(res) |
||||||
|
} |
||||||
|
|
||||||
|
override fun setBreadCrumbTitle(text: CharSequence?): FragmentTransaction { |
||||||
|
return fragmentTransaction.setBreadCrumbTitle(text) |
||||||
|
} |
||||||
|
|
||||||
|
override fun setReorderingAllowed(reorderingAllowed: Boolean): FragmentTransaction { |
||||||
|
return fragmentTransaction.setReorderingAllowed(reorderingAllowed) |
||||||
|
} |
||||||
|
|
||||||
|
@Deprecated("commit will be called automatically") |
||||||
|
override fun commit(): Int { |
||||||
|
throw UnsupportedOperationException("commit will be called automatically") |
||||||
|
} |
||||||
|
|
||||||
|
@Deprecated("commitAllowingStateLoss will be called automatically") |
||||||
|
override fun commitAllowingStateLoss(): Int { |
||||||
|
throw UnsupportedOperationException("commitAllowingStateLoss will be called automatically") |
||||||
|
} |
||||||
|
|
||||||
|
@Deprecated("commitNow will be called automatically", ReplaceWith("throw UnsupportedOperationException(\"commitNow will be called automatically\")")) |
||||||
|
override fun commitNow() { |
||||||
|
throw UnsupportedOperationException("commitNow will be called automatically") |
||||||
|
} |
||||||
|
|
||||||
|
@Deprecated("commitNowAllowingStateLoss will be called automatically", ReplaceWith("throw UnsupportedOperationException(\"commitNowAllowingStateLoss will be called automatically\")")) |
||||||
|
override fun commitNowAllowingStateLoss() { |
||||||
|
throw UnsupportedOperationException("commitNowAllowingStateLoss will be called automatically") |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,211 @@ |
|||||||
|
package com.android.base.app.fragment.tools; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.os.Bundle; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment; |
||||||
|
import androidx.fragment.app.FragmentManager; |
||||||
|
import androidx.fragment.app.FragmentTransaction; |
||||||
|
|
||||||
|
public abstract class TabManager { |
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess") public static final int ATTACH_DETACH = 1; |
||||||
|
@SuppressWarnings("WeakerAccess") public static final int SHOW_HIDE = 2; |
||||||
|
|
||||||
|
private final FragmentManager mFragmentManager; |
||||||
|
private final int mContainerId; |
||||||
|
private final Tabs mMainTabs; |
||||||
|
private final Context mContext; |
||||||
|
|
||||||
|
private FragmentInfo mCurrentFragmentInfo; |
||||||
|
|
||||||
|
private static final String CURRENT_ID_KET = "main_tab_id"; |
||||||
|
private static final int NONE = -1; |
||||||
|
private int mCurrentId = NONE; |
||||||
|
private final int mOperationType; |
||||||
|
|
||||||
|
public TabManager(Context context, FragmentManager fragmentManager, Tabs tabs, int containerId) { |
||||||
|
this(context, fragmentManager, tabs, containerId, ATTACH_DETACH); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param operationType {@link #ATTACH_DETACH} or {@link #SHOW_HIDE} |
||||||
|
*/ |
||||||
|
public TabManager(Context context, FragmentManager fragmentManager, Tabs tabs, int containerId, int operationType) { |
||||||
|
if (operationType != ATTACH_DETACH && operationType != SHOW_HIDE) { |
||||||
|
throw new IllegalArgumentException("the operationType must be ATTACH_DETACH or SHOW_HIDE"); |
||||||
|
} |
||||||
|
mMainTabs = tabs; |
||||||
|
mContainerId = containerId; |
||||||
|
mContext = context; |
||||||
|
mFragmentManager = fragmentManager; |
||||||
|
mOperationType = operationType; |
||||||
|
} |
||||||
|
|
||||||
|
public final void setup(Bundle bundle) { |
||||||
|
int pageId = mMainTabs.homePage().getPageId(); |
||||||
|
if (bundle == null) { |
||||||
|
switchPage(pageId); |
||||||
|
} else { |
||||||
|
mCurrentId = bundle.getInt(CURRENT_ID_KET, pageId); |
||||||
|
restoreState(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void restoreState() { |
||||||
|
List<FragmentInfo> pages = mMainTabs.getPages(); |
||||||
|
for (FragmentInfo page : pages) { |
||||||
|
page.setInstance(mFragmentManager.findFragmentByTag(page.getTag())); |
||||||
|
if (mCurrentId == page.getPageId()) { |
||||||
|
mCurrentFragmentInfo = page; |
||||||
|
} |
||||||
|
} |
||||||
|
if (mCurrentId == NONE) { |
||||||
|
doChangeTab(mMainTabs.homePage().getPageId()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void switchPage(int pageId) { |
||||||
|
if (mCurrentId == pageId) { |
||||||
|
return; |
||||||
|
} |
||||||
|
FragmentTransaction ft = null; |
||||||
|
if (mCurrentFragmentInfo != null) { |
||||||
|
Fragment fragment = mCurrentFragmentInfo.getInstance(); |
||||||
|
if (fragment != null) { |
||||||
|
ft = mFragmentManager.beginTransaction(); |
||||||
|
hideOrDetach(ft, fragment); |
||||||
|
} |
||||||
|
} |
||||||
|
if (ft != null) { |
||||||
|
ft.commit(); |
||||||
|
} |
||||||
|
doChangeTab(pageId); |
||||||
|
} |
||||||
|
|
||||||
|
private void hideOrDetach(FragmentTransaction ft, Fragment fragment) { |
||||||
|
if (mOperationType == SHOW_HIDE) { |
||||||
|
ft.hide(fragment); |
||||||
|
} else { |
||||||
|
ft.detach(fragment); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void showOrAttach(FragmentTransaction fragmentTransaction, Fragment fragment) { |
||||||
|
if (mOperationType == SHOW_HIDE) { |
||||||
|
fragmentTransaction.show(fragment); |
||||||
|
} else { |
||||||
|
fragmentTransaction.attach(fragment); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void doChangeTab(int fragmentId) { |
||||||
|
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); |
||||||
|
FragmentInfo fragmentInfo = mMainTabs.getFragmentInfo(fragmentId); |
||||||
|
Fragment fragment = fragmentInfo.getInstance(); |
||||||
|
if (fragment != null) { |
||||||
|
showOrAttach(fragmentTransaction, fragment); |
||||||
|
} else { |
||||||
|
Fragment newFragment = fragmentInfo.newFragment(mContext); |
||||||
|
fragmentInfo.setInstance(newFragment); |
||||||
|
onFragmentCreated(fragmentId, newFragment); |
||||||
|
fragmentTransaction.add(mContainerId, newFragment, fragmentInfo.getTag()); |
||||||
|
} |
||||||
|
mCurrentFragmentInfo = fragmentInfo; |
||||||
|
mCurrentId = fragmentId; |
||||||
|
fragmentTransaction.commit(); |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
public void selectTabByPosition(int position) { |
||||||
|
switchPage(mMainTabs.getIdByPosition(position)); |
||||||
|
} |
||||||
|
|
||||||
|
public void selectTabById(int tabId) { |
||||||
|
selectTabByPosition(mMainTabs.getPositionById(tabId)); |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("unused") |
||||||
|
public int getCurrentPosition() { |
||||||
|
return mMainTabs.getPositionById(mCurrentId); |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess,unused") |
||||||
|
protected void onFragmentCreated(int id, Fragment newFragment) { |
||||||
|
} |
||||||
|
|
||||||
|
public void onSaveInstanceState(Bundle bundle) { |
||||||
|
bundle.putInt(CURRENT_ID_KET, mCurrentId); |
||||||
|
} |
||||||
|
|
||||||
|
public static abstract class Tabs { |
||||||
|
|
||||||
|
private final List<FragmentInfo> mPages; |
||||||
|
|
||||||
|
protected Tabs() { |
||||||
|
mPages = new ArrayList<>(); |
||||||
|
} |
||||||
|
|
||||||
|
protected void add(FragmentInfo page) { |
||||||
|
mPages.add(page); |
||||||
|
} |
||||||
|
|
||||||
|
FragmentInfo homePage() { |
||||||
|
return mPages.get(defaultIndex()); |
||||||
|
} |
||||||
|
|
||||||
|
protected int defaultIndex() { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
public int size() { |
||||||
|
return mPages.size(); |
||||||
|
} |
||||||
|
|
||||||
|
FragmentInfo getFragmentInfo(int id) { |
||||||
|
for (FragmentInfo page : mPages) { |
||||||
|
if (page.getPageId() == id) { |
||||||
|
return page; |
||||||
|
} |
||||||
|
} |
||||||
|
throw new IllegalArgumentException("MainPages not has this pageId :" + id); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param clazz Fragment对应的clazz |
||||||
|
* @return pagerId ,没有则返回-1 |
||||||
|
*/ |
||||||
|
@SuppressWarnings("unused") |
||||||
|
int getIdByClazz(Class<? extends Fragment> clazz) { |
||||||
|
for (FragmentInfo page : mPages) { |
||||||
|
if (page.getClazz() == clazz) { |
||||||
|
return page.getPageId(); |
||||||
|
} |
||||||
|
} |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
List<FragmentInfo> getPages() { |
||||||
|
return Collections.unmodifiableList(mPages); |
||||||
|
} |
||||||
|
|
||||||
|
private int getPositionById(int tabId) { |
||||||
|
int size = mPages.size(); |
||||||
|
for (int i = 0; i < size; i++) { |
||||||
|
if (mPages.get(i).getPageId() == tabId) { |
||||||
|
return i; |
||||||
|
} |
||||||
|
} |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
private int getIdByPosition(int position) { |
||||||
|
return mPages.get(position).getPageId(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,52 @@ |
|||||||
|
package com.android.base.app.fragment |
||||||
|
|
||||||
|
import android.os.Bundle |
||||||
|
import androidx.lifecycle.LifecycleOwner |
||||||
|
import androidx.lifecycle.lifecycleScope |
||||||
|
import com.android.base.app.ui.LoadingView |
||||||
|
import kotlinx.coroutines.CancellationException |
||||||
|
import kotlinx.coroutines.delay |
||||||
|
import kotlinx.coroutines.launch |
||||||
|
|
||||||
|
/** |
||||||
|
*@author Ztiany |
||||||
|
* Email: ztiany3@gmail.com |
||||||
|
* Date : 2019-10-16 17:28 |
||||||
|
*/ |
||||||
|
internal fun <T> T.dismissDialog(recentShowingDialogTime: Long, minimumMills: Long, onDismiss: () -> Unit) where T : LoadingView, T : LifecycleOwner { |
||||||
|
|
||||||
|
if (!isLoadingDialogShowing()) { |
||||||
|
onDismiss() |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
val dialogShowingTime = System.currentTimeMillis() - recentShowingDialogTime |
||||||
|
|
||||||
|
if (dialogShowingTime >= minimumMills) { |
||||||
|
dismissLoadingDialog() |
||||||
|
onDismiss() |
||||||
|
} else { |
||||||
|
lifecycleScope.launch { |
||||||
|
try { |
||||||
|
delay(minimumMills - dialogShowingTime) |
||||||
|
dismissLoadingDialog() |
||||||
|
onDismiss() |
||||||
|
} catch (e: CancellationException) { |
||||||
|
onDismiss() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private const val FRAGMENT_STATE_KEY = "fragment_state_key" |
||||||
|
|
||||||
|
internal fun saveInstanceState(outState: Bundle, _state: Bundle?) { |
||||||
|
_state?.let { |
||||||
|
outState.putBundle(FRAGMENT_STATE_KEY, it) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal fun getInstanceState(state: Bundle?): Bundle? { |
||||||
|
return state?.getBundle(FRAGMENT_STATE_KEY) |
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
package com.android.base.app.mvp |
||||||
|
|
||||||
|
import androidx.annotation.CallSuper |
||||||
|
import java.lang.ref.WeakReference |
||||||
|
|
||||||
|
abstract class AbstractPresenter<V : IBaseView> : IPresenter<V> { |
||||||
|
|
||||||
|
private var _view: WeakReference<V>? = null |
||||||
|
|
||||||
|
protected val view: V? |
||||||
|
get() = if (_view != null) { |
||||||
|
_view?.get() |
||||||
|
} else null |
||||||
|
|
||||||
|
protected val isViewAttached: Boolean |
||||||
|
get() = _view != null && _view?.get() != null |
||||||
|
|
||||||
|
final override fun bindView(view: V?) { |
||||||
|
if (view == null) { |
||||||
|
throw NullPointerException("Presenter bindView --> view is null") |
||||||
|
} |
||||||
|
if (_view != null) { |
||||||
|
throw UnsupportedOperationException("Presenter bindView --> the view already bind") |
||||||
|
} |
||||||
|
_view = WeakReference(view) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onPostStart() {} |
||||||
|
|
||||||
|
override fun onPause() { |
||||||
|
} |
||||||
|
|
||||||
|
override fun onResume() { |
||||||
|
} |
||||||
|
|
||||||
|
@CallSuper |
||||||
|
override fun onDestroy() { |
||||||
|
if (_view != null) { |
||||||
|
_view?.clear() |
||||||
|
_view = null |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
package com.android.base.app.mvp |
||||||
|
|
||||||
|
interface IBaseView |
@ -0,0 +1,13 @@ |
|||||||
|
package com.android.base.app.mvp |
||||||
|
|
||||||
|
|
||||||
|
interface IPresenter<V : IBaseView> : Lifecycle { |
||||||
|
|
||||||
|
/** |
||||||
|
* bind a view |
||||||
|
* |
||||||
|
* @param view V |
||||||
|
*/ |
||||||
|
fun bindView(view: V?) |
||||||
|
|
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
package com.android.base.app.mvp |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @author Ztiany |
||||||
|
* Email: 1169654504@qq.com |
||||||
|
* Date : 2017-04-18 16:23 |
||||||
|
*/ |
||||||
|
interface Lifecycle { |
||||||
|
|
||||||
|
/** |
||||||
|
* start the Lifecycle , initialize something, will be called only once |
||||||
|
*/ |
||||||
|
fun onStart() |
||||||
|
|
||||||
|
/** |
||||||
|
* will be called when view is ready. |
||||||
|
*/ |
||||||
|
fun onPostStart() |
||||||
|
|
||||||
|
fun onResume() |
||||||
|
|
||||||
|
fun onPause() |
||||||
|
|
||||||
|
/** |
||||||
|
* destroy the Lifecycle and release resource, will be called only once |
||||||
|
*/ |
||||||
|
fun onDestroy() |
||||||
|
|
||||||
|
} |
||||||
|
|
@ -0,0 +1,66 @@ |
|||||||
|
package com.android.base.app.mvp |
||||||
|
|
||||||
|
import android.os.Bundle |
||||||
|
import androidx.fragment.app.Fragment |
||||||
|
import android.view.View |
||||||
|
import com.android.base.app.fragment.delegates.FragmentDelegate |
||||||
|
import com.android.base.app.fragment.delegates.FragmentDelegateOwner |
||||||
|
|
||||||
|
|
||||||
|
class PresenterBinder constructor(private val lifecycle: Lifecycle) : FragmentDelegate<Fragment> { |
||||||
|
|
||||||
|
private var isCalled: Boolean = false |
||||||
|
private var host: Fragment? = null |
||||||
|
|
||||||
|
override fun onAttachToFragment(fragment: Fragment) { |
||||||
|
host = fragment |
||||||
|
} |
||||||
|
|
||||||
|
override fun onDetachFromFragment() { |
||||||
|
host = null |
||||||
|
} |
||||||
|
|
||||||
|
override fun onActivityCreated(savedInstanceState: Bundle?) { |
||||||
|
if (!isCalled) { |
||||||
|
lifecycle.onStart() |
||||||
|
val activity = host?.activity |
||||||
|
activity?.findViewById<View>(android.R.id.content)?.post { lifecycle.onPostStart() } |
||||||
|
isCalled = true |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onResume() { |
||||||
|
lifecycle.onResume() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onPause() { |
||||||
|
lifecycle.onPause() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onDestroy() { |
||||||
|
lifecycle.onDestroy() |
||||||
|
} |
||||||
|
|
||||||
|
companion object { |
||||||
|
|
||||||
|
/** |
||||||
|
* @param v The MVP of the V |
||||||
|
* @param p The MVP of the P |
||||||
|
* @param <V> The MVP of the V |
||||||
|
</V> */ |
||||||
|
fun <V : IBaseView> bind(fragmentDelegateOwner: FragmentDelegateOwner, v: V, p: IPresenter<V>): PresenterBinder { |
||||||
|
p.bindView(v) |
||||||
|
val lifecycleDelegate = PresenterBinder(p) |
||||||
|
fragmentDelegateOwner.addDelegate(lifecycleDelegate) |
||||||
|
return lifecycleDelegate |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
fun <V : IBaseView> FragmentDelegateOwner.bindPresenter(v: V, p: IPresenter<V>): PresenterBinder { |
||||||
|
p.bindView(v) |
||||||
|
val lifecycleDelegate = PresenterBinder(p) |
||||||
|
addDelegate(lifecycleDelegate) |
||||||
|
return lifecycleDelegate |
||||||
|
} |
@ -0,0 +1,65 @@ |
|||||||
|
package com.android.base.app.mvp |
||||||
|
|
||||||
|
import androidx.annotation.CallSuper |
||||||
|
import com.android.base.rx.autodispose.AutoDisposeLifecycleScopeProviderEx |
||||||
|
import com.uber.autodispose.lifecycle.CorrespondingEventsFunction |
||||||
|
import com.uber.autodispose.lifecycle.LifecycleEndedException |
||||||
|
import com.uber.autodispose.lifecycle.LifecycleScopes |
||||||
|
import io.reactivex.CompletableSource |
||||||
|
import io.reactivex.Observable |
||||||
|
import io.reactivex.subjects.BehaviorSubject |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* work with RxJava |
||||||
|
* |
||||||
|
* @author Ztiany |
||||||
|
* Date : 2016-10-19 12:17 |
||||||
|
* Email: 1169654504@qq.com |
||||||
|
*/ |
||||||
|
abstract class RxPresenter<V : IBaseView> : AbstractPresenter<V>(), AutoDisposeLifecycleScopeProviderEx<RxPresenter.LifecycleEvent> { |
||||||
|
|
||||||
|
private val lifecycleSubject = BehaviorSubject.create<LifecycleEvent>() |
||||||
|
|
||||||
|
enum class LifecycleEvent { |
||||||
|
START, |
||||||
|
DESTROY |
||||||
|
} |
||||||
|
|
||||||
|
@CallSuper |
||||||
|
override fun onStart() { |
||||||
|
lifecycleSubject.onNext(LifecycleEvent.START) |
||||||
|
} |
||||||
|
|
||||||
|
@CallSuper |
||||||
|
override fun onDestroy() { |
||||||
|
lifecycleSubject.onNext(LifecycleEvent.DESTROY) |
||||||
|
super@RxPresenter.onDestroy() |
||||||
|
} |
||||||
|
|
||||||
|
final override fun lifecycle(): Observable<LifecycleEvent> { |
||||||
|
return lifecycleSubject |
||||||
|
} |
||||||
|
|
||||||
|
final override fun correspondingEvents(): CorrespondingEventsFunction<LifecycleEvent> { |
||||||
|
return LIFECYCLE_CORRESPONDING_EVENTS |
||||||
|
} |
||||||
|
|
||||||
|
final override fun peekLifecycle(): LifecycleEvent? { |
||||||
|
return lifecycleSubject.value |
||||||
|
} |
||||||
|
|
||||||
|
final override fun requestScope(): CompletableSource { |
||||||
|
return LifecycleScopes.resolveScopeFromLifecycle<LifecycleEvent>(this) |
||||||
|
} |
||||||
|
|
||||||
|
companion object { |
||||||
|
internal val LIFECYCLE_CORRESPONDING_EVENTS: CorrespondingEventsFunction<LifecycleEvent> = CorrespondingEventsFunction { |
||||||
|
return@CorrespondingEventsFunction when (it) { |
||||||
|
LifecycleEvent.START -> LifecycleEvent.DESTROY |
||||||
|
else -> throw LifecycleEndedException("Cannot bind to LifecycleEvent when outside of it.") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
package com.android.base.app.mvvm |
||||||
|
|
||||||
|
|
||||||
|
import androidx.annotation.CallSuper |
||||||
|
import androidx.lifecycle.ViewModel |
||||||
|
import com.android.base.rx.autodispose.AutoDisposeLifecycleScopeProviderEx |
||||||
|
import com.uber.autodispose.lifecycle.CorrespondingEventsFunction |
||||||
|
import com.uber.autodispose.lifecycle.LifecycleEndedException |
||||||
|
import com.uber.autodispose.lifecycle.LifecycleScopes |
||||||
|
import io.reactivex.CompletableSource |
||||||
|
import io.reactivex.Observable |
||||||
|
import io.reactivex.subjects.BehaviorSubject |
||||||
|
|
||||||
|
/** |
||||||
|
* ArchViewModel work with Rx |
||||||
|
* |
||||||
|
* @author Ztiany |
||||||
|
* Email: 1169654504@qq.com |
||||||
|
* Date : 2017-04-18 16:25 |
||||||
|
*/ |
||||||
|
abstract class RxViewModel : ViewModel(), AutoDisposeLifecycleScopeProviderEx<RxViewModel.ViewModelEvent> { |
||||||
|
|
||||||
|
private val archLifecycleSubject = BehaviorSubject.createDefault(ViewModelEvent.CREATED) |
||||||
|
|
||||||
|
enum class ViewModelEvent { |
||||||
|
CREATED, CLEARED |
||||||
|
} |
||||||
|
|
||||||
|
@CallSuper |
||||||
|
override fun onCleared() { |
||||||
|
archLifecycleSubject.onNext(ViewModelEvent.CLEARED) |
||||||
|
super.onCleared() |
||||||
|
} |
||||||
|
|
||||||
|
final override fun correspondingEvents(): CorrespondingEventsFunction<ViewModelEvent> { |
||||||
|
return CORRESPONDING_EVENTS |
||||||
|
} |
||||||
|
|
||||||
|
final override fun lifecycle(): Observable<ViewModelEvent> { |
||||||
|
return archLifecycleSubject.hide() |
||||||
|
} |
||||||
|
|
||||||
|
final override fun peekLifecycle(): ViewModelEvent? { |
||||||
|
return archLifecycleSubject.value |
||||||
|
} |
||||||
|
|
||||||
|
final override fun requestScope(): CompletableSource { |
||||||
|
return LifecycleScopes.resolveScopeFromLifecycle(this) |
||||||
|
} |
||||||
|
|
||||||
|
companion object { |
||||||
|
private val CORRESPONDING_EVENTS = CorrespondingEventsFunction<ViewModelEvent> { event -> |
||||||
|
when (event) { |
||||||
|
ViewModelEvent.CREATED -> ViewModelEvent.CLEARED |
||||||
|
else -> throw LifecycleEndedException( |
||||||
|
"Cannot bind to ViewModel lifecycle after onCleared.") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,39 @@ |
|||||||
|
package com.android.base.app.ui; |
||||||
|
|
||||||
|
import com.android.base.adapter.DataManager; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Ztiany |
||||||
|
* Email: ztiany3@gmail.com |
||||||
|
* Date : 2018-04-27 14:04 |
||||||
|
*/ |
||||||
|
public class AutoPageNumber extends PageNumber { |
||||||
|
|
||||||
|
private final DataManager mDataManager; |
||||||
|
private final RefreshListLayout mRefreshListLayout; |
||||||
|
|
||||||
|
public AutoPageNumber(RefreshListLayout refreshListLayout, DataManager dataManager) { |
||||||
|
mRefreshListLayout = refreshListLayout; |
||||||
|
mDataManager = dataManager; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getCurrentPage() { |
||||||
|
return calcPageNumber(mDataManager.getDataSize()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getLoadingPage() { |
||||||
|
if (mRefreshListLayout.isRefreshing()) { |
||||||
|
return getPageStart(); |
||||||
|
} else { |
||||||
|
return getCurrentPage() + 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getItemCount() { |
||||||
|
return mDataManager.getDataSize(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
package com.android.base.app.ui; |
||||||
|
|
||||||
|
|
||||||
|
import com.android.base.R; |
||||||
|
|
||||||
|
public class CommonId { |
||||||
|
|
||||||
|
private CommonId() { |
||||||
|
throw new UnsupportedOperationException(); |
||||||
|
} |
||||||
|
|
||||||
|
public static final int REFRESH_ID = R.id.base_refresh_layout; |
||||||
|
public static final int STATE_ID = R.id.base_state_layout; |
||||||
|
public static final int LIST_ID = R.id.base_list_layout; |
||||||
|
|
||||||
|
public static final int RETRY_TV_ID = R.id.base_retry_tv; |
||||||
|
public static final int RETRY_IV_ID = R.id.base_retry_icon; |
||||||
|
public static final int RETRY_BTN_ID = R.id.base_retry_btn; |
||||||
|
|
||||||
|
public static final int TOOLBAR_ID = R.id.common_toolbar; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
package com.android.base.app.ui |
||||||
|
|
||||||
|
import androidx.lifecycle.LifecycleOwner |
||||||
|
import androidx.lifecycle.LiveData |
||||||
|
import androidx.lifecycle.Observer |
||||||
|
import com.android.base.app.Sword |
||||||
|
import com.android.base.data.State |
||||||
|
import timber.log.Timber |
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,25 @@ |
|||||||
|
package com.android.base.app.ui |
||||||
|
|
||||||
|
import androidx.annotation.StringRes |
||||||
|
|
||||||
|
/** |
||||||
|
* 显示通用的 LoadingDialog 和 Message |
||||||
|
* |
||||||
|
* @author Ztiany |
||||||
|
* Email: 1169654504@qq.com |
||||||
|
* Date : 2016-12-02 15:12 |
||||||
|
*/ |
||||||
|
interface LoadingView { |
||||||
|
|
||||||
|
fun showLoadingDialog() |
||||||
|
fun showLoadingDialog(cancelable: Boolean) |
||||||
|
fun showLoadingDialog(message: CharSequence, cancelable: Boolean) |
||||||
|
fun showLoadingDialog(@StringRes messageId: Int, cancelable: Boolean) |
||||||
|
fun dismissLoadingDialog() |
||||||
|
fun dismissLoadingDialog(minimumMills: Long, onDismiss: () -> Unit) |
||||||
|
fun isLoadingDialogShowing(): Boolean |
||||||
|
|
||||||
|
fun showMessage(message: CharSequence) |
||||||
|
fun showMessage(@StringRes messageId: Int) |
||||||
|
|
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
package com.android.base.app.ui; |
||||||
|
|
||||||
|
import static com.android.base.app.ui.StateLayoutConfig.*; |
||||||
|
|
||||||
|
public interface OnRetryActionListener { |
||||||
|
|
||||||
|
void onRetry(@RetryableState int state); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,99 @@ |
|||||||
|
package com.android.base.app.ui; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Ztiany |
||||||
|
* @version 1.0 |
||||||
|
*/ |
||||||
|
public abstract class PageNumber { |
||||||
|
|
||||||
|
private static int PAGE_START = 1; |
||||||
|
private static int PAGE_SIZE = 20; |
||||||
|
|
||||||
|
private int mPageStart; |
||||||
|
private int mPageSize; |
||||||
|
|
||||||
|
private Object mPageToken; |
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess,unused") |
||||||
|
public PageNumber() { |
||||||
|
this(PAGE_START, PAGE_SIZE); |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
public PageNumber(int pageStart, int pageSize) { |
||||||
|
mPageStart = pageStart; |
||||||
|
mPageSize = pageSize; |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("unused") |
||||||
|
public void setPageToken(Object pageToken) { |
||||||
|
mPageToken = pageToken; |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("unchecked,unused") |
||||||
|
public <T> T getPageToken() { |
||||||
|
return (T) mPageToken; |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("unused") |
||||||
|
public int getPageSize() { |
||||||
|
return mPageSize; |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
public int getPageStart() { |
||||||
|
return mPageStart; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean hasMore(int size) { |
||||||
|
return size >= mPageSize; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据page size计算当前的页码 |
||||||
|
*/ |
||||||
|
int calcPageNumber(int dataSize) { |
||||||
|
/* s=1 s=0 |
||||||
|
19/20 = 0 1 0 |
||||||
|
21/20 = 1 2 1 |
||||||
|
54/20 = 2 3 2 |
||||||
|
64/20 = 3 4 3 |
||||||
|
*/ |
||||||
|
int pageNumber; |
||||||
|
int pageSize = mPageSize; |
||||||
|
int pageStart = mPageStart; |
||||||
|
if (pageStart == 0) { |
||||||
|
pageNumber = (dataSize / pageSize) - 1; |
||||||
|
pageNumber = pageNumber < 0 ? 0 : pageNumber; |
||||||
|
} else if (pageStart == 1) { |
||||||
|
pageNumber = (dataSize / pageSize); |
||||||
|
pageNumber = pageNumber < 1 ? 1 : pageNumber; |
||||||
|
} else { |
||||||
|
throw new RuntimeException("pageStart must be 0 or 1"); |
||||||
|
} |
||||||
|
return pageNumber; |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("unused") |
||||||
|
public void changePageSetting(int pageStart, int pageSize) { |
||||||
|
mPageStart = pageStart; |
||||||
|
mPageSize = pageSize; |
||||||
|
} |
||||||
|
|
||||||
|
public static void setDefaultPageStart(int pageSize) { |
||||||
|
PAGE_START = pageSize; |
||||||
|
} |
||||||
|
|
||||||
|
public static void setDefaultPageSize(int pageSize) { |
||||||
|
PAGE_SIZE = pageSize; |
||||||
|
} |
||||||
|
|
||||||
|
public abstract int getCurrentPage(); |
||||||
|
|
||||||
|
@SuppressWarnings("unused") |
||||||
|
public abstract int getLoadingPage(); |
||||||
|
|
||||||
|
@SuppressWarnings("unused") |
||||||
|
public abstract int getItemCount(); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
package com.android.base.app.ui; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* 列表视图行为 |
||||||
|
* |
||||||
|
* @author Ztiany |
||||||
|
* Email: 1169654504@qq.com |
||||||
|
* Date : 2017-03-29 22:16 |
||||||
|
*/ |
||||||
|
public interface RefreshListLayout<T> extends RefreshStateLayout { |
||||||
|
|
||||||
|
void replaceData(List<T> data); |
||||||
|
|
||||||
|
void addData(List<T> data); |
||||||
|
|
||||||
|
PageNumber getPager(); |
||||||
|
|
||||||
|
boolean isEmpty(); |
||||||
|
|
||||||
|
void loadMoreCompleted(boolean hasMore); |
||||||
|
|
||||||
|
void loadMoreFailed(); |
||||||
|
|
||||||
|
boolean isLoadingMore(); |
||||||
|
|
||||||
|
@Override |
||||||
|
boolean isRefreshing(); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
package com.android.base.app.ui; |
||||||
|
|
||||||
|
/** |
||||||
|
* <br/> 对下拉刷新的抽象 |
||||||
|
* <br/> Email: 1169654504@qq.com |
||||||
|
* |
||||||
|
* @author Ztiany |
||||||
|
* @version 1.0 |
||||||
|
*/ |
||||||
|
public interface RefreshLoadMoreView { |
||||||
|
|
||||||
|
void autoRefresh(); |
||||||
|
|
||||||
|
void refreshCompleted(); |
||||||
|
|
||||||
|
void loadMoreCompleted(boolean hasMore); |
||||||
|
|
||||||
|
void loadMoreFailed(); |
||||||
|
|
||||||
|
void setRefreshHandler(RefreshHandler refreshHandler); |
||||||
|
|
||||||
|
void setLoadMoreHandler(LoadMoreHandler loadMoreHandler); |
||||||
|
|
||||||
|
boolean isRefreshing(); |
||||||
|
|
||||||
|
boolean isLoadingMore(); |
||||||
|
|
||||||
|
void setRefreshEnable(boolean enable); |
||||||
|
|
||||||
|
void setLoadMoreEnable(boolean enable); |
||||||
|
|
||||||
|
interface RefreshHandler { |
||||||
|
void onRefresh(); |
||||||
|
} |
||||||
|
|
||||||
|
interface LoadMoreHandler { |
||||||
|
void onRefresh(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
package com.android.base.app.ui; |
||||||
|
|
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
/** |
||||||
|
* RefreshLoadMoreView Factory |
||||||
|
* |
||||||
|
* @author Ztiany |
||||||
|
* Email: 1169654504@qq.com |
||||||
|
* @version 1.0 |
||||||
|
*/ |
||||||
|
public class RefreshLoadViewFactory { |
||||||
|
|
||||||
|
private static Factory sFactory; |
||||||
|
|
||||||
|
public static RefreshLoadMoreView createRefreshView(View view) { |
||||||
|
if (sFactory != null) { |
||||||
|
return sFactory.createRefreshView(view); |
||||||
|
} |
||||||
|
throw new IllegalArgumentException("RefreshLoadViewFactory does not support create RefreshLoadMoreView . the view :" + view); |
||||||
|
} |
||||||
|
|
||||||
|
public static void registerFactory(Factory factory) { |
||||||
|
sFactory = factory; |
||||||
|
} |
||||||
|
|
||||||
|
public interface Factory { |
||||||
|
RefreshLoadMoreView createRefreshView(View view); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
package com.android.base.app.ui; |
||||||
|
|
||||||
|
public interface RefreshStateLayout extends StateLayout{ |
||||||
|
|
||||||
|
void autoRefresh(); |
||||||
|
|
||||||
|
void refreshCompleted(); |
||||||
|
|
||||||
|
boolean isRefreshing(); |
||||||
|
|
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue