6.7.0 - Alpha23 - 移除 "android.enableJetifier=true"; 模块化 Expandable Layout, Expandable RecyclerView, Recyclerview Flexible Divider
This commit is contained in:
@@ -86,8 +86,8 @@
|
||||
"نص بناء Gradle أصبح أكثر قدرة على التكيّف مع الإصدارات الجديدة _[`discussion #369`](http://discussions.autojs6.com/369)_"
|
||||
],
|
||||
"dependency": [
|
||||
"مرفق Material Dialogs الإصدار 0.9.6.0 (محلي)",
|
||||
"مرفق Material Date Time Picker الإصدار 4.2.3 (محلي)",
|
||||
"مرفق Material Dialogs الإصدار 0.9.6.0 (تجزئة إلى وحدات)",
|
||||
"مرفق Material Date Time Picker الإصدار 4.2.3 (تجزئة إلى وحدات)",
|
||||
"مرفق libimagequant الإصدار 2.17.0 (محلي)",
|
||||
"مرفق libpng الإصدار 1.6.49 (محلي)",
|
||||
"مضاف ICU4J الإصدار 77.1",
|
||||
|
||||
@@ -86,8 +86,8 @@
|
||||
"Gradle build script now better adapts to newer versions _[`discussion #369`](http://discussions.autojs6.com/369)_"
|
||||
],
|
||||
"dependency": [
|
||||
"Bundled Material Dialogs 0.9.6.0 (localized)",
|
||||
"Bundled Material Date Time Picker 4.2.3 (localized)",
|
||||
"Bundled Material Dialogs 0.9.6.0 (modularized)",
|
||||
"Bundled Material Date Time Picker 4.2.3 (modularized)",
|
||||
"Bundled libimagequant 2.17.0 (localized)",
|
||||
"Bundled libpng 1.6.49 (localized)",
|
||||
"Added ICU4J 77.1",
|
||||
|
||||
@@ -86,8 +86,8 @@
|
||||
"El script de compilación Gradle mejora su capacidad de adaptación de versiones _[`discussion #369`](http://discussions.autojs6.com/369)_"
|
||||
],
|
||||
"dependency": [
|
||||
"Incluido Material Dialogs versión 0.9.6.0 (localizado)",
|
||||
"Incluido Material Date Time Picker versión 4.2.3 (localizado)",
|
||||
"Incluido Material Dialogs versión 0.9.6.0 (modularizado)",
|
||||
"Incluido Material Date Time Picker versión 4.2.3 (modularizado)",
|
||||
"Incluido libimagequant versión 2.17.0 (localizado)",
|
||||
"Incluido libpng versión 1.6.49 (localizado)",
|
||||
"Añadido ICU4J versión 77.1",
|
||||
|
||||
@@ -86,8 +86,8 @@
|
||||
"Le script de build Gradle améliore sa capacité d'adaptation aux nouvelles versions _[`discussion #369`](http://discussions.autojs6.com/369)_"
|
||||
],
|
||||
"dependency": [
|
||||
"Inclus Material Dialogs version 0.9.6.0 (localisé)",
|
||||
"Inclus Material Date Time Picker version 4.2.3 (localisé)",
|
||||
"Inclus Material Dialogs version 0.9.6.0 (modularise)",
|
||||
"Inclus Material Date Time Picker version 4.2.3 (modularise)",
|
||||
"Inclus libimagequant version 2.17.0 (localisé)",
|
||||
"Inclus libpng version 1.6.49 (localisé)",
|
||||
"Ajouté ICU4J version 77.1",
|
||||
|
||||
@@ -86,8 +86,8 @@
|
||||
"Gradle ビルドスクリプトが新しいバージョンに柔軟に対応 _[`discussion #369`](http://discussions.autojs6.com/369)_"
|
||||
],
|
||||
"dependency": [
|
||||
"同梱 Material Dialogs バージョン 0.9.6.0 (ローカライズ済み)",
|
||||
"同梱 Material Date Time Picker バージョン 4.2.3 (ローカライズ済み)",
|
||||
"同梱 Material Dialogs バージョン 0.9.6.0 (モジュール化)",
|
||||
"同梱 Material Date Time Picker バージョン 4.2.3 (モジュール化)",
|
||||
"同梱 libimagequant バージョン 2.17.0 (ローカライズ済み)",
|
||||
"同梱 libpng バージョン 1.6.49 (ローカライズ済み)",
|
||||
"追加 ICU4J バージョン 77.1",
|
||||
|
||||
@@ -86,8 +86,8 @@
|
||||
"Gradle 빌드 스크립트가 최신 버전에 더 유연하게 대응 _[`discussion #369`](http://discussions.autojs6.com/369)_"
|
||||
],
|
||||
"dependency": [
|
||||
"내장 Material Dialogs 버전 0.9.6.0 (현지화)",
|
||||
"내장 Material Date Time Picker 버전 4.2.3 (현지화)",
|
||||
"내장 Material Dialogs 버전 0.9.6.0 (모듈화)",
|
||||
"내장 Material Date Time Picker 버전 4.2.3 (모듈화)",
|
||||
"내장 libimagequant 버전 2.17.0 (현지화)",
|
||||
"내장 libpng 버전 1.6.49 (현지화)",
|
||||
"추가 ICU4J 버전 77.1",
|
||||
|
||||
@@ -86,8 +86,8 @@
|
||||
"Скрипт сборки Gradle лучше адаптируется к новым версиям _[`discussion #369`](http://discussions.autojs6.com/369)_"
|
||||
],
|
||||
"dependency": [
|
||||
"Встроено Material Dialogs версии 0.9.6.0 (локализовано)",
|
||||
"Встроено Material Date Time Picker версии 4.2.3 (локализовано)",
|
||||
"Встроено Material Dialogs версии 0.9.6.0 (модульный)",
|
||||
"Встроено Material Date Time Picker версии 4.2.3 (модульный)",
|
||||
"Встроено libimagequant версии 2.17.0 (локализовано)",
|
||||
"Встроено libpng версии 1.6.49 (локализовано)",
|
||||
"Добавлено ICU4J версии 77.1",
|
||||
|
||||
@@ -177,18 +177,22 @@
|
||||
"使用 [版本目录 (Version Catalogs)](https://developer.android.com/build/migrate-to-catalogs) 集中管理 Gradle 依赖和插件版本",
|
||||
"模块化 Gradle 构建脚本, 将共享构建逻辑迁移至 build-logic 并抽象为约定插件",
|
||||
"使用 Gradle 约定插件简化本地 AAR 库加载逻辑",
|
||||
"移除 \"android.enableJetifier=true\" Gradle 配置选项以适配 Gradle 9",
|
||||
"使用 [Toolchain](https://docs.gradle.org/current/userguide/toolchains.html) 替代 sourceCompatibility/targetCompatibility 以降低构建环境差异"
|
||||
],
|
||||
"dependency": [
|
||||
"模块化 Expandable Layout 版本 1.6.0",
|
||||
"模块化 Expandable RecyclerView 版本 3.0.0-RC1",
|
||||
"模块化 Recyclerview Flexible Divider 版本 1.4.0",
|
||||
"本地化 Root Shell 版本 1.6",
|
||||
"本地化 Expandable Layout 版本 1.6.0",
|
||||
"本地化 Recyclerview Flexible Divider 版本 1.4.0",
|
||||
"附加 AndroidX Activity (KTX) 版本 1.12.2",
|
||||
"附加 AndroidX Core (KTX) 版本 1.15.0",
|
||||
"附加 AndroidX Work Runtime 版本 2.11.0",
|
||||
"附加 AndroidX Interpolator 版本 1.0.0",
|
||||
"附加 Room RxJava2 版本 2.8.1",
|
||||
"升级 Android SDK 版本 35 -> 36",
|
||||
"升级 Gradle 版本 8.14 -> 8.14.3",
|
||||
"升级 Gradle 8 版本 8.14 -> 8.14.4",
|
||||
"升级 Gradle 9 版本 8.14 -> 9.4.0",
|
||||
"升级 Rhino 版本 1.8.1-SNAPSHOT -> 2.0.0-SNAPSHOT",
|
||||
"升级 Desugar JDK Libs 版本 2.0.4 -> 2.1.5",
|
||||
"升级 Android Material 版本 1.12.0 -> 1.13.0",
|
||||
@@ -205,7 +209,10 @@
|
||||
"升级 ARSCLib 版本 1.3.1 -> 1.3.5",
|
||||
"升级 Jsoup 版本 1.19.1 -> 1.20.1",
|
||||
"升级 Junit Jupiter 版本 5.10.3 -> 5.13.0",
|
||||
"升级 OpenCC 版本 1.2.0 -> 1.2.2"
|
||||
"升级 OpenCC 版本 1.2.0 -> 1.2.2",
|
||||
"移除 Android Support Annotation v7 版本 28.0.0",
|
||||
"移除 Android Support AppCompat v7 版本 28.0.0",
|
||||
"移除 Android Support RecyclerView v7 版本 28.0.0"
|
||||
]
|
||||
},
|
||||
"v6.6.4": {
|
||||
@@ -294,8 +301,8 @@
|
||||
"Gradle 构建脚本提升版本自适应能力 _[`discussion #369`](http://discussions.autojs6.com/369)_"
|
||||
],
|
||||
"dependency": [
|
||||
"本地化 Material Dialogs 版本 0.9.6.0",
|
||||
"本地化 Material Date Time Picker 版本 4.2.3",
|
||||
"模块化 Material Dialogs 版本 0.9.6.0",
|
||||
"模块化 Material Date Time Picker 版本 4.2.3",
|
||||
"本地化 libimagequant 版本 2.17.0",
|
||||
"本地化 libpng 版本 1.6.49",
|
||||
"附加 ICU4J 版本 77.1",
|
||||
|
||||
@@ -86,8 +86,8 @@
|
||||
"Gradle 構建腳本提升版本自適應能力 _[`discussion #369`](http://discussions.autojs6.com/369)_"
|
||||
],
|
||||
"dependency": [
|
||||
"本地化 Material Dialogs 版本 0.9.6.0",
|
||||
"本地化 Material Date Time Picker 版本 4.2.3",
|
||||
"模塊化 Material Dialogs 版本 0.9.6.0",
|
||||
"模塊化 Material Date Time Picker 版本 4.2.3",
|
||||
"本地化 libimagequant 版本 2.17.0",
|
||||
"本地化 libpng 版本 1.6.49",
|
||||
"附加 ICU4J 版本 77.1",
|
||||
|
||||
@@ -86,8 +86,8 @@
|
||||
"Gradle 構建指令碼提升版本自適應能力 _[`discussion #369`](http://discussions.autojs6.com/369)_"
|
||||
],
|
||||
"dependency": [
|
||||
"本地化 Material Dialogs 版本 0.9.6.0",
|
||||
"本地化 Material Date Time Picker 版本 4.2.3",
|
||||
"模組化 Material Dialogs 版本 0.9.6.0",
|
||||
"模組化 Material Date Time Picker 版本 4.2.3",
|
||||
"本地化 libimagequant 版本 2.17.0",
|
||||
"本地化 libpng 版本 1.6.49",
|
||||
"附加 ICU4J 版本 77.1",
|
||||
|
||||
@@ -284,9 +284,9 @@ dependencies /* Test */ {
|
||||
|
||||
dependencies /* Annotations */ {
|
||||
// Android Annotations
|
||||
implementation(libs.androidannotations.api)
|
||||
implementation(libs.android.annotations.api)
|
||||
implementation(libs.annotation)
|
||||
ksp(libs.androidannotations)
|
||||
ksp(libs.android.annotations)
|
||||
|
||||
// JCIP Annotations
|
||||
implementation(libs.jcip.annotations)
|
||||
@@ -332,14 +332,12 @@ dependencies /* Material Dialogs */ {
|
||||
dependencies /* Layout */ {
|
||||
// Expandable Layout
|
||||
// implementation("com.github.aakira:expandable-layout:1.6.0")
|
||||
implementation(project(":libs:expandable-layout-1_6_0"))
|
||||
// implementation(project(":libs:expandable-layout-1_6_0"))
|
||||
implementation(project(":modules:expandable-layout"))
|
||||
|
||||
// Expandable RecyclerView
|
||||
implementation(libs.expandablerecyclerview)
|
||||
implementation(project(":modules:expandable-recyclerview"))
|
||||
|
||||
// Flexible Divider
|
||||
// implementation("com.yqritc:recyclerview-flexibledivider:1.4.0")
|
||||
implementation(project(":libs:recyclerview-flexibledivider-1_4_0"))
|
||||
implementation(project(":modules:recyclerview-flexibledivider"))
|
||||
}
|
||||
|
||||
dependencies /* View */ {
|
||||
|
||||
@@ -115,7 +115,7 @@ class Selector(private val scriptRuntime: ScriptRuntime) : Augmentable(scriptRun
|
||||
when {
|
||||
arg !is Double -> arg
|
||||
paramType == Double::class.java || paramType == java.lang.Double.TYPE -> arg
|
||||
paramType == Float::class.java || paramType == java.lang.Float.TYPE -> arg.toDouble()
|
||||
paramType == Float::class.java || paramType == java.lang.Float.TYPE -> arg.toFloat()
|
||||
paramType == Long::class.java || paramType == java.lang.Long.TYPE -> arg.roundToLong()
|
||||
paramType == Int::class.java || paramType == Integer.TYPE -> arg.roundToInt()
|
||||
paramType == Short::class.java || paramType == java.lang.Short.TYPE -> arg.toInt().toShort()
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
#Thu Nov 11 16:38:18 CST 2021
|
||||
# Thu Nov 11 16:38:18 CST 2021
|
||||
# Configure memory settings for Gradle daemon and Kotlin daemon
|
||||
# -Xms: Initial heap size
|
||||
# -Xmx: Maximum heap size
|
||||
@@ -29,11 +29,6 @@ org.gradle.parallel=true
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
# https://developer.android.com/jetpack/androidx/migrate
|
||||
android.useAndroidX=true
|
||||
# When this flag is set to true,
|
||||
# the Android plugin automatically migrates existing third-party libraries
|
||||
# to use AndroidX dependencies by rewriting their binaries.
|
||||
# https://developer.android.com/jetpack/androidx/migrate
|
||||
android.enableJetifier=true
|
||||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
@@ -44,8 +39,15 @@ android.nonFinalResIds=true
|
||||
# auto-download: Automatically download required Java version if not found
|
||||
org.gradle.java.installations.auto-detect=true
|
||||
org.gradle.java.installations.auto-download=true
|
||||
# BuildConfig feature flag history:
|
||||
# - Added in Gradle Build Tool 8.0.0-alpha09
|
||||
# - Removed in Gradle Build Tool 8.2.0-alpha15
|
||||
# - Controls generation of BuildConfig class
|
||||
# ====== Archived ======
|
||||
# ------ Jetifier ------
|
||||
# When this flag is set to true,
|
||||
# the Android plugin automatically migrates existing third-party libraries
|
||||
# to use AndroidX dependencies by rewriting their binaries.
|
||||
# https://developer.android.com/jetpack/androidx/migrate
|
||||
# // Removed since Gradle 9.4.0
|
||||
# android.enableJetifier=true
|
||||
# ------ BuildConfig ------
|
||||
# Controls generation of BuildConfig class
|
||||
# // Removed since Gradle Build Tool 8.2.0-alpha15
|
||||
# android.defaults.buildfeatures.buildconfig=true
|
||||
|
||||
@@ -40,10 +40,9 @@ log4j = "1.2.17"
|
||||
desugar = "2.1.5"
|
||||
|
||||
activity-ktx = "1.12.2"
|
||||
android-annotations = "4.8.0"
|
||||
android-device-names = "2.1.1"
|
||||
android-logging-log4j = "1.0.3"
|
||||
android-support-v7 = "28.0.0"
|
||||
androidannotations = "4.8.0"
|
||||
animated-svg-view = "1.0.6"
|
||||
annotation = "1.9.1"
|
||||
appcompat = "1.7.1"
|
||||
@@ -60,13 +59,13 @@ commons-lang3 = "3.18.0"
|
||||
constraintlayout = "2.2.1"
|
||||
easywindow = "10.3"
|
||||
eventbus = "3.3.1"
|
||||
expandablerecyclerview = "3.0.0-RC1"
|
||||
flexbox = "3.0.0"
|
||||
flexmark-html2md = "0.64.8"
|
||||
foojay-resolver-convention = "1.0.0"
|
||||
glide = "5.0.5"
|
||||
gson = "2.13.2"
|
||||
icu4j = "77.1"
|
||||
interpolator = "1.0.0"
|
||||
jcip-annotations = "1.0"
|
||||
jdeferred = "1.2.6"
|
||||
joda-time = "2.14.0"
|
||||
@@ -112,15 +111,14 @@ zip4j = "2.11.5"
|
||||
[libraries]
|
||||
|
||||
activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activity-ktx" }
|
||||
android-annotations = { module = "org.androidannotations:androidannotations", version.ref = "android-annotations" }
|
||||
android-annotations-api = { module = "org.androidannotations:androidannotations-api", version.ref = "android-annotations" }
|
||||
android-device-names = { module = "com.jaredrummler:android-device-names", version.ref = "android-device-names" }
|
||||
android-logging-log4j = { module = "de.mindpipe.android:android-logging-log4j", version.ref = "android-logging-log4j" }
|
||||
androidannotations = { module = "org.androidannotations:androidannotations", version.ref = "androidannotations" }
|
||||
androidannotations-api = { module = "org.androidannotations:androidannotations-api", version.ref = "androidannotations" }
|
||||
animated-svg-view = { module = "com.jaredrummler:animated-svg-view", version.ref = "animated-svg-view" }
|
||||
annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" }
|
||||
apache-commons-compress = { module = "org.apache.commons:commons-compress", version.ref = "commons-compress" }
|
||||
appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
|
||||
appcompat-v7 = { module = "com.android.support:appcompat-v7", version.ref = "android-support-v7" }
|
||||
arsclib = { module = "io.github.reandroid:ARSCLib", version.ref = "arsclib" }
|
||||
barcode-scanning = { module = "com.google.mlkit:barcode-scanning", version.ref = "barcode-scanning" }
|
||||
bcpkix-jdk15on = { module = "org.bouncycastle:bcpkix-jdk15on", version.ref = "bcpkix-jdk15on" }
|
||||
@@ -136,13 +134,13 @@ core-ktx = { module = "androidx.core:core-ktx", version.ref = "core-ktx" }
|
||||
desugar = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar" }
|
||||
easywindow = { module = "com.github.getActivity:EasyWindow", version.ref = "easywindow" }
|
||||
eventbus = { module = "org.greenrobot:eventbus", version.ref = "eventbus" }
|
||||
expandablerecyclerview = { module = "com.bignerdranch.android:expandablerecyclerview", version.ref = "expandablerecyclerview" }
|
||||
flexbox = { module = "com.google.android.flexbox:flexbox", version.ref = "flexbox" }
|
||||
flexmark-html2md = { module = "com.vladsch.flexmark:flexmark-html2md-converter", version.ref = "flexmark-html2md" }
|
||||
glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" }
|
||||
glide-ksp = { module = "com.github.bumptech.glide:ksp", version.ref = "glide" }
|
||||
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
|
||||
icu4j = { module = "com.ibm.icu:icu4j", version.ref = "icu4j" }
|
||||
interpolator = { group = "androidx.interpolator", name = "interpolator", version.ref = "interpolator" }
|
||||
jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson-databind" }
|
||||
jcip-annotations = { module = "net.jcip:jcip-annotations", version.ref = "jcip-annotations" }
|
||||
jdeferred = { module = "org.jdeferred:jdeferred-android-aar", version.ref = "jdeferred" }
|
||||
@@ -170,7 +168,6 @@ preference-ktx = { module = "androidx.preference:preference-ktx", version.ref =
|
||||
prov = { module = "com.madgag.spongycastle:prov", version.ref = "prov" }
|
||||
r8 = { module = "com.android.tools:r8", version.ref = "r8" }
|
||||
recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" }
|
||||
recyclerview-v7 = { module = "com.android.support:recyclerview-v7", version.ref = "android-support-v7" }
|
||||
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
|
||||
retrofit-adapter-rxjava2 = { module = "com.squareup.retrofit2:adapter-rxjava2", version.ref = "retrofit" }
|
||||
retrofit-converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" }
|
||||
@@ -184,7 +181,6 @@ rxandroid = { module = "io.reactivex.rxjava2:rxandroid", version.ref = "rxandroi
|
||||
rxjava = { module = "io.reactivex.rxjava2:rxjava", version.ref = "rxjava" }
|
||||
shizuku-api = { module = "dev.rikka.shizuku:api", version.ref = "shizuku" }
|
||||
shizuku-provider = { module = "dev.rikka.shizuku:provider", version.ref = "shizuku" }
|
||||
support-annotations = { module = "com.android.support:support-annotations", version.ref = "android-support-v7" }
|
||||
swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swiperefreshlayout" }
|
||||
test-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "test-espresso-core" }
|
||||
test-ext-junit = { module = "androidx.test.ext:junit", version.ref = "test-junit-version" }
|
||||
@@ -200,4 +196,4 @@ zip4j = { module = "net.lingala.zip4j:zip4j", version.ref = "zip4j" }
|
||||
|
||||
[plugins]
|
||||
|
||||
foojay-resolver-convention = { id = "org.gradle.toolchains.foojay-resolver-convention", version.ref = "foojay-resolver-convention" }
|
||||
foojay-resolver-convention = { id = "org.gradle.toolchains.foojay-resolver-convention", version.ref = "foojay-resolver-convention" }
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
plugins {
|
||||
id("org.autojs.build.local-arr-register-convention")
|
||||
}
|
||||
|
||||
localAars {
|
||||
files += "expandable-layout-1.6.0.aar"
|
||||
}
|
||||
Binary file not shown.
@@ -1,7 +0,0 @@
|
||||
plugins {
|
||||
id("org.autojs.build.local-arr-register-convention")
|
||||
}
|
||||
|
||||
localAars {
|
||||
files += "recyclerview-flexibledivider-1.4.0.aar"
|
||||
}
|
||||
Binary file not shown.
22
modules/expandable-layout/build.gradle
Normal file
22
modules/expandable-layout/build.gradle
Normal file
@@ -0,0 +1,22 @@
|
||||
plugins {
|
||||
id("org.autojs.build.versions")
|
||||
id("org.autojs.build.jvm-convention")
|
||||
id("com.android.library")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace "com.github.aakira.expandablelayout"
|
||||
compileSdkVersion versions.sdkVersionCompile
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion versions.sdkVersionMin
|
||||
targetSdkVersion versions.sdkVersionTarget
|
||||
versionCode 12
|
||||
versionName "1.6.0 Mod"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation libs.annotation
|
||||
implementation libs.interpolator
|
||||
}
|
||||
17
modules/expandable-layout/proguard-rules.pro
vendored
Normal file
17
modules/expandable-layout/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Applications/eclipse/android/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 *;
|
||||
#}
|
||||
1
modules/expandable-layout/src/main/AndroidManifest.xml
Normal file
1
modules/expandable-layout/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
||||
<manifest />
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.github.aakira.expandablelayout;
|
||||
|
||||
import android.animation.TimeInterpolator;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
public interface ExpandableLayout {
|
||||
|
||||
/**
|
||||
* Duration of expand animation
|
||||
*/
|
||||
int DEFAULT_DURATION = 300;
|
||||
/**
|
||||
* Visibility of the layout when the layout attaches
|
||||
*/
|
||||
boolean DEFAULT_EXPANDED = false;
|
||||
/**
|
||||
* Orientation of child views
|
||||
*/
|
||||
int HORIZONTAL = 0;
|
||||
/**
|
||||
* Orientation of child views
|
||||
*/
|
||||
int VERTICAL = 1;
|
||||
|
||||
/**
|
||||
* Orientation of layout
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({HORIZONTAL, VERTICAL})
|
||||
@interface Orientation {
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts animation the state of the view to the inverse of its current state.
|
||||
*/
|
||||
void toggle();
|
||||
|
||||
/**
|
||||
* Starts animation the state of the view to the inverse of its current state.
|
||||
*
|
||||
* @param duration
|
||||
* @param interpolator use the default interpolator if the argument is null.
|
||||
*/
|
||||
void toggle(final long duration, @Nullable final TimeInterpolator interpolator);
|
||||
|
||||
/**
|
||||
* Starts expand animation.
|
||||
*/
|
||||
void expand();
|
||||
|
||||
/**
|
||||
* Starts expand animation.
|
||||
*
|
||||
* @param duration
|
||||
* @param interpolator use the default interpolator if the argument is null.
|
||||
*/
|
||||
void expand(final long duration, @Nullable final TimeInterpolator interpolator);
|
||||
|
||||
/**
|
||||
* Starts collapse animation.
|
||||
*/
|
||||
void collapse();
|
||||
|
||||
/**
|
||||
* Starts collapse animation.
|
||||
*
|
||||
* @param duration
|
||||
* @param interpolator use the default interpolator if the argument is null.
|
||||
*/
|
||||
void collapse(final long duration, @Nullable final TimeInterpolator interpolator);
|
||||
|
||||
/**
|
||||
* Sets the expandable layout listener.
|
||||
*
|
||||
* @param listener ExpandableLayoutListener
|
||||
*/
|
||||
void setListener(@NonNull final ExpandableLayoutListener listener);
|
||||
|
||||
/**
|
||||
* Sets the length of the animation.
|
||||
* The default duration is 300 milliseconds.
|
||||
*
|
||||
* @param duration
|
||||
*/
|
||||
void setDuration(final int duration);
|
||||
|
||||
/**
|
||||
* Sets state of expanse.
|
||||
*
|
||||
* @param expanded The layout is visible if expanded is true
|
||||
*/
|
||||
void setExpanded(final boolean expanded);
|
||||
|
||||
/**
|
||||
* Gets state of expanse.
|
||||
*
|
||||
* @return true if the layout is visible
|
||||
*/
|
||||
boolean isExpanded();
|
||||
|
||||
/**
|
||||
* The time interpolator used in calculating the elapsed fraction of this animation. The
|
||||
* interpolator determines whether the animation runs with linear or non-linear motion,
|
||||
* such as acceleration and deceleration.
|
||||
* The default value is {@link android.view.animation.AccelerateDecelerateInterpolator}
|
||||
*
|
||||
* @param interpolator
|
||||
*/
|
||||
void setInterpolator(@NonNull final TimeInterpolator interpolator);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.github.aakira.expandablelayout;
|
||||
|
||||
public interface ExpandableLayoutListener {
|
||||
/**
|
||||
* Notifies the start of the animation.
|
||||
* Sync from android.animation.Animator.AnimatorListener.onAnimationStart(Animator animation)
|
||||
*/
|
||||
void onAnimationStart();
|
||||
|
||||
/**
|
||||
* Notifies the end of the animation.
|
||||
* Sync from android.animation.Animator.AnimatorListener.onAnimationEnd(Animator animation)
|
||||
*/
|
||||
void onAnimationEnd();
|
||||
|
||||
/**
|
||||
* Notifies the layout is going to open.
|
||||
*/
|
||||
void onPreOpen();
|
||||
|
||||
/**
|
||||
* Notifies the layout is going to equal close size.
|
||||
*/
|
||||
void onPreClose();
|
||||
|
||||
/**
|
||||
* Notifies the layout opened.
|
||||
*/
|
||||
void onOpened();
|
||||
|
||||
/**
|
||||
* Notifies the layout size equal closed size.
|
||||
*/
|
||||
void onClosed();
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.github.aakira.expandablelayout;
|
||||
|
||||
public abstract class ExpandableLayoutListenerAdapter implements ExpandableLayoutListener {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onAnimationStart() {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onAnimationEnd() {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onPreOpen() {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onPreClose() {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onOpened() {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onClosed() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,565 @@
|
||||
package com.github.aakira.expandablelayout;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Build;
|
||||
import android.os.Parcelable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.widget.LinearLayout;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ExpandableLinearLayout extends LinearLayout implements ExpandableLayout {
|
||||
|
||||
private int duration;
|
||||
private TimeInterpolator interpolator = new LinearInterpolator();
|
||||
/**
|
||||
* Default state of expanse
|
||||
*
|
||||
* @see #defaultChildIndex
|
||||
* @see #defaultPosition
|
||||
*/
|
||||
private boolean defaultExpanded;
|
||||
/**
|
||||
* You cannot define {@link #defaultExpanded}, {@link #defaultChildIndex}
|
||||
* and {@link #defaultPosition} at the same time.
|
||||
* {@link #defaultPosition} has priority over {@link #defaultExpanded}
|
||||
* and {@link #defaultChildIndex} if you set them at the same time.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* Priority
|
||||
* {@link #defaultPosition} > {@link #defaultChildIndex} > {@link #defaultExpanded}
|
||||
*/
|
||||
private int defaultChildIndex;
|
||||
private int defaultPosition;
|
||||
/**
|
||||
* The close position is width from left of layout if orientation is horizontal.
|
||||
* The close position is height from top of layout if orientation is vertical.
|
||||
*/
|
||||
private int closePosition = 0;
|
||||
|
||||
private ExpandableLayoutListener listener;
|
||||
private ExpandableSavedState savedState;
|
||||
private boolean isExpanded;
|
||||
private int layoutSize = 0;
|
||||
private boolean inRecyclerView = false;
|
||||
private boolean isArranged = false;
|
||||
private boolean isCalculatedSize = false;
|
||||
private boolean isAnimating = false;
|
||||
/**
|
||||
* State of expanse in recycler view.
|
||||
*/
|
||||
private boolean recyclerExpanded = false;
|
||||
/**
|
||||
* view size of children
|
||||
**/
|
||||
private List<Integer> childSizeList = new ArrayList<>();
|
||||
private ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener;
|
||||
|
||||
public ExpandableLinearLayout(final Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ExpandableLinearLayout(final Context context, final AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public ExpandableLinearLayout(final Context context, final AttributeSet attrs,
|
||||
final int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public ExpandableLinearLayout(final Context context, final AttributeSet attrs,
|
||||
final int defStyleAttr, final int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
init(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
private void init(final Context context, final AttributeSet attrs, final int defStyleAttr) {
|
||||
final TypedArray a = context.obtainStyledAttributes(
|
||||
attrs, R.styleable.expandableLayout, defStyleAttr, 0);
|
||||
duration = a.getInteger(R.styleable.expandableLayout_ael_duration, DEFAULT_DURATION);
|
||||
defaultExpanded = a.getBoolean(R.styleable.expandableLayout_ael_expanded, DEFAULT_EXPANDED);
|
||||
defaultChildIndex = a.getInteger(R.styleable.expandableLayout_ael_defaultChildIndex,
|
||||
Integer.MAX_VALUE);
|
||||
defaultPosition = a.getDimensionPixelSize(R.styleable.expandableLayout_ael_defaultPosition,
|
||||
Integer.MIN_VALUE);
|
||||
final int interpolatorType = a.getInteger(R.styleable.expandableLayout_ael_interpolator,
|
||||
Utils.LINEAR_INTERPOLATOR);
|
||||
a.recycle();
|
||||
interpolator = Utils.createInterpolator(interpolatorType);
|
||||
isExpanded = defaultExpanded;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
if (!isCalculatedSize) {
|
||||
// calculate a size of children
|
||||
childSizeList.clear();
|
||||
final int childCount = getChildCount();
|
||||
|
||||
if (childCount > 0) {
|
||||
int sumSize = 0;
|
||||
View view;
|
||||
LayoutParams params;
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
view = getChildAt(i);
|
||||
params = (LayoutParams) view.getLayoutParams();
|
||||
|
||||
if (0 < i) {
|
||||
sumSize = childSizeList.get(i - 1);
|
||||
}
|
||||
childSizeList.add(
|
||||
(isVertical()
|
||||
? view.getMeasuredHeight() + params.topMargin + params.bottomMargin
|
||||
: view.getMeasuredWidth() + params.leftMargin + params.rightMargin
|
||||
) + sumSize);
|
||||
}
|
||||
layoutSize = childSizeList.get(childCount - 1) +
|
||||
(isVertical()
|
||||
? getPaddingTop() + getPaddingBottom()
|
||||
: getPaddingLeft() + getPaddingRight()
|
||||
);
|
||||
isCalculatedSize = true;
|
||||
} else {
|
||||
throw new IllegalStateException("The expandableLinearLayout must have at least one child");
|
||||
}
|
||||
}
|
||||
|
||||
if (isArranged) return;
|
||||
|
||||
// adjust default position if a user set a value.
|
||||
if (!defaultExpanded) {
|
||||
setLayoutSize(closePosition);
|
||||
}
|
||||
if (inRecyclerView) {
|
||||
setLayoutSize(recyclerExpanded ? layoutSize : closePosition);
|
||||
}
|
||||
final int childNumbers = childSizeList.size();
|
||||
if (childNumbers > defaultChildIndex && childNumbers > 0) {
|
||||
moveChild(defaultChildIndex, 0, null);
|
||||
}
|
||||
if (defaultPosition > 0 && layoutSize >= defaultPosition && layoutSize > 0) {
|
||||
move(defaultPosition, 0, null);
|
||||
}
|
||||
isArranged = true;
|
||||
|
||||
if (savedState == null) return;
|
||||
setLayoutSize(savedState.getSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Parcelable onSaveInstanceState() {
|
||||
final Parcelable parcelable = super.onSaveInstanceState();
|
||||
final ExpandableSavedState ss = new ExpandableSavedState(parcelable);
|
||||
ss.setSize(getCurrentPosition());
|
||||
return ss;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(final Parcelable state) {
|
||||
if (!(state instanceof ExpandableSavedState)) {
|
||||
super.onRestoreInstanceState(state);
|
||||
return;
|
||||
}
|
||||
|
||||
final ExpandableSavedState ss = (ExpandableSavedState) state;
|
||||
super.onRestoreInstanceState(ss.getSuperState());
|
||||
savedState = ss;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setListener(@NonNull ExpandableLayoutListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void toggle() {
|
||||
toggle(duration, interpolator);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void toggle(final long duration, final @Nullable TimeInterpolator interpolator) {
|
||||
if (closePosition < getCurrentPosition()) {
|
||||
collapse(duration, interpolator);
|
||||
} else {
|
||||
expand(duration, interpolator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void expand() {
|
||||
if (isAnimating) return;
|
||||
|
||||
createExpandAnimator(getCurrentPosition(), layoutSize, duration, interpolator).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void expand(final long duration, final @Nullable TimeInterpolator interpolator) {
|
||||
if (isAnimating) return;
|
||||
|
||||
if (duration <= 0) {
|
||||
move(layoutSize, duration, interpolator);
|
||||
return;
|
||||
}
|
||||
createExpandAnimator(getCurrentPosition(), layoutSize, duration, interpolator).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void collapse() {
|
||||
if (isAnimating) return;
|
||||
|
||||
createExpandAnimator(getCurrentPosition(), closePosition, duration, interpolator).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void collapse(final long duration, final @Nullable TimeInterpolator interpolator) {
|
||||
if (isAnimating) return;
|
||||
|
||||
if (duration <= 0) {
|
||||
move(closePosition, duration, interpolator);
|
||||
return;
|
||||
}
|
||||
createExpandAnimator(getCurrentPosition(), closePosition, duration, interpolator).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setDuration(final int duration) {
|
||||
if (duration < 0) {
|
||||
throw new IllegalArgumentException("Animators cannot have negative duration: " +
|
||||
duration);
|
||||
}
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setExpanded(final boolean expanded) {
|
||||
if (inRecyclerView) recyclerExpanded = expanded;
|
||||
|
||||
final int currentPosition = getCurrentPosition();
|
||||
if ((expanded && (currentPosition == layoutSize))
|
||||
|| (!expanded && currentPosition == closePosition)) return;
|
||||
|
||||
isExpanded = expanded;
|
||||
setLayoutSize(expanded ? layoutSize : closePosition);
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isExpanded() {
|
||||
return isExpanded;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setInterpolator(@NonNull final TimeInterpolator interpolator) {
|
||||
this.interpolator = interpolator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this layout.
|
||||
*/
|
||||
public void initLayout() {
|
||||
closePosition = 0;
|
||||
layoutSize = 0;
|
||||
isArranged = false;
|
||||
isCalculatedSize = false;
|
||||
savedState = null;
|
||||
|
||||
if (isVertical()) {
|
||||
measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.UNSPECIFIED));
|
||||
} else {
|
||||
measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.UNSPECIFIED),
|
||||
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param position
|
||||
*
|
||||
* @see #move(int, long, TimeInterpolator)
|
||||
*/
|
||||
public void move(int position) {
|
||||
move(position, duration, interpolator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves to position.
|
||||
* Sets 0 to duration if you want to move immediately.
|
||||
*
|
||||
* @param position
|
||||
* @param duration
|
||||
* @param interpolator use the default interpolator if the argument is null.
|
||||
*/
|
||||
public void move(int position, long duration, @Nullable TimeInterpolator interpolator) {
|
||||
if (isAnimating || 0 > position || layoutSize < position) return;
|
||||
|
||||
if (duration <= 0) {
|
||||
isExpanded = position > closePosition;
|
||||
setLayoutSize(position);
|
||||
requestLayout();
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
createExpandAnimator(getCurrentPosition(), position, duration,
|
||||
interpolator == null ? this.interpolator : interpolator).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index child view index
|
||||
*
|
||||
* @see #moveChild(int, long, TimeInterpolator)
|
||||
*/
|
||||
public void moveChild(int index) {
|
||||
moveChild(index, duration, interpolator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves to bottom(VERTICAL) or right(HORIZONTAL) of child view
|
||||
* Sets 0 to duration if you want to move immediately.
|
||||
*
|
||||
* @param index index child view index
|
||||
* @param duration
|
||||
* @param interpolator use the default interpolator if the argument is null.
|
||||
*/
|
||||
public void moveChild(int index, long duration, @Nullable TimeInterpolator interpolator) {
|
||||
if (isAnimating) return;
|
||||
|
||||
final int destination = getChildPosition(index) +
|
||||
(isVertical() ? getPaddingBottom() : getPaddingRight());
|
||||
if (duration <= 0) {
|
||||
isExpanded = destination > closePosition;
|
||||
setLayoutSize(destination);
|
||||
requestLayout();
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
createExpandAnimator(getCurrentPosition(), destination,
|
||||
duration, interpolator == null ? this.interpolator : interpolator).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the width from left of layout if orientation is horizontal.
|
||||
* Gets the height from top of layout if orientation is vertical.
|
||||
*
|
||||
* @param index index of child view
|
||||
*
|
||||
* @return position from top or left
|
||||
*/
|
||||
public int getChildPosition(final int index) {
|
||||
if (0 > index || childSizeList.size() <= index) {
|
||||
throw new IllegalArgumentException("There aren't the view having this index.");
|
||||
}
|
||||
return childSizeList.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the width from left of layout if orientation is horizontal.
|
||||
* Gets the height from top of layout if orientation is vertical.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @see #closePosition
|
||||
*/
|
||||
public int getClosePosition() {
|
||||
return closePosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the close position directly.
|
||||
*
|
||||
* @param position
|
||||
*
|
||||
* @see #closePosition
|
||||
* @see #setClosePositionIndex(int)
|
||||
*/
|
||||
public void setClosePosition(final int position) {
|
||||
this.closePosition = position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current position.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getCurrentPosition() {
|
||||
return isVertical() ? getMeasuredHeight() : getMeasuredWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets close position using index of child view.
|
||||
*
|
||||
* @param childIndex
|
||||
*
|
||||
* @see #closePosition
|
||||
* @see #setClosePosition(int)
|
||||
*/
|
||||
public void setClosePositionIndex(final int childIndex) {
|
||||
this.closePosition = getChildPosition(childIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set true if expandable layout is used in recycler view.
|
||||
*
|
||||
* @param inRecyclerView
|
||||
*/
|
||||
public void setInRecyclerView(final boolean inRecyclerView) {
|
||||
this.inRecyclerView = inRecyclerView;
|
||||
}
|
||||
|
||||
private boolean isVertical() {
|
||||
return getOrientation() == LinearLayout.VERTICAL;
|
||||
}
|
||||
|
||||
private void setLayoutSize(int size) {
|
||||
if (isVertical()) {
|
||||
getLayoutParams().height = size;
|
||||
} else {
|
||||
getLayoutParams().width = size;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates value animator.
|
||||
* Expand the layout if {@param to} is bigger than {@param from}.
|
||||
* Collapse the layout if {@param from} is bigger than {@param to}.
|
||||
*
|
||||
* @param from
|
||||
* @param to
|
||||
* @param duration
|
||||
* @param interpolator
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private ValueAnimator createExpandAnimator(
|
||||
final int from, final int to, final long duration, final TimeInterpolator interpolator) {
|
||||
final ValueAnimator valueAnimator = ValueAnimator.ofInt(from, to);
|
||||
valueAnimator.setDuration(duration);
|
||||
valueAnimator.setInterpolator(interpolator);
|
||||
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(final ValueAnimator animator) {
|
||||
if (isVertical()) {
|
||||
getLayoutParams().height = (int) animator.getAnimatedValue();
|
||||
} else {
|
||||
getLayoutParams().width = (int) animator.getAnimatedValue();
|
||||
}
|
||||
requestLayout();
|
||||
}
|
||||
});
|
||||
valueAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animator) {
|
||||
isAnimating = true;
|
||||
if (listener == null) return;
|
||||
|
||||
listener.onAnimationStart();
|
||||
if (layoutSize == to) {
|
||||
listener.onPreOpen();
|
||||
return;
|
||||
}
|
||||
if (closePosition == to) {
|
||||
listener.onPreClose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
isAnimating = false;
|
||||
isExpanded = to > closePosition;
|
||||
|
||||
if (listener == null) return;
|
||||
|
||||
listener.onAnimationEnd();
|
||||
if (to == layoutSize) {
|
||||
listener.onOpened();
|
||||
return;
|
||||
}
|
||||
if (to == closePosition) {
|
||||
listener.onClosed();
|
||||
}
|
||||
}
|
||||
});
|
||||
return valueAnimator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify listeners
|
||||
*/
|
||||
private void notifyListeners() {
|
||||
if (listener == null) return;
|
||||
|
||||
listener.onAnimationStart();
|
||||
if (isExpanded) {
|
||||
listener.onPreOpen();
|
||||
} else {
|
||||
listener.onPreClose();
|
||||
}
|
||||
mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
|
||||
getViewTreeObserver().removeGlobalOnLayoutListener(mGlobalLayoutListener);
|
||||
} else {
|
||||
getViewTreeObserver().removeOnGlobalLayoutListener(mGlobalLayoutListener);
|
||||
}
|
||||
|
||||
listener.onAnimationEnd();
|
||||
if (isExpanded) {
|
||||
listener.onOpened();
|
||||
} else {
|
||||
listener.onClosed();
|
||||
}
|
||||
}
|
||||
};
|
||||
getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,553 @@
|
||||
package com.github.aakira.expandablelayout;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Build;
|
||||
import android.os.Parcelable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.widget.RelativeLayout;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ExpandableRelativeLayout extends RelativeLayout implements ExpandableLayout {
|
||||
|
||||
private int duration;
|
||||
private TimeInterpolator interpolator = new LinearInterpolator();
|
||||
private int orientation;
|
||||
/**
|
||||
* Default state of expanse
|
||||
*
|
||||
* @see #defaultChildIndex
|
||||
* @see #defaultPosition
|
||||
*/
|
||||
private boolean defaultExpanded;
|
||||
/**
|
||||
* You cannot define {@link #defaultExpanded}, {@link #defaultChildIndex}
|
||||
* and {@link #defaultPosition} at the same time.
|
||||
* {@link #defaultPosition} has priority over {@link #defaultExpanded}
|
||||
* and {@link #defaultChildIndex} if you set them at the same time.
|
||||
* <p>
|
||||
* <p>
|
||||
* Priority
|
||||
* {@link #defaultPosition} > {@link #defaultChildIndex} > {@link #defaultExpanded}
|
||||
*/
|
||||
private int defaultChildIndex;
|
||||
private int defaultPosition;
|
||||
/**
|
||||
* The close position is width from left of layout if orientation is horizontal.
|
||||
* The close position is height from top of layout if orientation is vertical.
|
||||
*/
|
||||
private int closePosition = 0;
|
||||
|
||||
private ExpandableLayoutListener listener;
|
||||
private ExpandableSavedState savedState;
|
||||
private boolean isExpanded;
|
||||
private int layoutSize = 0;
|
||||
private boolean isArranged = false;
|
||||
private boolean isCalculatedSize = false;
|
||||
private boolean isAnimating = false;
|
||||
/**
|
||||
* view size of children
|
||||
**/
|
||||
private List<Integer> childSizeList = new ArrayList<>();
|
||||
/**
|
||||
* view position top or left of children
|
||||
**/
|
||||
private List<Integer> childPositionList = new ArrayList<>();
|
||||
private ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener;
|
||||
|
||||
public ExpandableRelativeLayout(final Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ExpandableRelativeLayout(final Context context, final AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public ExpandableRelativeLayout(final Context context, final AttributeSet attrs,
|
||||
final int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public ExpandableRelativeLayout(final Context context, final AttributeSet attrs,
|
||||
final int defStyleAttr, final int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
init(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
private void init(final Context context, final AttributeSet attrs, final int defStyleAttr) {
|
||||
final TypedArray a = context.obtainStyledAttributes(
|
||||
attrs, R.styleable.expandableLayout, defStyleAttr, 0);
|
||||
duration = a.getInteger(R.styleable.expandableLayout_ael_duration, DEFAULT_DURATION);
|
||||
defaultExpanded = a.getBoolean(R.styleable.expandableLayout_ael_expanded, DEFAULT_EXPANDED);
|
||||
orientation = a.getInteger(R.styleable.expandableLayout_ael_orientation, VERTICAL);
|
||||
defaultChildIndex = a.getInteger(R.styleable.expandableLayout_ael_defaultChildIndex,
|
||||
Integer.MAX_VALUE);
|
||||
defaultPosition = a.getDimensionPixelSize(R.styleable.expandableLayout_ael_defaultPosition,
|
||||
Integer.MIN_VALUE);
|
||||
final int interpolatorType = a.getInteger(R.styleable.expandableLayout_ael_interpolator,
|
||||
Utils.LINEAR_INTERPOLATOR);
|
||||
a.recycle();
|
||||
interpolator = Utils.createInterpolator(interpolatorType);
|
||||
isExpanded = defaultExpanded;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
if (isCalculatedSize) return;
|
||||
|
||||
final int measureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||
if (isVertical()) {
|
||||
int measuredHeight = getMeasuredHeight();
|
||||
|
||||
super.onMeasure(widthMeasureSpec, measureSpec);
|
||||
layoutSize = getMeasuredHeight();
|
||||
|
||||
setMeasuredDimension(getMeasuredWidth(), measuredHeight);
|
||||
} else {
|
||||
int measuredWidth = getMeasuredWidth();
|
||||
|
||||
super.onMeasure(measureSpec, heightMeasureSpec);
|
||||
layoutSize = getMeasuredWidth();
|
||||
|
||||
setMeasuredDimension(measuredWidth, getMeasuredHeight());
|
||||
}
|
||||
|
||||
// calculate a size of children
|
||||
childSizeList.clear();
|
||||
View view;
|
||||
LayoutParams params;
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
view = getChildAt(i);
|
||||
params = (LayoutParams) view.getLayoutParams();
|
||||
|
||||
childSizeList.add(isVertical()
|
||||
? view.getMeasuredHeight() + params.topMargin + params.bottomMargin
|
||||
: view.getMeasuredWidth() + params.leftMargin + params.rightMargin);
|
||||
}
|
||||
isCalculatedSize = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
super.onLayout(changed, l, t, r, b);
|
||||
|
||||
if (isArranged) return;
|
||||
|
||||
childPositionList.clear();
|
||||
// calculate a top position of children
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
childPositionList.add((int) (isVertical() ? getChildAt(i).getY() : getChildAt(i).getX()));
|
||||
}
|
||||
|
||||
// adjust default position if a user set a value.
|
||||
if (!defaultExpanded) {
|
||||
setLayoutSize(closePosition);
|
||||
}
|
||||
final int childNumbers = childSizeList.size();
|
||||
if (childNumbers > defaultChildIndex && childNumbers > 0) {
|
||||
moveChild(defaultChildIndex, 0, null);
|
||||
}
|
||||
if (defaultPosition > 0 && layoutSize >= defaultPosition && layoutSize > 0) {
|
||||
move(defaultPosition, 0, null);
|
||||
}
|
||||
isArranged = true;
|
||||
|
||||
if (savedState == null) return;
|
||||
setLayoutSize(savedState.getSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Parcelable onSaveInstanceState() {
|
||||
final Parcelable parcelable = super.onSaveInstanceState();
|
||||
final ExpandableSavedState ss = new ExpandableSavedState(parcelable);
|
||||
ss.setSize(getCurrentPosition());
|
||||
return ss;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(final Parcelable state) {
|
||||
if (!(state instanceof ExpandableSavedState)) {
|
||||
super.onRestoreInstanceState(state);
|
||||
return;
|
||||
}
|
||||
|
||||
final ExpandableSavedState ss = (ExpandableSavedState) state;
|
||||
super.onRestoreInstanceState(ss.getSuperState());
|
||||
savedState = ss;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setListener(@NonNull ExpandableLayoutListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void toggle() {
|
||||
toggle(duration, interpolator);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void toggle(final long duration, final @Nullable TimeInterpolator interpolator) {
|
||||
if (closePosition < getCurrentPosition()) {
|
||||
collapse(duration, interpolator);
|
||||
} else {
|
||||
expand(duration, interpolator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void expand() {
|
||||
if (isAnimating) return;
|
||||
|
||||
createExpandAnimator(getCurrentPosition(), layoutSize, duration, interpolator).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void expand(final long duration, final @Nullable TimeInterpolator interpolator) {
|
||||
if (isAnimating) return;
|
||||
|
||||
if (duration <= 0) {
|
||||
move(layoutSize, duration, interpolator);
|
||||
return;
|
||||
}
|
||||
createExpandAnimator(getCurrentPosition(), layoutSize, duration, interpolator).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void collapse() {
|
||||
if (isAnimating) return;
|
||||
|
||||
createExpandAnimator(getCurrentPosition(), closePosition, duration, interpolator).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void collapse(final long duration, final @Nullable TimeInterpolator interpolator) {
|
||||
if (isAnimating) return;
|
||||
|
||||
if (duration <= 0) {
|
||||
move(closePosition, duration, interpolator);
|
||||
return;
|
||||
}
|
||||
createExpandAnimator(getCurrentPosition(), closePosition, duration, interpolator).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setDuration(final int duration) {
|
||||
if (duration < 0) {
|
||||
throw new IllegalArgumentException("Animators cannot have negative duration: " +
|
||||
duration);
|
||||
}
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setExpanded(boolean expanded) {
|
||||
final int currentPosition = getCurrentPosition();
|
||||
if ((expanded && (currentPosition == layoutSize))
|
||||
|| (!expanded && currentPosition == closePosition)) return;
|
||||
|
||||
isExpanded = expanded;
|
||||
setLayoutSize(expanded ? layoutSize : closePosition);
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isExpanded() {
|
||||
return isExpanded;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setInterpolator(@NonNull final TimeInterpolator interpolator) {
|
||||
this.interpolator = interpolator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param position
|
||||
*
|
||||
* @see #move(int, long, TimeInterpolator)
|
||||
*/
|
||||
public void move(int position) {
|
||||
move(position, duration, interpolator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves to position.
|
||||
* Sets 0 to duration if you want to move immediately.
|
||||
*
|
||||
* @param position
|
||||
* @param duration
|
||||
* @param interpolator use the default interpolator if the argument is null.
|
||||
*/
|
||||
public void move(int position, long duration, @Nullable TimeInterpolator interpolator) {
|
||||
if (isAnimating || 0 > position || layoutSize < position) return;
|
||||
|
||||
if (duration <= 0) {
|
||||
isExpanded = position > closePosition;
|
||||
setLayoutSize(position);
|
||||
requestLayout();
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
createExpandAnimator(getCurrentPosition(), position, duration,
|
||||
interpolator == null ? this.interpolator : interpolator).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index child view index
|
||||
*
|
||||
* @see #moveChild(int, long, TimeInterpolator)
|
||||
*/
|
||||
public void moveChild(int index) {
|
||||
moveChild(index, duration, interpolator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves to bottom(VERTICAL) or right(HORIZONTAL) of child view
|
||||
* Sets 0 to duration if you want to move immediately.
|
||||
*
|
||||
* @param index index child view index
|
||||
* @param duration
|
||||
* @param interpolator use the default interpolator if the argument is null.
|
||||
*/
|
||||
public void moveChild(int index, long duration, @Nullable TimeInterpolator interpolator) {
|
||||
if (isAnimating) return;
|
||||
|
||||
final int destination = getChildPosition(index) +
|
||||
(isVertical() ? getPaddingBottom() : getPaddingRight());
|
||||
if (duration <= 0) {
|
||||
isExpanded = destination > closePosition;
|
||||
setLayoutSize(destination);
|
||||
requestLayout();
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
createExpandAnimator(getCurrentPosition(), destination,
|
||||
duration, interpolator == null ? this.interpolator : interpolator).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets orientation of expanse animation.
|
||||
*
|
||||
* @param orientation Set 0 if orientation is horizontal, 1 if orientation is vertical
|
||||
*/
|
||||
public void setOrientation(@Orientation final int orientation) {
|
||||
this.orientation = orientation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the width from left of layout if orientation is horizontal.
|
||||
* Gets the height from top of layout if orientation is vertical.
|
||||
*
|
||||
* @param index index of child view
|
||||
*
|
||||
* @return position from top or left
|
||||
*/
|
||||
public int getChildPosition(final int index) {
|
||||
if (0 > index || childSizeList.size() <= index) {
|
||||
throw new IllegalArgumentException("There aren't the view having this index.");
|
||||
}
|
||||
return childPositionList.get(index) + childSizeList.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the width from left of layout if orientation is horizontal.
|
||||
* Gets the height from top of layout if orientation is vertical.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @see #closePosition
|
||||
*/
|
||||
public int getClosePosition() {
|
||||
return closePosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the close position directly.
|
||||
*
|
||||
* @param position
|
||||
*
|
||||
* @see #closePosition
|
||||
* @see #setClosePositionIndex(int)
|
||||
*/
|
||||
public void setClosePosition(final int position) {
|
||||
this.closePosition = position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current position.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getCurrentPosition() {
|
||||
return isVertical() ? getMeasuredHeight() : getMeasuredWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets close position using index of child view.
|
||||
*
|
||||
* @param childIndex
|
||||
*
|
||||
* @see #closePosition
|
||||
* @see #setClosePosition(int)
|
||||
*/
|
||||
public void setClosePositionIndex(final int childIndex) {
|
||||
this.closePosition = getChildPosition(childIndex);
|
||||
}
|
||||
|
||||
private boolean isVertical() {
|
||||
return orientation == VERTICAL;
|
||||
}
|
||||
|
||||
private void setLayoutSize(int size) {
|
||||
if (isVertical()) {
|
||||
getLayoutParams().height = size;
|
||||
} else {
|
||||
getLayoutParams().width = size;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates value animator.
|
||||
* Expand the layout if {@param to} is bigger than {@param from}.
|
||||
* Collapse the layout if {@param from} is bigger than {@param to}.
|
||||
*
|
||||
* @param from
|
||||
* @param to
|
||||
* @param duration
|
||||
* @param interpolator
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private ValueAnimator createExpandAnimator(
|
||||
final int from, final int to, final long duration, final TimeInterpolator interpolator) {
|
||||
final ValueAnimator valueAnimator = ValueAnimator.ofInt(from, to);
|
||||
valueAnimator.setDuration(duration);
|
||||
valueAnimator.setInterpolator(interpolator);
|
||||
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(final ValueAnimator animator) {
|
||||
if (isVertical()) {
|
||||
getLayoutParams().height = (int) animator.getAnimatedValue();
|
||||
} else {
|
||||
getLayoutParams().width = (int) animator.getAnimatedValue();
|
||||
}
|
||||
requestLayout();
|
||||
}
|
||||
});
|
||||
valueAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animator) {
|
||||
isAnimating = true;
|
||||
if (listener == null) return;
|
||||
|
||||
listener.onAnimationStart();
|
||||
if (layoutSize == to) {
|
||||
listener.onPreOpen();
|
||||
return;
|
||||
}
|
||||
if (closePosition == to) {
|
||||
listener.onPreClose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
isAnimating = false;
|
||||
isExpanded = to > closePosition;
|
||||
|
||||
if (listener == null) return;
|
||||
|
||||
listener.onAnimationEnd();
|
||||
if (to == layoutSize) {
|
||||
listener.onOpened();
|
||||
return;
|
||||
}
|
||||
if (to == closePosition) {
|
||||
listener.onClosed();
|
||||
}
|
||||
}
|
||||
});
|
||||
return valueAnimator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify listeners
|
||||
*/
|
||||
private void notifyListeners() {
|
||||
if (listener == null) return;
|
||||
|
||||
listener.onAnimationStart();
|
||||
if (isExpanded) {
|
||||
listener.onPreOpen();
|
||||
} else {
|
||||
listener.onPreClose();
|
||||
}
|
||||
mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
|
||||
getViewTreeObserver().removeGlobalOnLayoutListener(mGlobalLayoutListener);
|
||||
} else {
|
||||
getViewTreeObserver().removeOnGlobalLayoutListener(mGlobalLayoutListener);
|
||||
}
|
||||
|
||||
listener.onAnimationEnd();
|
||||
if (isExpanded) {
|
||||
listener.onOpened();
|
||||
} else {
|
||||
listener.onClosed();
|
||||
}
|
||||
}
|
||||
};
|
||||
getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.github.aakira.expandablelayout;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.view.View;
|
||||
|
||||
public class ExpandableSavedState extends View.BaseSavedState {
|
||||
private int size;
|
||||
private float weight;
|
||||
|
||||
ExpandableSavedState(Parcelable superState) {
|
||||
super(superState);
|
||||
}
|
||||
|
||||
private ExpandableSavedState(Parcel in) {
|
||||
super(in);
|
||||
this.size = in.readInt();
|
||||
this.weight = in.readFloat();
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public void setSize(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public float getWeight() {
|
||||
return this.weight;
|
||||
}
|
||||
|
||||
public void setWeight(float weight) {
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
super.writeToParcel(out, flags);
|
||||
out.writeInt(this.size);
|
||||
out.writeFloat(this.weight);
|
||||
}
|
||||
|
||||
public static final Creator<ExpandableSavedState> CREATOR =
|
||||
new Creator<ExpandableSavedState>() {
|
||||
public ExpandableSavedState createFromParcel(Parcel in) {
|
||||
return new ExpandableSavedState(in);
|
||||
}
|
||||
|
||||
public ExpandableSavedState[] newArray(int size) {
|
||||
return new ExpandableSavedState[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
package com.github.aakira.expandablelayout;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Build;
|
||||
import android.os.Parcelable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class ExpandableWeightLayout extends RelativeLayout implements ExpandableLayout {
|
||||
|
||||
private int duration;
|
||||
private TimeInterpolator interpolator = new LinearInterpolator();
|
||||
private boolean defaultExpanded;
|
||||
|
||||
private ExpandableLayoutListener listener;
|
||||
private ExpandableSavedState savedState;
|
||||
private boolean isExpanded;
|
||||
private float layoutWeight = 0.0f;
|
||||
private boolean isArranged = false;
|
||||
private boolean isCalculatedSize = false;
|
||||
private boolean isAnimating = false;
|
||||
private ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener;
|
||||
|
||||
public ExpandableWeightLayout(final Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ExpandableWeightLayout(final Context context, final AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public ExpandableWeightLayout(final Context context, final AttributeSet attrs,
|
||||
final int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public ExpandableWeightLayout(final Context context, final AttributeSet attrs,
|
||||
final int defStyleAttr, final int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
init(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
private void init(final Context context, final AttributeSet attrs, final int defStyleAttr) {
|
||||
final TypedArray a = context.obtainStyledAttributes(
|
||||
attrs, R.styleable.expandableLayout, defStyleAttr, 0);
|
||||
duration = a.getInteger(R.styleable.expandableLayout_ael_duration, DEFAULT_DURATION);
|
||||
defaultExpanded = a.getBoolean(R.styleable.expandableLayout_ael_expanded, DEFAULT_EXPANDED);
|
||||
final int interpolatorType = a.getInteger(R.styleable.expandableLayout_ael_interpolator,
|
||||
Utils.LINEAR_INTERPOLATOR);
|
||||
a.recycle();
|
||||
interpolator = Utils.createInterpolator(interpolatorType);
|
||||
isExpanded = defaultExpanded;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
|
||||
// Check this layout using the attribute of weight
|
||||
if (!(getLayoutParams() instanceof LinearLayout.LayoutParams)) {
|
||||
throw new AssertionError("You must arrange in LinearLayout.");
|
||||
}
|
||||
if (0 >= getCurrentWeight()) throw new AssertionError("You must set a weight than 0.");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
if (!isCalculatedSize) {
|
||||
layoutWeight = getCurrentWeight();
|
||||
isCalculatedSize = true;
|
||||
}
|
||||
|
||||
if (isArranged) return;
|
||||
setWeight(defaultExpanded ? layoutWeight : 0);
|
||||
isArranged = true;
|
||||
|
||||
if (savedState == null) return;
|
||||
setWeight(savedState.getWeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Parcelable onSaveInstanceState() {
|
||||
final Parcelable parcelable = super.onSaveInstanceState();
|
||||
|
||||
final ExpandableSavedState ss = new ExpandableSavedState(parcelable);
|
||||
ss.setWeight(getCurrentWeight());
|
||||
return ss;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(final Parcelable state) {
|
||||
if (!(state instanceof ExpandableSavedState)) {
|
||||
super.onRestoreInstanceState(state);
|
||||
return;
|
||||
}
|
||||
final ExpandableSavedState ss = (ExpandableSavedState) state;
|
||||
super.onRestoreInstanceState(ss.getSuperState());
|
||||
savedState = ss;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setListener(@NonNull ExpandableLayoutListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void toggle() {
|
||||
toggle(duration, interpolator);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void toggle(final long duration, @Nullable final TimeInterpolator interpolator) {
|
||||
if (0 < getCurrentWeight()) {
|
||||
collapse(duration, interpolator);
|
||||
} else {
|
||||
expand(duration, interpolator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void expand() {
|
||||
if (isAnimating) return;
|
||||
|
||||
createExpandAnimator(0, layoutWeight, duration, interpolator).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void expand(final long duration, @Nullable final TimeInterpolator interpolator) {
|
||||
if (isAnimating) return;
|
||||
|
||||
if (duration <= 0) {
|
||||
isExpanded = true;
|
||||
setWeight(layoutWeight);
|
||||
requestLayout();
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
createExpandAnimator(getCurrentWeight(), layoutWeight, duration, interpolator).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void collapse() {
|
||||
if (isAnimating) return;
|
||||
|
||||
createExpandAnimator(getCurrentWeight(), 0, duration, interpolator).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void collapse(final long duration, @Nullable final TimeInterpolator interpolator) {
|
||||
if (isAnimating) return;
|
||||
|
||||
if (duration <= 0) {
|
||||
isExpanded = false;
|
||||
setWeight(0);
|
||||
requestLayout();
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
createExpandAnimator(getCurrentWeight(), 0, duration, interpolator).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setDuration(@NonNull final int duration) {
|
||||
if (duration < 0) {
|
||||
throw new IllegalArgumentException("Animators cannot have negative duration: " +
|
||||
duration);
|
||||
}
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setExpanded(boolean expanded) {
|
||||
final float currentWeight = getCurrentWeight();
|
||||
if ((expanded && (currentWeight == layoutWeight))
|
||||
|| (!expanded && currentWeight == 0)) return;
|
||||
|
||||
isExpanded = expanded;
|
||||
setWeight(expanded ? layoutWeight : 0);
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isExpanded() {
|
||||
return isExpanded;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setInterpolator(@NonNull final TimeInterpolator interpolator) {
|
||||
this.interpolator = interpolator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets weight of expandable layout.
|
||||
*
|
||||
* @param expandWeight expand to this weight by {@link #expand()}
|
||||
*/
|
||||
public void setExpandWeight(final float expandWeight) {
|
||||
layoutWeight = expandWeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets current weight of expandable layout.
|
||||
*
|
||||
* @return weight
|
||||
*/
|
||||
public float getCurrentWeight() {
|
||||
return ((LinearLayout.LayoutParams) getLayoutParams()).weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param weight
|
||||
*
|
||||
* @see #move(float, long, TimeInterpolator)
|
||||
*/
|
||||
public void move(float weight) {
|
||||
move(weight, duration, interpolator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change to weight.
|
||||
* Sets 0 to duration if you want to move immediately.
|
||||
*
|
||||
* @param weight
|
||||
* @param duration
|
||||
* @param interpolator use the default interpolator if the argument is null.
|
||||
*/
|
||||
public void move(float weight, long duration, @Nullable TimeInterpolator interpolator) {
|
||||
if (isAnimating) return;
|
||||
|
||||
if (duration <= 0L) {
|
||||
isExpanded = weight > 0;
|
||||
setWeight(weight);
|
||||
requestLayout();
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
createExpandAnimator(getCurrentWeight(), weight, duration, interpolator).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates value animator.
|
||||
* Expand the layout if @param.to is bigger than @param.from.
|
||||
* Collapse the layout if @param.from is bigger than @param.to.
|
||||
*
|
||||
* @param from
|
||||
* @param to
|
||||
* @param duration
|
||||
* @param interpolator TimeInterpolator
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private ValueAnimator createExpandAnimator(final float from, final float to, final long duration,
|
||||
@Nullable final TimeInterpolator interpolator) {
|
||||
final ValueAnimator valueAnimator = ValueAnimator.ofFloat(from, to);
|
||||
valueAnimator.setDuration(duration);
|
||||
valueAnimator.setInterpolator(interpolator == null ? this.interpolator : interpolator);
|
||||
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(final ValueAnimator animation) {
|
||||
setWeight((float) animation.getAnimatedValue());
|
||||
requestLayout();
|
||||
}
|
||||
});
|
||||
valueAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
isAnimating = true;
|
||||
|
||||
if (listener == null) return;
|
||||
|
||||
listener.onAnimationStart();
|
||||
if (layoutWeight == to) {
|
||||
listener.onPreOpen();
|
||||
return;
|
||||
}
|
||||
if (0 == to) {
|
||||
listener.onPreClose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
isAnimating = false;
|
||||
isExpanded = to > 0;
|
||||
|
||||
if (listener == null) return;
|
||||
|
||||
listener.onAnimationEnd();
|
||||
if (to == layoutWeight) {
|
||||
listener.onOpened();
|
||||
return;
|
||||
}
|
||||
if (to == 0) {
|
||||
listener.onClosed();
|
||||
}
|
||||
}
|
||||
});
|
||||
return valueAnimator;
|
||||
}
|
||||
|
||||
private void setWeight(final float weight) {
|
||||
((LinearLayout.LayoutParams) getLayoutParams()).weight = weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify listeners
|
||||
*/
|
||||
private void notifyListeners() {
|
||||
if (listener == null) return;
|
||||
|
||||
listener.onAnimationStart();
|
||||
if (isExpanded) {
|
||||
listener.onPreOpen();
|
||||
} else {
|
||||
listener.onPreClose();
|
||||
}
|
||||
mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
|
||||
getViewTreeObserver().removeGlobalOnLayoutListener(mGlobalLayoutListener);
|
||||
} else {
|
||||
getViewTreeObserver().removeOnGlobalLayoutListener(mGlobalLayoutListener);
|
||||
}
|
||||
|
||||
listener.onAnimationEnd();
|
||||
if (isExpanded) {
|
||||
listener.onOpened();
|
||||
} else {
|
||||
listener.onClosed();
|
||||
}
|
||||
}
|
||||
};
|
||||
getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.github.aakira.expandablelayout;
|
||||
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.AnticipateInterpolator;
|
||||
import android.view.animation.AnticipateOvershootInterpolator;
|
||||
import android.view.animation.BounceInterpolator;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.view.animation.OvershootInterpolator;
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.interpolator.view.animation.FastOutLinearInInterpolator;
|
||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
||||
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator;
|
||||
|
||||
public class Utils {
|
||||
|
||||
public static final int ACCELERATE_DECELERATE_INTERPOLATOR = 0;
|
||||
public static final int ACCELERATE_INTERPOLATOR = 1;
|
||||
public static final int ANTICIPATE_INTERPOLATOR = 2;
|
||||
public static final int ANTICIPATE_OVERSHOOT_INTERPOLATOR = 3;
|
||||
public static final int BOUNCE_INTERPOLATOR = 4;
|
||||
public static final int DECELERATE_INTERPOLATOR = 5;
|
||||
public static final int FAST_OUT_LINEAR_IN_INTERPOLATOR = 6;
|
||||
public static final int FAST_OUT_SLOW_IN_INTERPOLATOR = 7;
|
||||
public static final int LINEAR_INTERPOLATOR = 8;
|
||||
public static final int LINEAR_OUT_SLOW_IN_INTERPOLATOR = 9;
|
||||
public static final int OVERSHOOT_INTERPOLATOR = 10;
|
||||
|
||||
/**
|
||||
* Creates interpolator.
|
||||
*
|
||||
* @param interpolatorType
|
||||
* @return
|
||||
*/
|
||||
public static TimeInterpolator createInterpolator(@IntRange(from = 0, to = 10) final int interpolatorType) {
|
||||
switch (interpolatorType) {
|
||||
case ACCELERATE_DECELERATE_INTERPOLATOR:
|
||||
return new AccelerateDecelerateInterpolator();
|
||||
case ACCELERATE_INTERPOLATOR:
|
||||
return new AccelerateInterpolator();
|
||||
case ANTICIPATE_INTERPOLATOR:
|
||||
return new AnticipateInterpolator();
|
||||
case ANTICIPATE_OVERSHOOT_INTERPOLATOR:
|
||||
return new AnticipateOvershootInterpolator();
|
||||
case BOUNCE_INTERPOLATOR:
|
||||
return new BounceInterpolator();
|
||||
case DECELERATE_INTERPOLATOR:
|
||||
return new DecelerateInterpolator();
|
||||
case FAST_OUT_LINEAR_IN_INTERPOLATOR:
|
||||
return new FastOutLinearInInterpolator();
|
||||
case FAST_OUT_SLOW_IN_INTERPOLATOR:
|
||||
return new FastOutSlowInInterpolator();
|
||||
case LINEAR_INTERPOLATOR:
|
||||
return new LinearInterpolator();
|
||||
case LINEAR_OUT_SLOW_IN_INTERPOLATOR:
|
||||
return new LinearOutSlowInInterpolator();
|
||||
case OVERSHOOT_INTERPOLATOR:
|
||||
return new OvershootInterpolator();
|
||||
default:
|
||||
return new LinearInterpolator();
|
||||
}
|
||||
}
|
||||
}
|
||||
26
modules/expandable-layout/src/main/res/values/attrs.xml
Normal file
26
modules/expandable-layout/src/main/res/values/attrs.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<declare-styleable name="expandableLayout">
|
||||
<attr name="ael_duration" format="integer" />
|
||||
<attr name="ael_expanded" format="boolean" />
|
||||
<attr name="ael_defaultChildIndex" format="integer" />
|
||||
<attr name="ael_defaultPosition" format="dimension" />
|
||||
<attr name="ael_orientation" format="enum">
|
||||
<enum name="horizontal" value="0" />
|
||||
<enum name="vertical" value="1" />
|
||||
</attr>
|
||||
<attr name="ael_interpolator" format="enum">
|
||||
<enum name="accelerateDecelerate" value="0" />
|
||||
<enum name="accelerate" value="1" />
|
||||
<enum name="anticipate" value="2" />
|
||||
<enum name="anticipateOvershoot" value="3" />
|
||||
<enum name="bounce" value="4" />
|
||||
<enum name="decelerate" value="5" />
|
||||
<enum name="fastOutLinearIn" value="6" />
|
||||
<enum name="fastOutSlowIn" value="7" />
|
||||
<enum name="linear" value="8" />
|
||||
<enum name="linearOutSlowIn" value="9" />
|
||||
<enum name="overshoot" value="10" />
|
||||
</attr>
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
21
modules/expandable-recyclerview/build.gradle
Normal file
21
modules/expandable-recyclerview/build.gradle
Normal file
@@ -0,0 +1,21 @@
|
||||
plugins {
|
||||
id("org.autojs.build.versions")
|
||||
id("org.autojs.build.jvm-convention")
|
||||
id("com.android.library")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace "com.bignerdranch.expandablerecyclerview"
|
||||
compileSdkVersion versions.sdkVersionCompile
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion versions.sdkVersionMin
|
||||
targetSdkVersion versions.sdkVersionTarget
|
||||
versionName "3.0.0-RC1 Mod"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation libs.annotation
|
||||
implementation libs.recyclerview
|
||||
}
|
||||
17
modules/expandable-recyclerview/proguard-rules.pro
vendored
Normal file
17
modules/expandable-recyclerview/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Applications/eclipse/android/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 @@
|
||||
<manifest />
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.bignerdranch.expandablerecyclerview;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* ViewHolder for a child list item.
|
||||
* <p>
|
||||
* The user should extend this class and implement as they wish for their
|
||||
* child list item.
|
||||
*/
|
||||
public class ChildViewHolder<C> extends RecyclerView.ViewHolder {
|
||||
C mChild;
|
||||
ExpandableRecyclerAdapter mExpandableAdapter;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*
|
||||
* @param itemView The {@link View} being hosted in this ViewHolder
|
||||
*/
|
||||
public ChildViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the childListItem associated with this view holder
|
||||
*/
|
||||
@UiThread
|
||||
public C getChild() {
|
||||
return mChild;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the adapter position of the Parent associated with this ChildViewHolder
|
||||
*
|
||||
* @return The adapter position of the Parent if it still exists in the adapter.
|
||||
* RecyclerView.NO_POSITION if item has been removed from the adapter,
|
||||
* RecyclerView.Adapter.notifyDataSetChanged() has been called after the last
|
||||
* layout pass or the ViewHolder has already been recycled.
|
||||
*/
|
||||
@UiThread
|
||||
public int getParentAdapterPosition() {
|
||||
int flatPosition = getAdapterPosition();
|
||||
if (mExpandableAdapter == null || flatPosition == RecyclerView.NO_POSITION) {
|
||||
return RecyclerView.NO_POSITION;
|
||||
}
|
||||
|
||||
return mExpandableAdapter.getNearestParentPosition(flatPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the adapter position of the Child associated with this ChildViewHolder
|
||||
*
|
||||
* @return The adapter position of the Child if it still exists in the adapter.
|
||||
* RecyclerView.NO_POSITION if item has been removed from the adapter,
|
||||
* RecyclerView.Adapter.notifyDataSetChanged() has been called after the last
|
||||
* layout pass or the ViewHolder has already been recycled.
|
||||
*/
|
||||
@UiThread
|
||||
public int getChildAdapterPosition() {
|
||||
int flatPosition = getAdapterPosition();
|
||||
if (mExpandableAdapter == null || flatPosition == RecyclerView.NO_POSITION) {
|
||||
return RecyclerView.NO_POSITION;
|
||||
}
|
||||
|
||||
return mExpandableAdapter.getChildPosition(flatPosition);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,201 @@
|
||||
package com.bignerdranch.expandablerecyclerview;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
|
||||
import com.bignerdranch.expandablerecyclerview.model.Parent;
|
||||
|
||||
/**
|
||||
* ViewHolder for a {@link Parent}
|
||||
* Keeps track of expanded state and holds callbacks which can be used to
|
||||
* trigger expansion-based events.
|
||||
*
|
||||
* @author Ryan Brooks
|
||||
* @version 1.0
|
||||
* @since 5/27/2015
|
||||
*/
|
||||
public class ParentViewHolder<P extends Parent<C>, C> extends RecyclerView.ViewHolder implements View.OnClickListener {
|
||||
@Nullable
|
||||
private ParentViewHolderExpandCollapseListener mParentViewHolderExpandCollapseListener;
|
||||
private boolean mExpanded;
|
||||
P mParent;
|
||||
ExpandableRecyclerAdapter mExpandableAdapter;
|
||||
|
||||
/**
|
||||
* Empowers {@link com.bignerdranch.expandablerecyclerview.ExpandableRecyclerAdapter}
|
||||
* implementations to be notified of expand/collapse state change events.
|
||||
*/
|
||||
interface ParentViewHolderExpandCollapseListener {
|
||||
|
||||
/**
|
||||
* Called when a parent is expanded.
|
||||
*
|
||||
* @param flatParentPosition The index of the parent in the list being expanded
|
||||
*/
|
||||
@UiThread
|
||||
void onParentExpanded(int flatParentPosition);
|
||||
|
||||
/**
|
||||
* Called when a parent is collapsed.
|
||||
*
|
||||
* @param flatParentPosition The index of the parent in the list being collapsed
|
||||
*/
|
||||
@UiThread
|
||||
void onParentCollapsed(int flatParentPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*
|
||||
* @param itemView The {@link View} being hosted in this ViewHolder
|
||||
*/
|
||||
@UiThread
|
||||
public ParentViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
mExpanded = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the Parent associated with this ViewHolder
|
||||
*/
|
||||
@UiThread
|
||||
public P getParent() {
|
||||
return mParent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the adapter position of the Parent associated with this ParentViewHolder
|
||||
*
|
||||
* @return The adapter position of the Parent if it still exists in the adapter.
|
||||
* RecyclerView.NO_POSITION if item has been removed from the adapter,
|
||||
* RecyclerView.Adapter.notifyDataSetChanged() has been called after the last
|
||||
* layout pass or the ViewHolder has already been recycled.
|
||||
*/
|
||||
@UiThread
|
||||
public int getParentAdapterPosition() {
|
||||
int flatPosition = getAdapterPosition();
|
||||
if (flatPosition == RecyclerView.NO_POSITION) {
|
||||
return flatPosition;
|
||||
}
|
||||
|
||||
return mExpandableAdapter.getNearestParentPosition(flatPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a {@link android.view.View.OnClickListener} on the entire parent
|
||||
* view to trigger expansion.
|
||||
*/
|
||||
@UiThread
|
||||
public void setMainItemClickToExpand() {
|
||||
itemView.setOnClickListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns expanded state for the {@link Parent}
|
||||
* corresponding to this {@link ParentViewHolder}.
|
||||
*
|
||||
* @return true if expanded, false if not
|
||||
*/
|
||||
@UiThread
|
||||
public boolean isExpanded() {
|
||||
return mExpanded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter method for expanded state, used for initialization of expanded state.
|
||||
* changes to the state are given in {@link #onExpansionToggled(boolean)}
|
||||
*
|
||||
* @param expanded true if expanded, false if not
|
||||
*/
|
||||
@UiThread
|
||||
public void setExpanded(boolean expanded) {
|
||||
mExpanded = expanded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback triggered when expansion state is changed, but not during
|
||||
* initialization.
|
||||
* <p>
|
||||
* Useful for implementing animations on expansion.
|
||||
*
|
||||
* @param expanded true if view is expanded before expansion is toggled,
|
||||
* false if not
|
||||
*/
|
||||
@UiThread
|
||||
public void onExpansionToggled(boolean expanded) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the {@link ParentViewHolderExpandCollapseListener} implemented in
|
||||
* {@link com.bignerdranch.expandablerecyclerview.ExpandableRecyclerAdapter}.
|
||||
*
|
||||
* @param parentViewHolderExpandCollapseListener The {@link ParentViewHolderExpandCollapseListener} to set on the {@link ParentViewHolder}
|
||||
*/
|
||||
@UiThread
|
||||
void setParentViewHolderExpandCollapseListener(ParentViewHolderExpandCollapseListener parentViewHolderExpandCollapseListener) {
|
||||
mParentViewHolderExpandCollapseListener = parentViewHolderExpandCollapseListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link android.view.View.OnClickListener} to listen for click events on
|
||||
* the entire parent {@link View}.
|
||||
* <p>
|
||||
* Only registered if {@link #shouldItemViewClickToggleExpansion()} is true.
|
||||
*
|
||||
* @param v The {@link View} that is the trigger for expansion
|
||||
*/
|
||||
@Override
|
||||
@UiThread
|
||||
public void onClick(View v) {
|
||||
if (mExpanded) {
|
||||
collapseView();
|
||||
} else {
|
||||
expandView();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to determine whether a click in the entire parent {@link View}
|
||||
* should trigger row expansion.
|
||||
* <p>
|
||||
* If you return false, you can call {@link #expandView()} to trigger an
|
||||
* expansion in response to a another event or {@link #collapseView()} to
|
||||
* trigger a collapse.
|
||||
*
|
||||
* @return true to set an {@link android.view.View.OnClickListener} on the item view
|
||||
*/
|
||||
@UiThread
|
||||
public boolean shouldItemViewClickToggleExpansion() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers expansion of the parent.
|
||||
*/
|
||||
@UiThread
|
||||
protected void expandView() {
|
||||
setExpanded(true);
|
||||
onExpansionToggled(false);
|
||||
|
||||
if (mParentViewHolderExpandCollapseListener != null) {
|
||||
mParentViewHolderExpandCollapseListener.onParentExpanded(getAdapterPosition());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers collapse of the parent.
|
||||
*/
|
||||
@UiThread
|
||||
protected void collapseView() {
|
||||
setExpanded(false);
|
||||
onExpansionToggled(true);
|
||||
|
||||
if (mParentViewHolderExpandCollapseListener != null) {
|
||||
mParentViewHolderExpandCollapseListener.onParentCollapsed(getAdapterPosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.bignerdranch.expandablerecyclerview.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Wrapper used to link metadata with a list item.
|
||||
*
|
||||
* @param <P> Parent list item
|
||||
* @param <C> Child list item
|
||||
*/
|
||||
public class ExpandableWrapper<P extends Parent<C>, C> {
|
||||
|
||||
private P mParent;
|
||||
private C mChild;
|
||||
private boolean mWrappedParent;
|
||||
private boolean mExpanded;
|
||||
|
||||
private List<ExpandableWrapper<P, C>> mWrappedChildList;
|
||||
|
||||
/**
|
||||
* Constructor to wrap a parent object of type {@link P}.
|
||||
*
|
||||
* @param parent The parent object to wrap
|
||||
*/
|
||||
public ExpandableWrapper(@NonNull P parent) {
|
||||
mParent = parent;
|
||||
mWrappedParent = true;
|
||||
mExpanded = false;
|
||||
|
||||
mWrappedChildList = generateChildItemList(parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor to wrap a child object of type {@link C}
|
||||
*
|
||||
* @param child The child object to wrap
|
||||
*/
|
||||
public ExpandableWrapper(@NonNull C child) {
|
||||
mChild = child;
|
||||
mWrappedParent = false;
|
||||
mExpanded = false;
|
||||
}
|
||||
|
||||
public P getParent() {
|
||||
return mParent;
|
||||
}
|
||||
|
||||
public void setParent(@NonNull P parent) {
|
||||
mParent = parent;
|
||||
mWrappedChildList = generateChildItemList(parent);
|
||||
}
|
||||
|
||||
public C getChild() {
|
||||
return mChild;
|
||||
}
|
||||
|
||||
public boolean isExpanded() {
|
||||
return mExpanded;
|
||||
}
|
||||
|
||||
public void setExpanded(boolean expanded) {
|
||||
mExpanded = expanded;
|
||||
}
|
||||
|
||||
public boolean isParent() {
|
||||
return mWrappedParent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The initial expanded state of a parent
|
||||
* @throws IllegalStateException If a parent isn't being wrapped
|
||||
*/
|
||||
public boolean isParentInitiallyExpanded() {
|
||||
if (!mWrappedParent) {
|
||||
throw new IllegalStateException("Parent not wrapped");
|
||||
}
|
||||
|
||||
return mParent.isInitiallyExpanded();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The list of children of a parent
|
||||
* @throws IllegalStateException If a parent isn't being wrapped
|
||||
*/
|
||||
public List<ExpandableWrapper<P, C>> getWrappedChildList() {
|
||||
if (!mWrappedParent) {
|
||||
throw new IllegalStateException("Parent not wrapped");
|
||||
}
|
||||
|
||||
return mWrappedChildList;
|
||||
}
|
||||
|
||||
private List<ExpandableWrapper<P, C>> generateChildItemList(P parentListItem) {
|
||||
List<ExpandableWrapper<P, C>> childItemList = new ArrayList<>();
|
||||
|
||||
for (C child : parentListItem.getChildList()) {
|
||||
childItemList.add(new ExpandableWrapper<P, C>(child));
|
||||
}
|
||||
|
||||
return childItemList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
final ExpandableWrapper<?, ?> that = (ExpandableWrapper<?, ?>) o;
|
||||
|
||||
if (mParent != null ? !mParent.equals(that.mParent) : that.mParent != null)
|
||||
return false;
|
||||
return mChild != null ? mChild.equals(that.mChild) : that.mChild == null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = mParent != null ? mParent.hashCode() : 0;
|
||||
result = 31 * result + (mChild != null ? mChild.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.bignerdranch.expandablerecyclerview.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Interface for implementing required methods in a parent.
|
||||
*/
|
||||
public interface Parent<C> {
|
||||
|
||||
/**
|
||||
* Getter for the list of this parent's child items.
|
||||
* <p>
|
||||
* If list is empty, the parent has no children.
|
||||
*
|
||||
* @return A {@link List} of the children of this {@link Parent}
|
||||
*/
|
||||
List<C> getChildList();
|
||||
|
||||
/**
|
||||
* Getter used to determine if this {@link Parent}'s
|
||||
* {@link android.view.View} should show up initially as expanded.
|
||||
*
|
||||
* @return true if expanded, false if not
|
||||
*/
|
||||
boolean isInitiallyExpanded();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.bignerdranch.expandablerecyclerview.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Simple implementation of the ParentListItem interface,
|
||||
* by default all items are not initially expanded.
|
||||
*
|
||||
* @param <C> Type of the Child Items held by the Parent.
|
||||
*/
|
||||
public class SimpleParent<C> implements Parent<C> {
|
||||
|
||||
private List<C> mChildList;
|
||||
|
||||
protected SimpleParent(List<C> childItemList) {
|
||||
mChildList = childItemList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitiallyExpanded() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<C> getChildList() {
|
||||
return mChildList;
|
||||
}
|
||||
|
||||
public void setChildList(List<C> childList) {
|
||||
mChildList = childList;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "com.wdullaer.materialdatetimepicker"
|
||||
version = "4.2.3 (54)"
|
||||
version = "4.2.3 (54) Mod"
|
||||
compileSdk = versions.sdkVersionCompile
|
||||
|
||||
defaultConfig {
|
||||
|
||||
@@ -6,7 +6,7 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "com.afollestad.materialdialogs"
|
||||
version = "0.9.6.0 (179)"
|
||||
version = "0.9.6.0 (179) Mod"
|
||||
|
||||
compileSdk = versions.sdkVersionCompile
|
||||
|
||||
@@ -27,8 +27,8 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.appcompat.v7)
|
||||
implementation(libs.support.annotations)
|
||||
implementation(libs.recyclerview.v7)
|
||||
implementation(libs.appcompat)
|
||||
implementation(libs.annotation)
|
||||
implementation(libs.recyclerview)
|
||||
implementation(libs.materialprogressbar)
|
||||
}
|
||||
|
||||
20
modules/recyclerview-flexibledivider/build.gradle
Normal file
20
modules/recyclerview-flexibledivider/build.gradle
Normal file
@@ -0,0 +1,20 @@
|
||||
plugins {
|
||||
id("org.autojs.build.versions")
|
||||
id("org.autojs.build.jvm-convention")
|
||||
id("com.android.library")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace "com.yqritc.recyclerviewflexibledivider"
|
||||
compileSdkVersion versions.sdkVersionCompile
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion versions.sdkVersionMin
|
||||
targetSdkVersion versions.sdkVersionTarget
|
||||
versionCode 15
|
||||
versionName "1.4.0 Mod"
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation libs.recyclerview
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<manifest package="com.yqritc.recyclerviewflexibledivider"></manifest>
|
||||
@@ -0,0 +1,439 @@
|
||||
package com.yqritc.recyclerviewflexibledivider;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.DimenRes;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* Created by yqritc on 2015/01/08.
|
||||
*/
|
||||
public abstract class FlexibleDividerDecoration extends RecyclerView.ItemDecoration {
|
||||
|
||||
private static final int DEFAULT_SIZE = 2;
|
||||
private static final int[] ATTRS = new int[]{
|
||||
android.R.attr.listDivider
|
||||
};
|
||||
|
||||
protected enum DividerType {
|
||||
DRAWABLE, PAINT, COLOR
|
||||
}
|
||||
|
||||
protected DividerType mDividerType = DividerType.DRAWABLE;
|
||||
protected VisibilityProvider mVisibilityProvider;
|
||||
protected PaintProvider mPaintProvider;
|
||||
protected ColorProvider mColorProvider;
|
||||
protected DrawableProvider mDrawableProvider;
|
||||
protected SizeProvider mSizeProvider;
|
||||
protected boolean mShowLastDivider;
|
||||
protected boolean mPositionInsideItem;
|
||||
private Paint mPaint;
|
||||
|
||||
protected FlexibleDividerDecoration(Builder builder) {
|
||||
if (builder.mPaintProvider != null) {
|
||||
mDividerType = DividerType.PAINT;
|
||||
mPaintProvider = builder.mPaintProvider;
|
||||
} else if (builder.mColorProvider != null) {
|
||||
mDividerType = DividerType.COLOR;
|
||||
mColorProvider = builder.mColorProvider;
|
||||
mPaint = new Paint();
|
||||
setSizeProvider(builder);
|
||||
} else {
|
||||
mDividerType = DividerType.DRAWABLE;
|
||||
if (builder.mDrawableProvider == null) {
|
||||
TypedArray a = builder.mContext.obtainStyledAttributes(ATTRS);
|
||||
final Drawable divider = a.getDrawable(0);
|
||||
a.recycle();
|
||||
mDrawableProvider = new DrawableProvider() {
|
||||
@Override
|
||||
public Drawable drawableProvider(int position, RecyclerView parent) {
|
||||
return divider;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
mDrawableProvider = builder.mDrawableProvider;
|
||||
}
|
||||
mSizeProvider = builder.mSizeProvider;
|
||||
}
|
||||
|
||||
mVisibilityProvider = builder.mVisibilityProvider;
|
||||
mShowLastDivider = builder.mShowLastDivider;
|
||||
mPositionInsideItem = builder.mPositionInsideItem;
|
||||
}
|
||||
|
||||
private void setSizeProvider(Builder builder) {
|
||||
mSizeProvider = builder.mSizeProvider;
|
||||
if (mSizeProvider == null) {
|
||||
mSizeProvider = new SizeProvider() {
|
||||
@Override
|
||||
public int dividerSize(int position, RecyclerView parent) {
|
||||
return DEFAULT_SIZE;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
||||
RecyclerView.Adapter adapter = parent.getAdapter();
|
||||
if (adapter == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int itemCount = adapter.getItemCount();
|
||||
int lastDividerOffset = getLastDividerOffset(parent);
|
||||
int validChildCount = parent.getChildCount();
|
||||
int lastChildPosition = -1;
|
||||
for (int i = 0; i < validChildCount; i++) {
|
||||
View child = parent.getChildAt(i);
|
||||
int childPosition = parent.getChildAdapterPosition(child);
|
||||
|
||||
if (childPosition < lastChildPosition) {
|
||||
// Avoid remaining divider when animation starts
|
||||
continue;
|
||||
}
|
||||
lastChildPosition = childPosition;
|
||||
|
||||
if (!mShowLastDivider && childPosition >= itemCount - lastDividerOffset) {
|
||||
// Don't draw divider for last line if mShowLastDivider = false
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wasDividerAlreadyDrawn(childPosition, parent)) {
|
||||
// No need to draw divider again as it was drawn already by previous column
|
||||
continue;
|
||||
}
|
||||
|
||||
int groupIndex = getGroupIndex(childPosition, parent);
|
||||
if (mVisibilityProvider.shouldHideDivider(groupIndex, parent)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Rect bounds = getDividerBound(groupIndex, parent, child);
|
||||
switch (mDividerType) {
|
||||
case DRAWABLE:
|
||||
Drawable drawable = mDrawableProvider.drawableProvider(groupIndex, parent);
|
||||
drawable.setBounds(bounds);
|
||||
drawable.draw(c);
|
||||
break;
|
||||
case PAINT:
|
||||
mPaint = mPaintProvider.dividerPaint(groupIndex, parent);
|
||||
c.drawLine(bounds.left, bounds.top, bounds.right, bounds.bottom, mPaint);
|
||||
break;
|
||||
case COLOR:
|
||||
mPaint.setColor(mColorProvider.dividerColor(groupIndex, parent));
|
||||
mPaint.setStrokeWidth(mSizeProvider.dividerSize(groupIndex, parent));
|
||||
c.drawLine(bounds.left, bounds.top, bounds.right, bounds.bottom, mPaint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(Rect rect, View v, RecyclerView parent, RecyclerView.State state) {
|
||||
int position = parent.getChildAdapterPosition(v);
|
||||
int itemCount = parent.getAdapter().getItemCount();
|
||||
int lastDividerOffset = getLastDividerOffset(parent);
|
||||
if (!mShowLastDivider && position >= itemCount - lastDividerOffset) {
|
||||
// Don't set item offset for last line if mShowLastDivider = false
|
||||
return;
|
||||
}
|
||||
|
||||
int groupIndex = getGroupIndex(position, parent);
|
||||
if (mVisibilityProvider.shouldHideDivider(groupIndex, parent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setItemOffsets(rect, groupIndex, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if recyclerview is reverse layout
|
||||
*
|
||||
* @param parent RecyclerView
|
||||
* @return true if recyclerview is reverse layout
|
||||
*/
|
||||
protected boolean isReverseLayout(RecyclerView parent) {
|
||||
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
|
||||
if (layoutManager instanceof LinearLayoutManager) {
|
||||
return ((LinearLayoutManager) layoutManager).getReverseLayout();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In the case mShowLastDivider = false,
|
||||
* Returns offset for how many views we don't have to draw a divider for,
|
||||
* for LinearLayoutManager it is as simple as not drawing the last child divider,
|
||||
* but for a GridLayoutManager it needs to take the span count for the last items into account
|
||||
* until we use the span count configured for the grid.
|
||||
*
|
||||
* @param parent RecyclerView
|
||||
* @return offset for how many views we don't have to draw a divider or 1 if its a
|
||||
* LinearLayoutManager
|
||||
*/
|
||||
private int getLastDividerOffset(RecyclerView parent) {
|
||||
if (parent.getLayoutManager() instanceof GridLayoutManager) {
|
||||
GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
|
||||
GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup();
|
||||
int spanCount = layoutManager.getSpanCount();
|
||||
int itemCount = parent.getAdapter().getItemCount();
|
||||
for (int i = itemCount - 1; i >= 0; i--) {
|
||||
if (spanSizeLookup.getSpanIndex(i, spanCount) == 0) {
|
||||
return itemCount - i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether divider was already drawn for the row the item is in,
|
||||
* effectively only makes sense for a grid
|
||||
*
|
||||
* @param position current view position to draw divider
|
||||
* @param parent RecyclerView
|
||||
* @return true if the divider can be skipped as it is in the same row as the previous one.
|
||||
*/
|
||||
private boolean wasDividerAlreadyDrawn(int position, RecyclerView parent) {
|
||||
if (parent.getLayoutManager() instanceof GridLayoutManager) {
|
||||
GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
|
||||
GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup();
|
||||
int spanCount = layoutManager.getSpanCount();
|
||||
return spanSizeLookup.getSpanIndex(position, spanCount) > 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a group index for GridLayoutManager.
|
||||
* for LinearLayoutManager, always returns position.
|
||||
*
|
||||
* @param position current view position to draw divider
|
||||
* @param parent RecyclerView
|
||||
* @return group index of items
|
||||
*/
|
||||
private int getGroupIndex(int position, RecyclerView parent) {
|
||||
if (parent.getLayoutManager() instanceof GridLayoutManager) {
|
||||
GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
|
||||
GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup();
|
||||
int spanCount = layoutManager.getSpanCount();
|
||||
return spanSizeLookup.getSpanGroupIndex(position, spanCount);
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
protected abstract Rect getDividerBound(int position, RecyclerView parent, View child);
|
||||
|
||||
protected abstract void setItemOffsets(Rect outRect, int position, RecyclerView parent);
|
||||
|
||||
/**
|
||||
* Interface for controlling divider visibility
|
||||
*/
|
||||
public interface VisibilityProvider {
|
||||
|
||||
/**
|
||||
* Returns true if divider should be hidden.
|
||||
*
|
||||
* @param position Divider position (or group index for GridLayoutManager)
|
||||
* @param parent RecyclerView
|
||||
* @return True if the divider at position should be hidden
|
||||
*/
|
||||
boolean shouldHideDivider(int position, RecyclerView parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for controlling paint instance for divider drawing
|
||||
*/
|
||||
public interface PaintProvider {
|
||||
|
||||
/**
|
||||
* Returns {@link android.graphics.Paint} for divider
|
||||
*
|
||||
* @param position Divider position (or group index for GridLayoutManager)
|
||||
* @param parent RecyclerView
|
||||
* @return Paint instance
|
||||
*/
|
||||
Paint dividerPaint(int position, RecyclerView parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for controlling divider color
|
||||
*/
|
||||
public interface ColorProvider {
|
||||
|
||||
/**
|
||||
* Returns {@link android.graphics.Color} value of divider
|
||||
*
|
||||
* @param position Divider position (or group index for GridLayoutManager)
|
||||
* @param parent RecyclerView
|
||||
* @return Color value
|
||||
*/
|
||||
int dividerColor(int position, RecyclerView parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for controlling drawable object for divider drawing
|
||||
*/
|
||||
public interface DrawableProvider {
|
||||
|
||||
/**
|
||||
* Returns drawable instance for divider
|
||||
*
|
||||
* @param position Divider position (or group index for GridLayoutManager)
|
||||
* @param parent RecyclerView
|
||||
* @return Drawable instance
|
||||
*/
|
||||
Drawable drawableProvider(int position, RecyclerView parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for controlling divider size
|
||||
*/
|
||||
public interface SizeProvider {
|
||||
|
||||
/**
|
||||
* Returns size value of divider.
|
||||
* Height for horizontal divider, width for vertical divider
|
||||
*
|
||||
* @param position Divider position (or group index for GridLayoutManager)
|
||||
* @param parent RecyclerView
|
||||
* @return Size of divider
|
||||
*/
|
||||
int dividerSize(int position, RecyclerView parent);
|
||||
}
|
||||
|
||||
public static class Builder<T extends Builder> {
|
||||
|
||||
private Context mContext;
|
||||
protected Resources mResources;
|
||||
private PaintProvider mPaintProvider;
|
||||
private ColorProvider mColorProvider;
|
||||
private DrawableProvider mDrawableProvider;
|
||||
private SizeProvider mSizeProvider;
|
||||
private VisibilityProvider mVisibilityProvider = new VisibilityProvider() {
|
||||
@Override
|
||||
public boolean shouldHideDivider(int position, RecyclerView parent) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
private boolean mShowLastDivider = false;
|
||||
private boolean mPositionInsideItem = false;
|
||||
|
||||
public Builder(Context context) {
|
||||
mContext = context;
|
||||
mResources = context.getResources();
|
||||
}
|
||||
|
||||
public T paint(final Paint paint) {
|
||||
return paintProvider(new PaintProvider() {
|
||||
@Override
|
||||
public Paint dividerPaint(int position, RecyclerView parent) {
|
||||
return paint;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public T paintProvider(PaintProvider provider) {
|
||||
mPaintProvider = provider;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T color(final int color) {
|
||||
return colorProvider(new ColorProvider() {
|
||||
@Override
|
||||
public int dividerColor(int position, RecyclerView parent) {
|
||||
return color;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public T colorResId(@ColorRes int colorId) {
|
||||
return color(ContextCompat.getColor(mContext, colorId));
|
||||
}
|
||||
|
||||
public T colorProvider(ColorProvider provider) {
|
||||
mColorProvider = provider;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T drawable(@DrawableRes int id) {
|
||||
return drawable(ContextCompat.getDrawable(mContext, id));
|
||||
}
|
||||
|
||||
public T drawable(final Drawable drawable) {
|
||||
return drawableProvider(new DrawableProvider() {
|
||||
@Override
|
||||
public Drawable drawableProvider(int position, RecyclerView parent) {
|
||||
return drawable;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public T drawableProvider(DrawableProvider provider) {
|
||||
mDrawableProvider = provider;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T size(final int size) {
|
||||
return sizeProvider(new SizeProvider() {
|
||||
@Override
|
||||
public int dividerSize(int position, RecyclerView parent) {
|
||||
return size;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public T sizeResId(@DimenRes int sizeId) {
|
||||
return size(mResources.getDimensionPixelSize(sizeId));
|
||||
}
|
||||
|
||||
public T sizeProvider(SizeProvider provider) {
|
||||
mSizeProvider = provider;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T visibilityProvider(VisibilityProvider provider) {
|
||||
mVisibilityProvider = provider;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T showLastDivider() {
|
||||
mShowLastDivider = true;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T positionInsideItem(boolean positionInsideItem) {
|
||||
mPositionInsideItem = positionInsideItem;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
protected void checkBuilderParams() {
|
||||
if (mPaintProvider != null) {
|
||||
if (mColorProvider != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Use setColor method of Paint class to specify line color. Do not provider ColorProvider if you set PaintProvider.");
|
||||
}
|
||||
if (mSizeProvider != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Use setStrokeWidth method of Paint class to specify line size. Do not provider SizeProvider if you set PaintProvider.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package com.yqritc.recyclerviewflexibledivider;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
import androidx.annotation.DimenRes;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* Created by yqritc on 2015/01/15.
|
||||
*/
|
||||
public class HorizontalDividerItemDecoration extends FlexibleDividerDecoration {
|
||||
|
||||
private MarginProvider mMarginProvider;
|
||||
|
||||
protected HorizontalDividerItemDecoration(Builder builder) {
|
||||
super(builder);
|
||||
mMarginProvider = builder.mMarginProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Rect getDividerBound(int position, RecyclerView parent, View child) {
|
||||
Rect bounds = new Rect(0, 0, 0, 0);
|
||||
int transitionX = (int) ViewCompat.getTranslationX(child);
|
||||
int transitionY = (int) ViewCompat.getTranslationY(child);
|
||||
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
|
||||
bounds.left = parent.getPaddingLeft() +
|
||||
mMarginProvider.dividerLeftMargin(position, parent) + transitionX;
|
||||
bounds.right = parent.getWidth() - parent.getPaddingRight() -
|
||||
mMarginProvider.dividerRightMargin(position, parent) + transitionX;
|
||||
|
||||
int dividerSize = getDividerSize(position, parent);
|
||||
boolean isReverseLayout = isReverseLayout(parent);
|
||||
if (mDividerType == DividerType.DRAWABLE) {
|
||||
// set top and bottom position of divider
|
||||
if (isReverseLayout) {
|
||||
bounds.bottom = child.getTop() - params.topMargin + transitionY;
|
||||
bounds.top = bounds.bottom - dividerSize;
|
||||
} else {
|
||||
bounds.top = child.getBottom() + params.bottomMargin + transitionY;
|
||||
bounds.bottom = bounds.top + dividerSize;
|
||||
}
|
||||
} else {
|
||||
// set center point of divider
|
||||
int halfSize = dividerSize / 2;
|
||||
if (isReverseLayout) {
|
||||
bounds.top = child.getTop() - params.topMargin - halfSize + transitionY;
|
||||
} else {
|
||||
bounds.top = child.getBottom() + params.bottomMargin + halfSize + transitionY;
|
||||
}
|
||||
bounds.bottom = bounds.top;
|
||||
}
|
||||
|
||||
if (mPositionInsideItem) {
|
||||
if (isReverseLayout) {
|
||||
bounds.top += dividerSize;
|
||||
bounds.bottom += dividerSize;
|
||||
} else {
|
||||
bounds.top -= dividerSize;
|
||||
bounds.bottom -= dividerSize;
|
||||
}
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setItemOffsets(Rect outRect, int position, RecyclerView parent) {
|
||||
if (mPositionInsideItem) {
|
||||
outRect.set(0, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isReverseLayout(parent)) {
|
||||
outRect.set(0, getDividerSize(position, parent), 0, 0);
|
||||
} else {
|
||||
outRect.set(0, 0, 0, getDividerSize(position, parent));
|
||||
}
|
||||
}
|
||||
|
||||
private int getDividerSize(int position, RecyclerView parent) {
|
||||
if (mPaintProvider != null) {
|
||||
return (int) mPaintProvider.dividerPaint(position, parent).getStrokeWidth();
|
||||
} else if (mSizeProvider != null) {
|
||||
return mSizeProvider.dividerSize(position, parent);
|
||||
} else if (mDrawableProvider != null) {
|
||||
Drawable drawable = mDrawableProvider.drawableProvider(position, parent);
|
||||
return drawable.getIntrinsicHeight();
|
||||
}
|
||||
throw new RuntimeException("failed to get size");
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for controlling divider margin
|
||||
*/
|
||||
public interface MarginProvider {
|
||||
|
||||
/**
|
||||
* Returns left margin of divider.
|
||||
*
|
||||
* @param position Divider position (or group index for GridLayoutManager)
|
||||
* @param parent RecyclerView
|
||||
* @return left margin
|
||||
*/
|
||||
int dividerLeftMargin(int position, RecyclerView parent);
|
||||
|
||||
/**
|
||||
* Returns right margin of divider.
|
||||
*
|
||||
* @param position Divider position (or group index for GridLayoutManager)
|
||||
* @param parent RecyclerView
|
||||
* @return right margin
|
||||
*/
|
||||
int dividerRightMargin(int position, RecyclerView parent);
|
||||
}
|
||||
|
||||
public static class Builder extends FlexibleDividerDecoration.Builder<Builder> {
|
||||
|
||||
private MarginProvider mMarginProvider = new MarginProvider() {
|
||||
@Override
|
||||
public int dividerLeftMargin(int position, RecyclerView parent) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int dividerRightMargin(int position, RecyclerView parent) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
public Builder(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public Builder margin(final int leftMargin, final int rightMargin) {
|
||||
return marginProvider(new MarginProvider() {
|
||||
@Override
|
||||
public int dividerLeftMargin(int position, RecyclerView parent) {
|
||||
return leftMargin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int dividerRightMargin(int position, RecyclerView parent) {
|
||||
return rightMargin;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Builder margin(int horizontalMargin) {
|
||||
return margin(horizontalMargin, horizontalMargin);
|
||||
}
|
||||
|
||||
public Builder marginResId(@DimenRes int leftMarginId, @DimenRes int rightMarginId) {
|
||||
return margin(mResources.getDimensionPixelSize(leftMarginId),
|
||||
mResources.getDimensionPixelSize(rightMarginId));
|
||||
}
|
||||
|
||||
public Builder marginResId(@DimenRes int horizontalMarginId) {
|
||||
return marginResId(horizontalMarginId, horizontalMarginId);
|
||||
}
|
||||
|
||||
public Builder marginProvider(MarginProvider provider) {
|
||||
mMarginProvider = provider;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HorizontalDividerItemDecoration build() {
|
||||
checkBuilderParams();
|
||||
return new HorizontalDividerItemDecoration(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package com.yqritc.recyclerviewflexibledivider;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
import androidx.annotation.DimenRes;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* Created by yqritc on 2015/01/15.
|
||||
*/
|
||||
public class VerticalDividerItemDecoration extends FlexibleDividerDecoration {
|
||||
|
||||
private MarginProvider mMarginProvider;
|
||||
|
||||
protected VerticalDividerItemDecoration(Builder builder) {
|
||||
super(builder);
|
||||
mMarginProvider = builder.mMarginProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Rect getDividerBound(int position, RecyclerView parent, View child) {
|
||||
Rect bounds = new Rect(0, 0, 0, 0);
|
||||
int transitionX = (int) ViewCompat.getTranslationX(child);
|
||||
int transitionY = (int) ViewCompat.getTranslationY(child);
|
||||
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
|
||||
bounds.top = parent.getPaddingTop() +
|
||||
mMarginProvider.dividerTopMargin(position, parent) + transitionY;
|
||||
bounds.bottom = parent.getHeight() - parent.getPaddingBottom() -
|
||||
mMarginProvider.dividerBottomMargin(position, parent) + transitionY;
|
||||
|
||||
int dividerSize = getDividerSize(position, parent);
|
||||
boolean isReverseLayout = isReverseLayout(parent);
|
||||
if (mDividerType == DividerType.DRAWABLE) {
|
||||
// set left and right position of divider
|
||||
if (isReverseLayout) {
|
||||
bounds.right = child.getLeft() - params.leftMargin + transitionX;
|
||||
bounds.left = bounds.right - dividerSize;
|
||||
} else {
|
||||
bounds.left = child.getRight() + params.rightMargin + transitionX;
|
||||
bounds.right = bounds.left + dividerSize;
|
||||
}
|
||||
} else {
|
||||
// set center point of divider
|
||||
int halfSize = dividerSize / 2;
|
||||
if (isReverseLayout) {
|
||||
bounds.left = child.getLeft() - params.leftMargin - halfSize + transitionX;
|
||||
} else {
|
||||
bounds.left = child.getRight() + params.rightMargin + halfSize + transitionX;
|
||||
}
|
||||
bounds.right = bounds.left;
|
||||
}
|
||||
|
||||
if (mPositionInsideItem) {
|
||||
if (isReverseLayout) {
|
||||
bounds.left += dividerSize;
|
||||
bounds.right += dividerSize;
|
||||
} else {
|
||||
bounds.left -= dividerSize;
|
||||
bounds.right -= dividerSize;
|
||||
}
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setItemOffsets(Rect outRect, int position, RecyclerView parent) {
|
||||
if (mPositionInsideItem) {
|
||||
outRect.set(0, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isReverseLayout(parent)) {
|
||||
outRect.set(getDividerSize(position, parent), 0, 0, 0);
|
||||
} else {
|
||||
outRect.set(0, 0, getDividerSize(position, parent), 0);
|
||||
}
|
||||
}
|
||||
|
||||
private int getDividerSize(int position, RecyclerView parent) {
|
||||
if (mPaintProvider != null) {
|
||||
return (int) mPaintProvider.dividerPaint(position, parent).getStrokeWidth();
|
||||
} else if (mSizeProvider != null) {
|
||||
return mSizeProvider.dividerSize(position, parent);
|
||||
} else if (mDrawableProvider != null) {
|
||||
Drawable drawable = mDrawableProvider.drawableProvider(position, parent);
|
||||
return drawable.getIntrinsicWidth();
|
||||
}
|
||||
throw new RuntimeException("failed to get size");
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for controlling divider margin
|
||||
*/
|
||||
public interface MarginProvider {
|
||||
|
||||
/**
|
||||
* Returns top margin of divider.
|
||||
*
|
||||
* @param position Divider position (or group index for GridLayoutManager)
|
||||
* @param parent RecyclerView
|
||||
* @return top margin
|
||||
*/
|
||||
int dividerTopMargin(int position, RecyclerView parent);
|
||||
|
||||
/**
|
||||
* Returns bottom margin of divider.
|
||||
*
|
||||
* @param position Divider position (or group index for GridLayoutManager)
|
||||
* @param parent RecyclerView
|
||||
* @return bottom margin
|
||||
*/
|
||||
int dividerBottomMargin(int position, RecyclerView parent);
|
||||
}
|
||||
|
||||
public static class Builder extends FlexibleDividerDecoration.Builder<Builder> {
|
||||
|
||||
private MarginProvider mMarginProvider = new MarginProvider() {
|
||||
@Override
|
||||
public int dividerTopMargin(int position, RecyclerView parent) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int dividerBottomMargin(int position, RecyclerView parent) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
public Builder(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public Builder margin(final int topMargin, final int bottomMargin) {
|
||||
return marginProvider(new MarginProvider() {
|
||||
@Override
|
||||
public int dividerTopMargin(int position, RecyclerView parent) {
|
||||
return topMargin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int dividerBottomMargin(int position, RecyclerView parent) {
|
||||
return bottomMargin;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Builder margin(int verticalMargin) {
|
||||
return margin(verticalMargin, verticalMargin);
|
||||
}
|
||||
|
||||
public Builder marginResId(@DimenRes int topMarginId, @DimenRes int bottomMarginId) {
|
||||
return margin(mResources.getDimensionPixelSize(topMarginId),
|
||||
mResources.getDimensionPixelSize(bottomMarginId));
|
||||
}
|
||||
|
||||
public Builder marginResId(@DimenRes int verticalMarginId) {
|
||||
return marginResId(verticalMarginId, verticalMarginId);
|
||||
}
|
||||
|
||||
public Builder marginProvider(MarginProvider provider) {
|
||||
mMarginProvider = provider;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VerticalDividerItemDecoration build() {
|
||||
checkBuilderParams();
|
||||
return new VerticalDividerItemDecoration(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#Sat Mar 07 12:33:49 CST 2026
|
||||
BUILD_TIME=1772858029128
|
||||
#Sat Mar 07 19:14:09 CST 2026
|
||||
BUILD_TIME=1772882049270
|
||||
COMPILE_SDK_VERSION=36
|
||||
IMAGE_QUANT_CMAKE_VERSION=3.22.1
|
||||
IMAGE_QUANT_NDK_VERSION=26.1.10909125
|
||||
@@ -27,6 +27,6 @@ RAPID_OCR_OPENCV_MOBILE_LABEL_VERSION=13
|
||||
RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3
|
||||
TARGET_SDK_VERSION=36
|
||||
TARGET_SDK_VERSION_INRT=29
|
||||
VERSION_BUILD=3778
|
||||
VERSION_BUILD=3780
|
||||
VERSION_NAME=6.7.0 Alpha23
|
||||
VSCODE_EXT_REQUIRED_VERSION=1.0.13
|
||||
|
||||
Reference in New Issue
Block a user