commit 3f3da596a55ef345e6c8ff06801a6ad782e47799 Author: tongchao Date: Wed May 28 10:13:27 2025 +0800 first commit diff --git a/.gradle/7.3.3/checksums/checksums.lock b/.gradle/7.3.3/checksums/checksums.lock new file mode 100644 index 0000000..79228c2 Binary files /dev/null and b/.gradle/7.3.3/checksums/checksums.lock differ diff --git a/.gradle/7.3.3/checksums/md5-checksums.bin b/.gradle/7.3.3/checksums/md5-checksums.bin new file mode 100644 index 0000000..74d1082 Binary files /dev/null and b/.gradle/7.3.3/checksums/md5-checksums.bin differ diff --git a/.gradle/7.3.3/checksums/sha1-checksums.bin b/.gradle/7.3.3/checksums/sha1-checksums.bin new file mode 100644 index 0000000..047c617 Binary files /dev/null and b/.gradle/7.3.3/checksums/sha1-checksums.bin differ diff --git a/.gradle/7.3.3/dependencies-accessors/dependencies-accessors.lock b/.gradle/7.3.3/dependencies-accessors/dependencies-accessors.lock new file mode 100644 index 0000000..0325e52 Binary files /dev/null and b/.gradle/7.3.3/dependencies-accessors/dependencies-accessors.lock differ diff --git a/.gradle/7.3.3/dependencies-accessors/gc.properties b/.gradle/7.3.3/dependencies-accessors/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/.gradle/7.3.3/executionHistory/executionHistory.bin b/.gradle/7.3.3/executionHistory/executionHistory.bin new file mode 100644 index 0000000..f038e65 Binary files /dev/null and b/.gradle/7.3.3/executionHistory/executionHistory.bin differ diff --git a/.gradle/7.3.3/executionHistory/executionHistory.lock b/.gradle/7.3.3/executionHistory/executionHistory.lock new file mode 100644 index 0000000..f303cdf Binary files /dev/null and b/.gradle/7.3.3/executionHistory/executionHistory.lock differ diff --git a/.gradle/7.3.3/fileChanges/last-build.bin b/.gradle/7.3.3/fileChanges/last-build.bin new file mode 100644 index 0000000..f76dd23 Binary files /dev/null and b/.gradle/7.3.3/fileChanges/last-build.bin differ diff --git a/.gradle/7.3.3/fileHashes/fileHashes.bin b/.gradle/7.3.3/fileHashes/fileHashes.bin new file mode 100644 index 0000000..c37a49f Binary files /dev/null and b/.gradle/7.3.3/fileHashes/fileHashes.bin differ diff --git a/.gradle/7.3.3/fileHashes/fileHashes.lock b/.gradle/7.3.3/fileHashes/fileHashes.lock new file mode 100644 index 0000000..3a50db7 Binary files /dev/null and b/.gradle/7.3.3/fileHashes/fileHashes.lock differ diff --git a/.gradle/7.3.3/fileHashes/resourceHashesCache.bin b/.gradle/7.3.3/fileHashes/resourceHashesCache.bin new file mode 100644 index 0000000..644c90c Binary files /dev/null and b/.gradle/7.3.3/fileHashes/resourceHashesCache.bin differ diff --git a/.gradle/7.3.3/gc.properties b/.gradle/7.3.3/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock new file mode 100644 index 0000000..da5a3c1 Binary files /dev/null and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/.gradle/buildOutputCleanup/cache.properties b/.gradle/buildOutputCleanup/cache.properties new file mode 100644 index 0000000..2e64a86 --- /dev/null +++ b/.gradle/buildOutputCleanup/cache.properties @@ -0,0 +1,2 @@ +#Tue May 20 10:51:49 CST 2025 +gradle.version=7.3.3 diff --git a/.gradle/buildOutputCleanup/outputFiles.bin b/.gradle/buildOutputCleanup/outputFiles.bin new file mode 100644 index 0000000..40aae3d Binary files /dev/null and b/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/.gradle/checksums/checksums.lock b/.gradle/checksums/checksums.lock new file mode 100644 index 0000000..80ea9ed Binary files /dev/null and b/.gradle/checksums/checksums.lock differ diff --git a/.gradle/checksums/md5-checksums.bin b/.gradle/checksums/md5-checksums.bin new file mode 100644 index 0000000..6031e7c Binary files /dev/null and b/.gradle/checksums/md5-checksums.bin differ diff --git a/.gradle/checksums/sha1-checksums.bin b/.gradle/checksums/sha1-checksums.bin new file mode 100644 index 0000000..4de3cab Binary files /dev/null and b/.gradle/checksums/sha1-checksums.bin differ diff --git a/.gradle/configuration-cache/gc.properties b/.gradle/configuration-cache/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe new file mode 100644 index 0000000..7f7b4e9 Binary files /dev/null and b/.gradle/file-system.probe differ diff --git a/.gradle/vcs-1/gc.properties b/.gradle/vcs-1/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/assetWizardSettings.xml b/.idea/assetWizardSettings.xml new file mode 100644 index 0000000..fcf75f4 --- /dev/null +++ b/.idea/assetWizardSettings.xml @@ -0,0 +1,32 @@ + + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..38fe071 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..1f254f1 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..b268ef3 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..97626ba --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..ac9e81e --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,24 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..b0adc45 --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..fdf8d99 --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__C__Users_nopsp_work_android_sdk_build_tools_30_0_3_renderscript_lib_androidx_rs_jar.xml b/.idea/libraries/Gradle__C__Users_nopsp_work_android_sdk_build_tools_30_0_3_renderscript_lib_androidx_rs_jar.xml new file mode 100644 index 0000000..a15b289 --- /dev/null +++ b/.idea/libraries/Gradle__C__Users_nopsp_work_android_sdk_build_tools_30_0_3_renderscript_lib_androidx_rs_jar.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__C__Users_nopsp_work_workspace_industrial_operation_DialogX_libs_DialogXInterface_jar.xml b/.idea/libraries/Gradle__C__Users_nopsp_work_workspace_industrial_operation_DialogX_libs_DialogXInterface_jar.xml new file mode 100644 index 0000000..bde789b --- /dev/null +++ b/.idea/libraries/Gradle__C__Users_nopsp_work_workspace_industrial_operation_DialogX_libs_DialogXInterface_jar.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__C__Users_nopsp_work_workspace_industrial_operation_DialogX_libs_androidx_rs_jar.xml b/.idea/libraries/Gradle__C__Users_nopsp_work_workspace_industrial_operation_DialogX_libs_androidx_rs_jar.xml new file mode 100644 index 0000000..3ba9b83 --- /dev/null +++ b/.idea/libraries/Gradle__C__Users_nopsp_work_workspace_industrial_operation_DialogX_libs_androidx_rs_jar.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_activity_activity_1_2_4_aar.xml b/.idea/libraries/Gradle__androidx_activity_activity_1_2_4_aar.xml new file mode 100644 index 0000000..d0f69c1 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_activity_activity_1_2_4_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_annotation_annotation_1_3_0.xml b/.idea/libraries/Gradle__androidx_annotation_annotation_1_3_0.xml new file mode 100644 index 0000000..4cfd514 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_annotation_annotation_1_3_0.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_annotation_annotation_experimental_1_1_0_aar.xml b/.idea/libraries/Gradle__androidx_annotation_annotation_experimental_1_1_0_aar.xml new file mode 100644 index 0000000..bdb9f3b --- /dev/null +++ b/.idea/libraries/Gradle__androidx_annotation_annotation_experimental_1_1_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_4_1_aar.xml b/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_4_1_aar.xml new file mode 100644 index 0000000..05d3dec --- /dev/null +++ b/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_4_1_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_appcompat_appcompat_resources_1_4_1_aar.xml b/.idea/libraries/Gradle__androidx_appcompat_appcompat_resources_1_4_1_aar.xml new file mode 100644 index 0000000..b9c8a02 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_appcompat_appcompat_resources_1_4_1_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_arch_core_core_common_2_1_0.xml b/.idea/libraries/Gradle__androidx_arch_core_core_common_2_1_0.xml new file mode 100644 index 0000000..ce7fa42 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_arch_core_core_common_2_1_0.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_arch_core_core_runtime_2_0_0_aar.xml b/.idea/libraries/Gradle__androidx_arch_core_core_runtime_2_0_0_aar.xml new file mode 100644 index 0000000..c1a6c24 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_arch_core_core_runtime_2_0_0_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_arch_core_core_runtime_2_1_0_aar.xml b/.idea/libraries/Gradle__androidx_arch_core_core_runtime_2_1_0_aar.xml new file mode 100644 index 0000000..f7eed31 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_arch_core_core_runtime_2_1_0_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_asynclayoutinflater_asynclayoutinflater_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_asynclayoutinflater_asynclayoutinflater_1_0_0_aar.xml new file mode 100644 index 0000000..9c4bb31 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_asynclayoutinflater_asynclayoutinflater_1_0_0_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_cardview_cardview_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_cardview_cardview_1_0_0_aar.xml new file mode 100644 index 0000000..4453f65 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_cardview_cardview_1_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_collection_collection_1_1_0.xml b/.idea/libraries/Gradle__androidx_collection_collection_1_1_0.xml new file mode 100644 index 0000000..6b589c5 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_collection_collection_1_1_0.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_2_1_3_aar.xml b/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_2_1_3_aar.xml new file mode 100644 index 0000000..08d35e5 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_2_1_3_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_coordinatorlayout_coordinatorlayout_1_1_0_aar.xml b/.idea/libraries/Gradle__androidx_coordinatorlayout_coordinatorlayout_1_1_0_aar.xml new file mode 100644 index 0000000..3435990 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_coordinatorlayout_coordinatorlayout_1_1_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_core_core_1_7_0_aar.xml b/.idea/libraries/Gradle__androidx_core_core_1_7_0_aar.xml new file mode 100644 index 0000000..9de54aa --- /dev/null +++ b/.idea/libraries/Gradle__androidx_core_core_1_7_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_cursoradapter_cursoradapter_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_cursoradapter_cursoradapter_1_0_0_aar.xml new file mode 100644 index 0000000..fad177a --- /dev/null +++ b/.idea/libraries/Gradle__androidx_cursoradapter_cursoradapter_1_0_0_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_customview_customview_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_customview_customview_1_0_0_aar.xml new file mode 100644 index 0000000..2b6413a --- /dev/null +++ b/.idea/libraries/Gradle__androidx_customview_customview_1_0_0_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_customview_customview_1_1_0_aar.xml b/.idea/libraries/Gradle__androidx_customview_customview_1_1_0_aar.xml new file mode 100644 index 0000000..f85d784 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_customview_customview_1_1_0_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_databinding_viewbinding_7_1_0_aar.xml b/.idea/libraries/Gradle__androidx_databinding_viewbinding_7_1_0_aar.xml new file mode 100644 index 0000000..b5ab9fe --- /dev/null +++ b/.idea/libraries/Gradle__androidx_databinding_viewbinding_7_1_0_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_documentfile_documentfile_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_documentfile_documentfile_1_0_0_aar.xml new file mode 100644 index 0000000..754296b --- /dev/null +++ b/.idea/libraries/Gradle__androidx_documentfile_documentfile_1_0_0_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_drawerlayout_drawerlayout_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_drawerlayout_drawerlayout_1_0_0_aar.xml new file mode 100644 index 0000000..415c2f6 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_drawerlayout_drawerlayout_1_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_drawerlayout_drawerlayout_1_1_1_aar.xml b/.idea/libraries/Gradle__androidx_drawerlayout_drawerlayout_1_1_1_aar.xml new file mode 100644 index 0000000..ff50cb0 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_drawerlayout_drawerlayout_1_1_1_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_dynamicanimation_dynamicanimation_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_dynamicanimation_dynamicanimation_1_0_0_aar.xml new file mode 100644 index 0000000..9ca72ba --- /dev/null +++ b/.idea/libraries/Gradle__androidx_dynamicanimation_dynamicanimation_1_0_0_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_exifinterface_exifinterface_1_2_0_aar.xml b/.idea/libraries/Gradle__androidx_exifinterface_exifinterface_1_2_0_aar.xml new file mode 100644 index 0000000..20dc1ad --- /dev/null +++ b/.idea/libraries/Gradle__androidx_exifinterface_exifinterface_1_2_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_fragment_fragment_1_3_6_aar.xml b/.idea/libraries/Gradle__androidx_fragment_fragment_1_3_6_aar.xml new file mode 100644 index 0000000..9906720 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_fragment_fragment_1_3_6_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_interpolator_interpolator_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_interpolator_interpolator_1_0_0_aar.xml new file mode 100644 index 0000000..266e57c --- /dev/null +++ b/.idea/libraries/Gradle__androidx_interpolator_interpolator_1_0_0_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_legacy_legacy_support_core_ui_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_legacy_legacy_support_core_ui_1_0_0_aar.xml new file mode 100644 index 0000000..67761b6 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_legacy_legacy_support_core_ui_1_0_0_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_legacy_legacy_support_core_utils_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_legacy_legacy_support_core_utils_1_0_0_aar.xml new file mode 100644 index 0000000..b0dda08 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_legacy_legacy_support_core_utils_1_0_0_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_legacy_legacy_support_v4_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_legacy_legacy_support_v4_1_0_0_aar.xml new file mode 100644 index 0000000..cc2eda3 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_legacy_legacy_support_v4_1_0_0_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_2_3_1.xml b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_2_3_1.xml new file mode 100644 index 0000000..c0972be --- /dev/null +++ b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_2_3_1.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_2_4_0.xml b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_2_4_0.xml new file mode 100644 index 0000000..40ef930 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_2_4_0.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_2_0_0_aar.xml b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_2_0_0_aar.xml new file mode 100644 index 0000000..a67c292 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_2_0_0_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_core_2_3_1_aar.xml b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_core_2_3_1_aar.xml new file mode 100644 index 0000000..4f38716 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_core_2_3_1_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_runtime_2_3_1_aar.xml b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_runtime_2_3_1_aar.xml new file mode 100644 index 0000000..e8e8334 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_runtime_2_3_1_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_runtime_2_4_0_aar.xml b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_runtime_2_4_0_aar.xml new file mode 100644 index 0000000..fc3830b --- /dev/null +++ b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_runtime_2_4_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_2_3_1_aar.xml b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_2_3_1_aar.xml new file mode 100644 index 0000000..cc9e282 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_2_3_1_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_savedstate_2_3_1_aar.xml b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_savedstate_2_3_1_aar.xml new file mode 100644 index 0000000..031b0ab --- /dev/null +++ b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_savedstate_2_3_1_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_loader_loader_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_loader_loader_1_0_0_aar.xml new file mode 100644 index 0000000..fe841ad --- /dev/null +++ b/.idea/libraries/Gradle__androidx_loader_loader_1_0_0_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_localbroadcastmanager_localbroadcastmanager_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_localbroadcastmanager_localbroadcastmanager_1_0_0_aar.xml new file mode 100644 index 0000000..75089a4 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_localbroadcastmanager_localbroadcastmanager_1_0_0_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_media_media_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_media_media_1_0_0_aar.xml new file mode 100644 index 0000000..62f4d5f --- /dev/null +++ b/.idea/libraries/Gradle__androidx_media_media_1_0_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_print_print_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_print_print_1_0_0_aar.xml new file mode 100644 index 0000000..f7eac34 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_print_print_1_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_recyclerview_recyclerview_1_2_1_aar.xml b/.idea/libraries/Gradle__androidx_recyclerview_recyclerview_1_2_1_aar.xml new file mode 100644 index 0000000..4a7fba1 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_recyclerview_recyclerview_1_2_1_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_savedstate_savedstate_1_1_0_aar.xml b/.idea/libraries/Gradle__androidx_savedstate_savedstate_1_1_0_aar.xml new file mode 100644 index 0000000..ad9a133 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_savedstate_savedstate_1_1_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_slidingpanelayout_slidingpanelayout_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_slidingpanelayout_slidingpanelayout_1_0_0_aar.xml new file mode 100644 index 0000000..960cbf9 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_slidingpanelayout_slidingpanelayout_1_0_0_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_swiperefreshlayout_swiperefreshlayout_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_swiperefreshlayout_swiperefreshlayout_1_0_0_aar.xml new file mode 100644 index 0000000..2a669d6 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_swiperefreshlayout_swiperefreshlayout_1_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_transition_transition_1_2_0_aar.xml b/.idea/libraries/Gradle__androidx_transition_transition_1_2_0_aar.xml new file mode 100644 index 0000000..8724f4e --- /dev/null +++ b/.idea/libraries/Gradle__androidx_transition_transition_1_2_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_1_1_0_aar.xml b/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_1_1_0_aar.xml new file mode 100644 index 0000000..a27d6f3 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_1_1_0_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_animated_1_1_0_aar.xml b/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_animated_1_1_0_aar.xml new file mode 100644 index 0000000..3d3a2b6 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_animated_1_1_0_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_versionedparcelable_versionedparcelable_1_1_1_aar.xml b/.idea/libraries/Gradle__androidx_versionedparcelable_versionedparcelable_1_1_1_aar.xml new file mode 100644 index 0000000..349c63f --- /dev/null +++ b/.idea/libraries/Gradle__androidx_versionedparcelable_versionedparcelable_1_1_1_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_viewpager2_viewpager2_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_viewpager2_viewpager2_1_0_0_aar.xml new file mode 100644 index 0000000..c30f0c6 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_viewpager2_viewpager2_1_0_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_viewpager_viewpager_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_viewpager_viewpager_1_0_0_aar.xml new file mode 100644 index 0000000..38e7201 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_viewpager_viewpager_1_0_0_aar.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_facebook_conceal_conceal_1_1_3_aar.xml b/.idea/libraries/Gradle__com_facebook_conceal_conceal_1_1_3_aar.xml new file mode 100644 index 0000000..32cbac0 --- /dev/null +++ b/.idea/libraries/Gradle__com_facebook_conceal_conceal_1_1_3_aar.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_flyco_tablayout_FlycoTabLayout_Lib_2_1_2_aar.xml b/.idea/libraries/Gradle__com_flyco_tablayout_FlycoTabLayout_Lib_2_1_2_aar.xml new file mode 100644 index 0000000..56f03de --- /dev/null +++ b/.idea/libraries/Gradle__com_flyco_tablayout_FlycoTabLayout_Lib_2_1_2_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_github_CymChad_BaseRecyclerViewAdapterHelper_2_9_50_aar.xml b/.idea/libraries/Gradle__com_github_CymChad_BaseRecyclerViewAdapterHelper_2_9_50_aar.xml new file mode 100644 index 0000000..75ffff5 --- /dev/null +++ b/.idea/libraries/Gradle__com_github_CymChad_BaseRecyclerViewAdapterHelper_2_9_50_aar.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_github_bumptech_glide_annotations_4_12_0.xml b/.idea/libraries/Gradle__com_github_bumptech_glide_annotations_4_12_0.xml new file mode 100644 index 0000000..6cddbd1 --- /dev/null +++ b/.idea/libraries/Gradle__com_github_bumptech_glide_annotations_4_12_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_github_bumptech_glide_disklrucache_4_12_0.xml b/.idea/libraries/Gradle__com_github_bumptech_glide_disklrucache_4_12_0.xml new file mode 100644 index 0000000..303f77a --- /dev/null +++ b/.idea/libraries/Gradle__com_github_bumptech_glide_disklrucache_4_12_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_github_bumptech_glide_gifdecoder_4_12_0_aar.xml b/.idea/libraries/Gradle__com_github_bumptech_glide_gifdecoder_4_12_0_aar.xml new file mode 100644 index 0000000..3f148af --- /dev/null +++ b/.idea/libraries/Gradle__com_github_bumptech_glide_gifdecoder_4_12_0_aar.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_github_bumptech_glide_glide_4_12_0_aar.xml b/.idea/libraries/Gradle__com_github_bumptech_glide_glide_4_12_0_aar.xml new file mode 100644 index 0000000..96eeccc --- /dev/null +++ b/.idea/libraries/Gradle__com_github_bumptech_glide_glide_4_12_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_github_zhpanvip_bannerviewpager_3_5_12_aar.xml b/.idea/libraries/Gradle__com_github_zhpanvip_bannerviewpager_3_5_12_aar.xml new file mode 100644 index 0000000..c0a615b --- /dev/null +++ b/.idea/libraries/Gradle__com_github_zhpanvip_bannerviewpager_3_5_12_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_github_zhpanvip_viewpagerindicator_1_2_3_aar.xml b/.idea/libraries/Gradle__com_github_zhpanvip_viewpagerindicator_1_2_3_aar.xml new file mode 100644 index 0000000..26e7542 --- /dev/null +++ b/.idea/libraries/Gradle__com_github_zhpanvip_viewpagerindicator_1_2_3_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_google_android_material_material_1_5_0_aar.xml b/.idea/libraries/Gradle__com_google_android_material_material_1_5_0_aar.xml new file mode 100644 index 0000000..420e9f1 --- /dev/null +++ b/.idea/libraries/Gradle__com_google_android_material_material_1_5_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_google_code_gson_gson_2_8_7.xml b/.idea/libraries/Gradle__com_google_code_gson_gson_2_8_7.xml new file mode 100644 index 0000000..b0ed237 --- /dev/null +++ b/.idea/libraries/Gradle__com_google_code_gson_gson_2_8_7.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_gyf_immersionbar_immersionbar_3_0_0_aar.xml b/.idea/libraries/Gradle__com_gyf_immersionbar_immersionbar_3_0_0_aar.xml new file mode 100644 index 0000000..2e260bf --- /dev/null +++ b/.idea/libraries/Gradle__com_gyf_immersionbar_immersionbar_3_0_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_ljx_rxhttp_rxhttp_2_5_7.xml b/.idea/libraries/Gradle__com_ljx_rxhttp_rxhttp_2_5_7.xml new file mode 100644 index 0000000..adcaca8 --- /dev/null +++ b/.idea/libraries/Gradle__com_ljx_rxhttp_rxhttp_2_5_7.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_ljx_rxhttp_rxhttp_annotation_1_0_1.xml b/.idea/libraries/Gradle__com_ljx_rxhttp_rxhttp_annotation_1_0_1.xml new file mode 100644 index 0000000..21d3320 --- /dev/null +++ b/.idea/libraries/Gradle__com_ljx_rxhttp_rxhttp_annotation_1_0_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_orhanobut_hawk_2_0_1_aar.xml b/.idea/libraries/Gradle__com_orhanobut_hawk_2_0_1_aar.xml new file mode 100644 index 0000000..ba562fe --- /dev/null +++ b/.idea/libraries/Gradle__com_orhanobut_hawk_2_0_1_aar.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_rxjava_rxlife_rxlife_2_0_0_aar.xml b/.idea/libraries/Gradle__com_rxjava_rxlife_rxlife_2_0_0_aar.xml new file mode 100644 index 0000000..b4bc610 --- /dev/null +++ b/.idea/libraries/Gradle__com_rxjava_rxlife_rxlife_2_0_0_aar.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_scwang_smart_refresh_drawable_paint_2_0_1_aar.xml b/.idea/libraries/Gradle__com_scwang_smart_refresh_drawable_paint_2_0_1_aar.xml new file mode 100644 index 0000000..5009f07 --- /dev/null +++ b/.idea/libraries/Gradle__com_scwang_smart_refresh_drawable_paint_2_0_1_aar.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_scwang_smart_refresh_footer_classics_2_0_1_aar.xml b/.idea/libraries/Gradle__com_scwang_smart_refresh_footer_classics_2_0_1_aar.xml new file mode 100644 index 0000000..59c7c83 --- /dev/null +++ b/.idea/libraries/Gradle__com_scwang_smart_refresh_footer_classics_2_0_1_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_scwang_smart_refresh_header_classics_2_0_1_aar.xml b/.idea/libraries/Gradle__com_scwang_smart_refresh_header_classics_2_0_1_aar.xml new file mode 100644 index 0000000..d4235c2 --- /dev/null +++ b/.idea/libraries/Gradle__com_scwang_smart_refresh_header_classics_2_0_1_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_scwang_smart_refresh_header_material_2_0_1_aar.xml b/.idea/libraries/Gradle__com_scwang_smart_refresh_header_material_2_0_1_aar.xml new file mode 100644 index 0000000..ea01914 --- /dev/null +++ b/.idea/libraries/Gradle__com_scwang_smart_refresh_header_material_2_0_1_aar.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_scwang_smart_refresh_layout_kernel_2_0_1_aar.xml b/.idea/libraries/Gradle__com_scwang_smart_refresh_layout_kernel_2_0_1_aar.xml new file mode 100644 index 0000000..c8e7866 --- /dev/null +++ b/.idea/libraries/Gradle__com_scwang_smart_refresh_layout_kernel_2_0_1_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_squareup_okhttp3_okhttp_4_8_1.xml b/.idea/libraries/Gradle__com_squareup_okhttp3_okhttp_4_8_1.xml new file mode 100644 index 0000000..a8fb848 --- /dev/null +++ b/.idea/libraries/Gradle__com_squareup_okhttp3_okhttp_4_8_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_squareup_okio_okio_2_7_0.xml b/.idea/libraries/Gradle__com_squareup_okio_okio_2_7_0.xml new file mode 100644 index 0000000..751ae6b --- /dev/null +++ b/.idea/libraries/Gradle__com_squareup_okio_okio_2_7_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__io_reactivex_rxjava2_rxandroid_2_1_1_aar.xml b/.idea/libraries/Gradle__io_reactivex_rxjava2_rxandroid_2_1_1_aar.xml new file mode 100644 index 0000000..349862a --- /dev/null +++ b/.idea/libraries/Gradle__io_reactivex_rxjava2_rxandroid_2_1_1_aar.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__io_reactivex_rxjava2_rxjava_2_2_8.xml b/.idea/libraries/Gradle__io_reactivex_rxjava2_rxjava_2_2_8.xml new file mode 100644 index 0000000..c798b7c --- /dev/null +++ b/.idea/libraries/Gradle__io_reactivex_rxjava2_rxjava_2_2_8.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__org_greenrobot_eventbus_3_2_0.xml b/.idea/libraries/Gradle__org_greenrobot_eventbus_3_2_0.xml new file mode 100644 index 0000000..a4f4db6 --- /dev/null +++ b/.idea/libraries/Gradle__org_greenrobot_eventbus_3_2_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__org_jetbrains_annotations_13_0.xml b/.idea/libraries/Gradle__org_jetbrains_annotations_13_0.xml new file mode 100644 index 0000000..012775f --- /dev/null +++ b/.idea/libraries/Gradle__org_jetbrains_annotations_13_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_9_0.xml b/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_9_0.xml new file mode 100644 index 0000000..767eb15 --- /dev/null +++ b/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_9_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_9_0.xml b/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_9_0.xml new file mode 100644 index 0000000..4b7a670 --- /dev/null +++ b/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_9_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_9_0.xml b/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_9_0.xml new file mode 100644 index 0000000..3077d5b --- /dev/null +++ b/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_9_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk8_1_9_0.xml b/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk8_1_9_0.xml new file mode 100644 index 0000000..11f9602 --- /dev/null +++ b/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk8_1_9_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__org_jetbrains_kotlinx_kotlinx_coroutines_android_1_3_9.xml b/.idea/libraries/Gradle__org_jetbrains_kotlinx_kotlinx_coroutines_android_1_3_9.xml new file mode 100644 index 0000000..0f1ee54 --- /dev/null +++ b/.idea/libraries/Gradle__org_jetbrains_kotlinx_kotlinx_coroutines_android_1_3_9.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm_1_3_9.xml b/.idea/libraries/Gradle__org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm_1_3_9.xml new file mode 100644 index 0000000..2fb608a --- /dev/null +++ b/.idea/libraries/Gradle__org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm_1_3_9.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__org_reactivestreams_reactive_streams_1_0_2.xml b/.idea/libraries/Gradle__org_reactivestreams_reactive_streams_1_0_2.xml new file mode 100644 index 0000000..2d8f97a --- /dev/null +++ b/.idea/libraries/Gradle__org_reactivestreams_reactive_streams_1_0_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..cd47b8e --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..a1864ae --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/DialogX/industrial_operation.DialogX.androidTest.iml b/.idea/modules/DialogX/industrial_operation.DialogX.androidTest.iml new file mode 100644 index 0000000..9cc60c3 --- /dev/null +++ b/.idea/modules/DialogX/industrial_operation.DialogX.androidTest.iml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/DialogX/industrial_operation.DialogX.iml b/.idea/modules/DialogX/industrial_operation.DialogX.iml new file mode 100644 index 0000000..05e52ac --- /dev/null +++ b/.idea/modules/DialogX/industrial_operation.DialogX.iml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/DialogX/industrial_operation.DialogX.main.iml b/.idea/modules/DialogX/industrial_operation.DialogX.main.iml new file mode 100644 index 0000000..08f6217 --- /dev/null +++ b/.idea/modules/DialogX/industrial_operation.DialogX.main.iml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/DialogX/industrial_operation.DialogX.unitTest.iml b/.idea/modules/DialogX/industrial_operation.DialogX.unitTest.iml new file mode 100644 index 0000000..842a707 --- /dev/null +++ b/.idea/modules/DialogX/industrial_operation.DialogX.unitTest.iml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/DialogXInterface/industrial_operation.DialogXInterface.iml b/.idea/modules/DialogXInterface/industrial_operation.DialogXInterface.iml new file mode 100644 index 0000000..626acad --- /dev/null +++ b/.idea/modules/DialogXInterface/industrial_operation.DialogXInterface.iml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/DialogXInterface/industrial_operation.DialogXInterface.main.iml b/.idea/modules/DialogXInterface/industrial_operation.DialogXInterface.main.iml new file mode 100644 index 0000000..47645fa --- /dev/null +++ b/.idea/modules/DialogXInterface/industrial_operation.DialogXInterface.main.iml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/DialogXInterface/industrial_operation.DialogXInterface.test.iml b/.idea/modules/DialogXInterface/industrial_operation.DialogXInterface.test.iml new file mode 100644 index 0000000..df847f8 --- /dev/null +++ b/.idea/modules/DialogXInterface/industrial_operation.DialogXInterface.test.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/DialogXMaterialYou/industrial_operation.DialogXMaterialYou.androidTest.iml b/.idea/modules/DialogXMaterialYou/industrial_operation.DialogXMaterialYou.androidTest.iml new file mode 100644 index 0000000..65052a1 --- /dev/null +++ b/.idea/modules/DialogXMaterialYou/industrial_operation.DialogXMaterialYou.androidTest.iml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/DialogXMaterialYou/industrial_operation.DialogXMaterialYou.iml b/.idea/modules/DialogXMaterialYou/industrial_operation.DialogXMaterialYou.iml new file mode 100644 index 0000000..791b23a --- /dev/null +++ b/.idea/modules/DialogXMaterialYou/industrial_operation.DialogXMaterialYou.iml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/DialogXMaterialYou/industrial_operation.DialogXMaterialYou.main.iml b/.idea/modules/DialogXMaterialYou/industrial_operation.DialogXMaterialYou.main.iml new file mode 100644 index 0000000..70cb845 --- /dev/null +++ b/.idea/modules/DialogXMaterialYou/industrial_operation.DialogXMaterialYou.main.iml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/DialogXMaterialYou/industrial_operation.DialogXMaterialYou.unitTest.iml b/.idea/modules/DialogXMaterialYou/industrial_operation.DialogXMaterialYou.unitTest.iml new file mode 100644 index 0000000..1aec127 --- /dev/null +++ b/.idea/modules/DialogXMaterialYou/industrial_operation.DialogXMaterialYou.unitTest.iml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/app/industrial_operation.app.androidTest.iml b/.idea/modules/app/industrial_operation.app.androidTest.iml new file mode 100644 index 0000000..65cc14e --- /dev/null +++ b/.idea/modules/app/industrial_operation.app.androidTest.iml @@ -0,0 +1,140 @@ + + + + + + + + + + :app:main + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/app/industrial_operation.app.iml b/.idea/modules/app/industrial_operation.app.iml new file mode 100644 index 0000000..0a683b4 --- /dev/null +++ b/.idea/modules/app/industrial_operation.app.iml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/app/industrial_operation.app.main.iml b/.idea/modules/app/industrial_operation.app.main.iml new file mode 100644 index 0000000..1d90f4b --- /dev/null +++ b/.idea/modules/app/industrial_operation.app.main.iml @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/app/industrial_operation.app.unitTest.iml b/.idea/modules/app/industrial_operation.app.unitTest.iml new file mode 100644 index 0000000..f71ee36 --- /dev/null +++ b/.idea/modules/app/industrial_operation.app.unitTest.iml @@ -0,0 +1,135 @@ + + + + + + :app:main + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/industrial_operation.iml b/.idea/modules/industrial_operation.iml new file mode 100644 index 0000000..d99a9bd --- /dev/null +++ b/.idea/modules/industrial_operation.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DialogX/.gitignore b/DialogX/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/DialogX/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/DialogX/build.gradle b/DialogX/build.gradle new file mode 100644 index 0000000..1d95208 --- /dev/null +++ b/DialogX/build.gradle @@ -0,0 +1,39 @@ +plugins { + id 'com.android.library' +} + +android { + compileSdkVersion 33 + + defaultConfig { + minSdkVersion 19 + targetSdkVersion 33 + + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + viewBinding { + enabled = true + } +} + +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' +} + +artifacts { + archives sourcesJar +} +dependencies { + implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'androidx.constraintlayout:constraintlayout:2.1.3' + compileOnly files('libs\\androidx-rs.jar') + api files('libs\\DialogXInterface.jar') +} \ No newline at end of file diff --git a/DialogX/consumer-rules.pro b/DialogX/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/DialogX/libs/DialogXInterface.jar b/DialogX/libs/DialogXInterface.jar new file mode 100644 index 0000000..1092610 Binary files /dev/null and b/DialogX/libs/DialogXInterface.jar differ diff --git a/DialogX/libs/androidx-rs.jar b/DialogX/libs/androidx-rs.jar new file mode 100644 index 0000000..bce0d16 Binary files /dev/null and b/DialogX/libs/androidx-rs.jar differ diff --git a/DialogX/proguard-rules.pro b/DialogX/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/DialogX/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/DialogX/src/main/AndroidManifest.xml b/DialogX/src/main/AndroidManifest.xml new file mode 100644 index 0000000..1977b08 --- /dev/null +++ b/DialogX/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/DialogX/src/main/java/com/kongzue/dialogx/DialogX.java b/DialogX/src/main/java/com/kongzue/dialogx/DialogX.java new file mode 100644 index 0000000..0b51864 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/DialogX.java @@ -0,0 +1,217 @@ +package com.kongzue.dialogx; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Color; +import android.util.Log; + +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.style.MaterialStyle; +import com.kongzue.dialogx.util.DialogListBuilder; +import com.kongzue.dialogx.util.InputInfo; +import com.kongzue.dialogx.util.TextInfo; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/21 17:07 + */ +public class DialogX { + + public static final String ERROR_INIT_TIPS = "DialogX.init: 初始化异常,context 为 null 或未初始化,详情请查看 https://github.com/kongzue/DialogX/wiki"; + + //开启日志 + public static boolean DEBUGMODE = true; + + //全局主题风格 + public static DialogXStyle globalStyle = MaterialStyle.style(); + + //全局对话框明暗风格 + public static DialogX.THEME globalTheme = DialogX.THEME.LIGHT; + + //TipDialog 和 WaitDialog 明暗风格,不设置则默认根据 globalTheme 定义 + public static DialogX.THEME tipTheme; + + //DialogX 实现模式(实验性功能) + public static IMPL_MODE implIMPLMode = IMPL_MODE.VIEW; + + //对话框最大宽度(像素) + public static int dialogMaxWidth; + + //对话框最大高度(像素) + public static int dialogMaxHeight; + + //对话框最小宽度(像素) + public static int dialogMinWidth; + + //对话框最小高度(像素) + public static int dialogMinHeight; + + //是否允许 InputDialog 自动弹出键盘 + public static boolean autoShowInputKeyboard = true; + + //同时只显示一个 PopTip + public static boolean onlyOnePopTip = false; + + //同时只显示一个 PopNotification + public static boolean onlyOnePopNotification = true; + + //默认按钮文字样式 + public static TextInfo buttonTextInfo; + + //默认确定按钮文字样式 + public static TextInfo okButtonTextInfo; + + //默认标题文字样式 + public static TextInfo titleTextInfo; + + //默认内容文字样式 + public static TextInfo messageTextInfo; + + //默认 WaitDialog 和 TipDialog 文字样式 + public static TextInfo tipTextInfo; + + //默认输入框文字样式 + public static InputInfo inputInfo; + + //默认底部菜单、对话框的标题文字样式 + public static TextInfo menuTitleInfo; + + //默认底部菜单文本样式 + public static TextInfo menuTextInfo; + + //默认对话框背景颜色(值为 ColorInt,为 null 不生效) + public static Integer backgroundColor = null; + + //默认 TipDialog 和 WaitDialog 背景颜色(值为 ColorInt,为 null 不生效) + public static Integer tipBackgroundColor = null; + + /** + * 重写 TipDialog 和 WaitDialog 进度动画颜色, + * 注意此属性为覆盖性质,即设置此值将替换提示框原本的进度动画的颜色,包括亮暗色切换的颜色变化也将被替代 + * (值为 ColorInt,为 null 不生效) + */ + public static Integer tipProgressColor = null; + + //默认对话框默认是否可以点击外围遮罩区域或返回键关闭,此开关不影响提示框(TipDialog)以及等待框(TipDialog) + public static boolean cancelable = true; + + //默认提示框及等待框(WaitDialog、TipDialog)默认是否可以关闭 + public static boolean cancelableTipDialog = false; + + //默认取消按钮文本文字,影响 BottomDialog + public static String cancelButtonText; + + //默认 PopTip 文本样式 + public static TextInfo popTextInfo; + + //默认启动对话框动画时长 + public static long enterAnimDuration = -1; + + //默认关闭对话框动画时长 + public static long exitAnimDuration = -1; + + //全局 Dialog 生命周期监听器 + public static DialogLifecycleCallback dialogLifeCycleListener; + + //是否自动在主线程执行 + public static boolean autoRunOnUIThread = true; + + //使用振动反馈 + public static boolean useHaptic = true; + + /** + * 声明:若 Activity 已使用沉浸式适配请开启(已废弃) + *

+ * 请注意,若你没有使用沉浸式适配,请关闭此选项,此选项将影响对话框布局是否允许延伸至导航栏背后显示 + */ + @Deprecated + public static boolean useActivityLayoutTranslationNavigationBar = false; + + /** + * 设置 BottomDialog 导航栏背景颜色 + * 彩蛋:a_man 私人定制款属性 + */ + public static int bottomDialogNavbarColor = Color.TRANSPARENT; + + //触摸滑动触发阈值,影响 BottomDialog、FullScreenDialog 下滑关闭触发距离,单位:像素 + public static int touchSlideTriggerThreshold = dip2px(35); + + //Window 模式使用全局悬浮窗,需要 SYSTEM_ALERT_WINDOW 权限 + public static boolean globalHoverWindow = false; + + //部分插屏广告 SDK 可能出现背景黑屏的问题,在这里配置需要 DialogX 屏蔽的 Activity 的包名以屏蔽对该 activity 的支持: + public static String[] unsupportedActivitiesPackageNames = new String[]{ + "com.bytedance.sdk.openadsdk.stub.activity", + "com.mobile.auth.gatewayauth", + "com.google.android.gms.ads" + }; + + public static int defaultMessageDialogBackgroundRadius = -1; + + public static int defaultBottomDialogBackgroundRadius = -1; + + public static int defaultFullScreenDialogBackgroundRadius = -1; + + public static int defaultWaitAndTipDialogBackgroundRadius = -1; + + public static int defaultPopMenuBackgroundRadius = -1; + + public static int defaultPopTipBackgroundRadius = -1; + + public static int defaultPopNotificationBackgroundRadius = -1; + + //开启沉浸式适配 + public static boolean enableImmersiveMode = true; + + //沉浸式忽略左右的非安全区 + public static boolean ignoreUnsafeInsetsHorizontal = false; + + public enum THEME { + LIGHT, DARK, AUTO + } + + public enum IMPL_MODE { + VIEW, WINDOW, DIALOG_FRAGMENT, FLOATING_ACTIVITY + } + + public static void init(Context context) { + if (context == null) { + error(ERROR_INIT_TIPS); + return; + } + BaseDialog.init(context); + } + + public static void error(Object o) { + if (DEBUGMODE) Log.e(">>>", o.toString()); + } + + private static int dip2px(float dpValue) { + final float scale = Resources.getSystem().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + public static DialogListBuilder showDialogList(BaseDialog... dialogs) { + return DialogListBuilder.create(dialogs).show(); + } + + // 默认消息对话框标题文本 + public static CharSequence defaultMessageDialogTitleText; + + // 等待提示框默认文本 + public static CharSequence defaultWaitDialogWaitingText; + + // 成功提示框默认文本 + public static CharSequence defaultTipDialogSuccessText; + + // 错误提示框默认文本 + public static CharSequence defaultTipDialogErrorText; + + // 警告提示框默认文本 + public static CharSequence defaultTipDialogWarningText; +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/BottomDialog.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/BottomDialog.java new file mode 100644 index 0000000..fb5084e --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/BottomDialog.java @@ -0,0 +1,1449 @@ +package com.kongzue.dialogx.dialogs; + +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.app.Activity; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Outline; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.animation.DecelerateInterpolator; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.appcompat.app.AppCompatActivity; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.BaseOnDialogClickCallback; +import com.kongzue.dialogx.interfaces.BlurViewType; +import com.kongzue.dialogx.interfaces.BottomDialogSlideEventLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogConvertViewInterface; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXBaseBottomDialog; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.OnDialogButtonClickListener; +import com.kongzue.dialogx.interfaces.OnMenuButtonClickListener; +import com.kongzue.dialogx.interfaces.ScrollController; +import com.kongzue.dialogx.util.BottomDialogTouchEventInterceptor; +import com.kongzue.dialogx.util.TextInfo; +import com.kongzue.dialogx.util.views.DialogScrollView; +import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout; +import com.kongzue.dialogx.util.views.MaxRelativeLayout; + +import java.util.HashMap; +import java.util.List; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/6 15:17 + */ +public class BottomDialog extends BaseDialog implements DialogXBaseBottomDialog { + + public static int overrideEnterDuration = -1; + public static int overrideExitDuration = -1; + public static BOOLEAN overrideCancelable; + protected OnBindView onBindView; + protected CharSequence title; + protected CharSequence message; + protected CharSequence cancelText; + protected CharSequence okText; + protected CharSequence otherText; + protected boolean allowInterceptTouch = true; + protected boolean bottomNonSafetyAreaBySelf = false; + protected Integer maskColor = null; + protected BaseOnDialogClickCallback cancelButtonClickListener; + protected BaseOnDialogClickCallback okButtonClickListener; + protected BaseOnDialogClickCallback otherButtonClickListener; + protected OnBackgroundMaskClickListener onBackgroundMaskClickListener; + protected OnBackPressedListener onBackPressedListener; + protected BOOLEAN privateCancelable; + protected boolean bkgInterceptTouch = true; + protected float backgroundRadius = DialogX.defaultBottomDialogBackgroundRadius; + protected Drawable titleIcon; + protected DialogXAnimInterface dialogXAnimImpl; + protected BUTTON_SELECT_RESULT buttonSelectResult = BUTTON_SELECT_RESULT.NONE; + protected boolean scrollableWhenContentLargeThanVisibleRange = true; + + protected TextInfo titleTextInfo; + protected TextInfo messageTextInfo; + protected TextInfo menuTextInfo; + protected TextInfo cancelTextInfo = new TextInfo().setBold(true); + protected TextInfo okTextInfo = new TextInfo().setBold(true); + protected TextInfo otherTextInfo = new TextInfo().setBold(true); + + /** + * 此值用于,当禁用滑动时(style.overrideBottomDialogRes.touchSlide = false时)的最大显示高度。 + * 0:不限制,最大显示到屏幕可用高度。 + */ + protected float bottomDialogMaxHeight = 0f; + + protected DialogLifecycleCallback dialogLifecycleCallback; + + protected BottomDialog me = this; + + protected BottomDialog() { + super(); + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + public static BottomDialog build() { + return new BottomDialog(); + } + + public static BottomDialog build(DialogXStyle style) { + return new BottomDialog().setStyle(style); + } + + public static BottomDialog build(OnBindView onBindView) { + return new BottomDialog().setCustomView(onBindView); + } + + public BottomDialog(CharSequence title, CharSequence message) { + this.title = title; + this.message = message; + } + + public BottomDialog(int titleResId, int messageResId) { + this.title = getString(titleResId); + this.message = getString(messageResId); + } + + public static BottomDialog show(CharSequence title, CharSequence message) { + BottomDialog bottomDialog = new BottomDialog(title, message); + bottomDialog.show(); + return bottomDialog; + } + + public static BottomDialog show(int titleResId, int messageResId) { + BottomDialog bottomDialog = new BottomDialog(titleResId, messageResId); + bottomDialog.show(); + return bottomDialog; + } + + public BottomDialog(CharSequence title, CharSequence message, OnBindView onBindView) { + this.title = title; + this.message = message; + this.onBindView = onBindView; + } + + public BottomDialog(int titleResId, int messageResId, OnBindView onBindView) { + this.title = getString(titleResId); + this.message = getString(messageResId); + this.onBindView = onBindView; + } + + public static BottomDialog show(CharSequence title, CharSequence message, OnBindView onBindView) { + BottomDialog bottomDialog = new BottomDialog(title, message, onBindView); + bottomDialog.show(); + return bottomDialog; + } + + public static BottomDialog show(int titleResId, int messageResId, OnBindView onBindView) { + BottomDialog bottomDialog = new BottomDialog(titleResId, messageResId, onBindView); + bottomDialog.show(); + return bottomDialog; + } + + public BottomDialog(CharSequence title, OnBindView onBindView) { + this.title = title; + this.onBindView = onBindView; + } + + public BottomDialog(int titleResId, OnBindView onBindView) { + this.title = getString(titleResId); + this.onBindView = onBindView; + } + + public static BottomDialog show(CharSequence title, OnBindView onBindView) { + BottomDialog bottomDialog = new BottomDialog(title, onBindView); + bottomDialog.show(); + return bottomDialog; + } + + public static BottomDialog show(int titleResId, OnBindView onBindView) { + BottomDialog bottomDialog = new BottomDialog(titleResId, onBindView); + bottomDialog.show(); + return bottomDialog; + } + + public BottomDialog(OnBindView onBindView) { + this.onBindView = onBindView; + } + + public static BottomDialog show(OnBindView onBindView) { + BottomDialog bottomDialog = new BottomDialog(onBindView); + bottomDialog.show(); + return bottomDialog; + } + + public BottomDialog show() { + if (isHide && getDialogView() != null && isShow) { + if (hideWithExitAnim && getDialogImpl() != null) { + getDialogView().setVisibility(View.VISIBLE); + getDialogImpl().getDialogXAnimImpl().doShowAnim(me, getDialogImpl().bkg); + } else { + getDialogView().setVisibility(View.VISIBLE); + } + return this; + } + super.beforeShow(); + if (getDialogView() == null) { + int layoutId = isLightTheme() ? R.layout.layout_dialogx_bottom_material : R.layout.layout_dialogx_bottom_material_dark; + if (style.overrideBottomDialogRes() != null) { + layoutId = style.overrideBottomDialogRes().overrideDialogLayout(isLightTheme()); + } + + View dialogView = createView(layoutId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } else { + show(getDialogView()); + } + return this; + } + + public void show(Activity activity) { + super.beforeShow(); + if (getDialogView() == null) { + int layoutId = isLightTheme() ? R.layout.layout_dialogx_bottom_material : R.layout.layout_dialogx_bottom_material_dark; + if (style.overrideBottomDialogRes() != null) { + layoutId = style.overrideBottomDialogRes().overrideDialogLayout(isLightTheme()); + } + + View dialogView = createView(layoutId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(activity, dialogView); + } else { + show(activity, getDialogView()); + } + } + + protected DialogImpl dialogImpl; + + public class DialogImpl implements DialogConvertViewInterface { + + private BottomDialogTouchEventInterceptor bottomDialogTouchEventInterceptor; + + public DialogXBaseRelativeLayout boxRoot; + public RelativeLayout boxBkg; + public MaxRelativeLayout bkg; + public ImageView imgTab; + public ViewGroup boxBody; + public TextView txtDialogTitle; + public ScrollController scrollView; + public LinearLayout boxContent; + public TextView txtDialogTip; + public View imgSplit; + public ViewGroup boxList; + public RelativeLayout boxCustom; + public ViewGroup boxCancel; + public ImageView splitSelectPositive; + public ImageView splitSelectOther; + + public LinearLayout boxButton; + public TextView btnSelectNegative; + public TextView btnSelectOther; + public TextView btnSelectPositive; + + private List blurViews; + + public DialogImpl(View convertView) { + if (convertView == null) return; + setDialogView(convertView); + boxRoot = convertView.findViewById(R.id.box_root); + boxBkg = convertView.findViewById(R.id.box_bkg); + bkg = convertView.findViewById(R.id.bkg); + imgTab = convertView.findViewById(R.id.img_tab); + boxBody = convertView.findViewById(R.id.box_body); + txtDialogTitle = convertView.findViewById(R.id.txt_dialog_title); + scrollView = convertView.findViewById(R.id.scrollView); + boxContent = convertView.findViewById(R.id.box_content); + txtDialogTip = convertView.findViewById(R.id.txt_dialog_tip); + imgSplit = convertView.findViewWithTag("split"); + boxList = convertView.findViewById(R.id.box_list); + boxCustom = convertView.findViewById(R.id.box_custom); + + if (!scrollableWhenContentLargeThanVisibleRange) { + ViewGroup bodyContent = (ViewGroup) txtDialogTitle.getParent(); + ((ViewGroup) boxContent.getParent()).removeView(boxContent); + bodyContent.addView(boxContent, 1, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + } + boxCancel = convertView.findViewWithTag("cancelBox"); + + boxButton = convertView.findViewById(R.id.box_button); + btnSelectNegative = convertView.findViewById(R.id.btn_selectNegative); + btnSelectOther = convertView.findViewById(R.id.btn_selectOther); + btnSelectPositive = convertView.findViewById(R.id.btn_selectPositive); + splitSelectPositive = convertView.findViewWithTag("imgPositiveButtonSplit"); + splitSelectOther = convertView.findViewWithTag("imgOtherButtonSplit"); + + blurViews = findAllBlurView(convertView); + + init(); + dialogImpl = this; + refreshView(); + } + + public void reBuild() { + init(); + dialogImpl = this; + refreshView(); + } + + /** + * 此值记录了BottomDialog启动后的位置 + * ·当内容高度大于屏幕安全区高度时,BottomDialog会以全屏方式启动,但一开始只会展开到 0.8×屏幕高度, + * 此时可以再次上划查看全部内容。 + * ·当内容高度小于屏幕安全区高度时,BottomDialog会以内容高度启动。 + *

+ * 记录这个值的目的是,当用户向下滑动时,判断情况该回到这个位置还是关闭对话框, + * 并阻止当内容高度已经完全显示时的继续向上滑动操作。 + */ + public float bkgEnterAimY = -1; + + @Override + public void init() { + buttonSelectResult = BUTTON_SELECT_RESULT.NONE; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(getThisOrderIndex()); + } + + if (titleTextInfo == null) titleTextInfo = DialogX.titleTextInfo; + if (messageTextInfo == null) messageTextInfo = DialogX.messageTextInfo; + if (okTextInfo == null) okTextInfo = DialogX.okButtonTextInfo; + if (okTextInfo == null) okTextInfo = DialogX.buttonTextInfo; + if (cancelTextInfo == null) cancelTextInfo = DialogX.buttonTextInfo; + if (otherTextInfo == null) otherTextInfo = DialogX.buttonTextInfo; + if (backgroundColor == null) backgroundColor = DialogX.backgroundColor; + if (cancelText == null) cancelText = DialogX.cancelButtonText; + + if (txtDialogTitle != null) txtDialogTitle.getPaint().setFakeBoldText(true); + if (btnSelectNegative != null) btnSelectNegative.getPaint().setFakeBoldText(true); + if (btnSelectPositive != null) btnSelectPositive.getPaint().setFakeBoldText(true); + if (btnSelectOther != null) btnSelectOther.getPaint().setFakeBoldText(true); + + boxBkg.setY(getRootFrameLayout() == null ? Resources.getSystem().getDisplayMetrics().heightPixels : getRootFrameLayout().getMeasuredHeight()); + + boxRoot.setParentDialog(me); + boxRoot.setOnLifecycleCallBack(new DialogXBaseRelativeLayout.OnLifecycleCallBack() { + @Override + public void onShow() { + + isShow = true; + preShow = false; + + setLifecycleState(Lifecycle.State.CREATED); + getDialogLifecycleCallback().onShow(me); + BottomDialog.this.onShow(me); + + onDialogShow(); + + refreshUI(); + } + + @Override + public void onDismiss() { + isShow = false; + getDialogLifecycleCallback().onDismiss(me); + BottomDialog.this.onDismiss(me); + setLifecycleState(Lifecycle.State.DESTROYED); + dialogImpl = null; + bottomDialogTouchEventInterceptor = null; + dialogLifecycleCallback = null; + System.gc(); + } + }); + + if (btnSelectNegative != null) { + btnSelectNegative.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + haptic(v); + buttonSelectResult = BUTTON_SELECT_RESULT.BUTTON_CANCEL; + if (cancelButtonClickListener != null) { + if (cancelButtonClickListener instanceof OnDialogButtonClickListener) { + if (!((OnDialogButtonClickListener) cancelButtonClickListener).onClick(me, v)) { + dismiss(); + } + } else if (cancelButtonClickListener instanceof OnMenuButtonClickListener) { + if (!((OnMenuButtonClickListener) cancelButtonClickListener).onClick(me, v)) { + dismiss(); + } + } + } else { + dismiss(); + } + } + }); + } + if (btnSelectOther != null) { + btnSelectOther.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + haptic(v); + buttonSelectResult = BUTTON_SELECT_RESULT.BUTTON_OTHER; + if (otherButtonClickListener != null) { + if (otherButtonClickListener instanceof OnDialogButtonClickListener) { + if (!((OnDialogButtonClickListener) otherButtonClickListener).onClick(me, v)) { + dismiss(); + } + } else if (otherButtonClickListener instanceof OnMenuButtonClickListener) { + if (!((OnMenuButtonClickListener) otherButtonClickListener).onClick(me, v)) { + dismiss(); + } + } + } else { + dismiss(); + } + } + }); + } + if (btnSelectPositive != null) { + btnSelectPositive.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + haptic(v); + buttonSelectResult = BUTTON_SELECT_RESULT.BUTTON_OK; + if (okButtonClickListener != null) { + if (okButtonClickListener instanceof OnDialogButtonClickListener) { + if (!((OnDialogButtonClickListener) okButtonClickListener).onClick(me, v)) { + dismiss(); + } + } else if (okButtonClickListener instanceof OnMenuButtonClickListener) { + if (!((OnMenuButtonClickListener) okButtonClickListener).onClick(me, v)) { + dismiss(); + } + } + } else { + dismiss(); + } + } + }); + } + + if (imgSplit != null) { + int dividerRes = style.overrideBottomDialogRes().overrideMenuDividerDrawableRes(isLightTheme()); + int dividerHeight = style.overrideBottomDialogRes().overrideMenuDividerHeight(isLightTheme()); + if (dividerRes != 0) imgSplit.setBackgroundResource(dividerRes); + if (dividerHeight != 0) { + ViewGroup.LayoutParams lp = imgSplit.getLayoutParams(); + lp.height = dividerHeight; + imgSplit.setLayoutParams(lp); + } + } + + boxRoot.setOnBackPressedListener(new DialogXBaseRelativeLayout.PrivateBackPressedListener() { + @Override + public boolean onBackPressed() { + if (onBackPressedListener != null) { + if (onBackPressedListener.onBackPressed(me)) { + dismiss(); + } + } else { + if (isCancelable()) { + dismiss(); + } + } + return true; + } + }); + + boxBkg.post(new Runnable() { + @Override + public void run() { + getDialogXAnimImpl().doShowAnim(BottomDialog.this, bkg); + + Integer blurFrontColor = null; + Float dialogXRadius = null; + if (style.messageDialogBlurSettings() != null) { + blurFrontColor = getColorNullable(getIntStyleAttr(style.messageDialogBlurSettings().blurForwardColorRes(isLightTheme()))); + dialogXRadius = getFloatStyleAttr((float) style.messageDialogBlurSettings().blurBackgroundRoundRadiusPx()); + } + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setOverlayColor(backgroundColor == null ? blurFrontColor : backgroundColor); + ((BlurViewType) blurView).setRadiusPx(dialogXRadius); + } + } + } + }); + + runOnMainDelay(new Runnable() { + @Override + public void run() { + bottomDialogTouchEventInterceptor = new BottomDialogTouchEventInterceptor(me, dialogImpl); + } + }, getEnterAnimationDuration()); + + onDialogInit(); + } + + @Override + public void refreshView() { + if (boxRoot == null || getOwnActivity() == null) { + return; + } + + bkg.setMaxWidth(getMaxWidth()); + bkg.setMaxHeight(getMaxHeight()); + bkg.setMinimumWidth(getMinWidth()); + bkg.setMinimumHeight(getMinHeight()); + + boxRoot.setAutoUnsafePlacePadding(isEnableImmersiveMode()); + boxRoot.setRootPadding(screenPaddings[0], screenPaddings[1], screenPaddings[2], screenPaddings[3]); + if (backgroundColor != null) { + tintColor(bkg, backgroundColor); + if (style.tintButtonBackground()) { + tintColor(btnSelectOther, backgroundColor); + tintColor(btnSelectNegative, backgroundColor); + tintColor(btnSelectPositive, backgroundColor); + } + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setOverlayColor(backgroundColor); + } + } + } + + showText(txtDialogTitle, title); + showText(txtDialogTip, message); + + useTextInfo(txtDialogTitle, titleTextInfo); + useTextInfo(txtDialogTip, messageTextInfo); + useTextInfo(btnSelectNegative, cancelTextInfo); + useTextInfo(btnSelectOther, otherTextInfo); + useTextInfo(btnSelectPositive, okTextInfo); + + if (boxButton != null) { + boxButton.setVisibility((btnSelectNegative != null && btnSelectNegative.getVisibility() == View.VISIBLE) || + (btnSelectOther != null && btnSelectOther.getVisibility() == View.VISIBLE) || + (btnSelectPositive != null && btnSelectPositive.getVisibility() == View.VISIBLE) ? + View.VISIBLE : View.GONE); + } + if (titleIcon != null) { + int size = (int) txtDialogTitle.getTextSize(); + titleIcon.setBounds(0, 0, size, size); + txtDialogTitle.setCompoundDrawablePadding(dip2px(10)); + txtDialogTitle.setCompoundDrawables(titleIcon, null, null, null); + } + + if (bkgInterceptTouch) { + if (isCancelable()) { + boxRoot.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onBackgroundMaskClickListener == null || !onBackgroundMaskClickListener.onClick(me, v)) { + doDismiss(v); + } + } + }); + } else { + boxRoot.setOnClickListener(null); + } + } else { + boxRoot.setClickable(false); + } + boxBkg.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + boxRoot.callOnClick(); + } + }); + if (backgroundRadius > -1) { + if (bkg.getBackground() instanceof GradientDrawable) { + GradientDrawable gradientDrawable = (GradientDrawable) bkg.getBackground(); + if (gradientDrawable != null) + gradientDrawable.setCornerRadii(new float[]{backgroundRadius, backgroundRadius, backgroundRadius, backgroundRadius, 0, 0, 0, 0}); + } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + bkg.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getWidth(), (int) (view.getHeight() + backgroundRadius), backgroundRadius); + } + }); + bkg.setClipToOutline(true); + } + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setRadiusPx(backgroundRadius); + } + } + } + + if (maskColor != null) { + boxRoot.setBackground(new ColorDrawable(maskColor)); + } + + if (onBindView != null && onBindView.getCustomView() != null) { + onBindView.bindParent(boxCustom, me); + if (onBindView.getCustomView() instanceof ScrollController) { + if (scrollView instanceof DialogScrollView) { + ((DialogScrollView) scrollView).setVerticalScrollBarEnabled(false); + } + scrollView = (ScrollController) onBindView.getCustomView(); + } else { + View scrollController = onBindView.getCustomView().findViewWithTag("ScrollController"); + if (scrollController instanceof ScrollController) { + if (scrollView instanceof DialogScrollView) { + ((DialogScrollView) scrollView).setVerticalScrollBarEnabled(false); + } + scrollView = (ScrollController) scrollController; + } + } + } + + if (isAllowInterceptTouch() && isCancelable()) { + if (imgTab != null) imgTab.setVisibility(View.VISIBLE); + } else { + if (imgTab != null) imgTab.setVisibility(View.GONE); + } + + if (bottomDialogTouchEventInterceptor != null) { + bottomDialogTouchEventInterceptor.refresh(me, this); + } + + if (imgSplit != null) { + if (txtDialogTitle.getVisibility() == View.VISIBLE || txtDialogTip.getVisibility() == View.VISIBLE) { + imgSplit.setVisibility(View.VISIBLE); + } else { + imgSplit.setVisibility(View.GONE); + } + } + + if (boxCancel != null) { + if (isNull(cancelText)) { + boxCancel.setVisibility(View.GONE); + } else { + boxCancel.setVisibility(View.VISIBLE); + } + } + + showText(btnSelectPositive, okText); + showText(btnSelectNegative, cancelText); + showText(btnSelectOther, otherText); + if (splitSelectPositive != null) { + splitSelectPositive.setVisibility(btnSelectPositive.getVisibility()); + } + if (splitSelectOther != null) { + splitSelectOther.setVisibility(btnSelectOther.getVisibility()); + } + + onDialogRefreshUI(); + } + + @Override + public void doDismiss(View v) { + if (BottomDialog.this.preDismiss(BottomDialog.this)) { + return; + } + if (v != null) v.setEnabled(false); + if (getOwnActivity() == null) return; + + if (!dismissAnimFlag && getDialogXAnimImpl() != null) { + dismissAnimFlag = true; + + getDialogXAnimImpl().doExitAnim(BottomDialog.this, bkg); + + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (boxRoot != null) { + boxRoot.setVisibility(View.GONE); + } + dismiss(getDialogView()); + } + }, getExitAnimationDuration()); + } + } + + public void preDismiss() { + if (isCancelable()) { + if (getDialogLifecycleCallback() instanceof BottomDialogSlideEventLifecycleCallback) { + if (!((BottomDialogSlideEventLifecycleCallback) getDialogLifecycleCallback()).onSlideClose(me)) { + doDismiss(boxRoot); + } + return; + } + doDismiss(boxRoot); + } else { + long exitAnimDurationTemp = 300; + if (overrideExitDuration >= 0) { + exitAnimDurationTemp = overrideExitDuration; + } + if (exitAnimDuration >= 0) { + exitAnimDurationTemp = exitAnimDuration; + } + ObjectAnimator exitAnim = ObjectAnimator.ofFloat(boxBkg, "y", boxBkg.getY(), boxRoot.getUnsafePlace().top); + exitAnim.setDuration(exitAnimDurationTemp); + exitAnim.start(); + } + } + + protected DialogXAnimInterface getDialogXAnimImpl() { + if (dialogXAnimImpl == null) { + dialogXAnimImpl = new DialogXAnimInterface() { + @Override + public void doShowAnim(BottomDialog dialog, ViewGroup dialogBodyView) { + long enterAnimDurationTemp = getEnterAnimationDuration(); + + float customDialogTop = 0; + if (dialog.isAllowInterceptTouch()) { + if (bottomDialogMaxHeight > 0 && bottomDialogMaxHeight <= 1) { + customDialogTop = boxBkg.getHeight() - bottomDialogMaxHeight * boxBkg.getHeight(); + } else if (bottomDialogMaxHeight > 1) { + customDialogTop = boxBkg.getHeight() - bottomDialogMaxHeight; + } + } else { + if (bottomDialogMaxHeight > 0 && bottomDialogMaxHeight <= 1) { + customDialogTop = boxBkg.getHeight() - bottomDialogMaxHeight * boxBkg.getHeight(); + } else if (bottomDialogMaxHeight > 1) { + customDialogTop = boxBkg.getHeight() - bottomDialogMaxHeight; + } + boxBkg.setPadding(0, 0, 0, (int) customDialogTop); + } + + // 上移动画 + ObjectAnimator enterAnim = ObjectAnimator.ofFloat(boxBkg, "y", getRootFrameLayout() == null ? Resources.getSystem().getDisplayMetrics().heightPixels : getRootFrameLayout().getMeasuredHeight(), bkgEnterAimY = boxRoot.getUnsafePlace().top + customDialogTop); + enterAnim.setDuration(enterAnimDurationTemp); + enterAnim.setAutoCancel(true); + enterAnim.setInterpolator(new DecelerateInterpolator(2f)); + enterAnim.start(); + + // 遮罩层动画 + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f); + bkgAlpha.setDuration(enterAnimDurationTemp); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + }); + bkgAlpha.start(); + } + + @Override + public void doExitAnim(BottomDialog dialog, ViewGroup dialogBodyView) { + long exitAnimDurationTemp = getExitAnimationDuration(); + + ObjectAnimator exitAnim = ObjectAnimator.ofFloat(boxBkg, "y", boxBkg.getY(), boxBkg.getHeight()); + exitAnim.setDuration(exitAnimDurationTemp); + exitAnim.start(); + + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f); + bkgAlpha.setDuration(exitAnimDurationTemp); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + }); + bkgAlpha.start(); + } + }; + } + return dialogXAnimImpl; + } + + public long getExitAnimationDuration() { + long exitAnimDurationTemp = 300; + if (overrideExitDuration >= 0) { + exitAnimDurationTemp = overrideExitDuration; + } + if (exitAnimDuration != -1) { + exitAnimDurationTemp = exitAnimDuration; + } + return exitAnimDurationTemp; + } + + public long getEnterAnimationDuration() { + long enterAnimDurationTemp = 300; + if (overrideEnterDuration >= 0) { + enterAnimDurationTemp = overrideEnterDuration; + } + if (enterAnimDuration >= 0) { + enterAnimDurationTemp = enterAnimDuration; + } + return enterAnimDurationTemp; + } + + public BottomDialogTouchEventInterceptor getBottomDialogTouchEventInterceptor() { + return bottomDialogTouchEventInterceptor; + } + } + + public void refreshUI() { + if (getDialogImpl() == null) return; + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl != null) dialogImpl.refreshView(); + } + }); + } + + public void dismiss() { + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl == null) return; + dialogImpl.doDismiss(null); + } + }); + } + + public DialogLifecycleCallback getDialogLifecycleCallback() { + return dialogLifecycleCallback == null ? new DialogLifecycleCallback() { + } : dialogLifecycleCallback; + } + + public BottomDialog setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me); + return this; + } + + public OnBackPressedListener getOnBackPressedListener() { + return (OnBackPressedListener) onBackPressedListener; + } + + public BottomDialog setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + refreshUI(); + return this; + } + + public BottomDialog setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public BottomDialog setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public boolean isCancelable() { + if (privateCancelable != null) { + return privateCancelable == BOOLEAN.TRUE; + } + if (overrideCancelable != null) { + return overrideCancelable == BOOLEAN.TRUE; + } + return cancelable; + } + + public BottomDialog setCancelable(boolean cancelable) { + this.privateCancelable = cancelable ? BOOLEAN.TRUE : BOOLEAN.FALSE; + refreshUI(); + return this; + } + + public DialogImpl getDialogImpl() { + return dialogImpl; + } + + public CharSequence getTitle() { + return title; + } + + public BottomDialog setTitle(CharSequence title) { + this.title = title; + refreshUI(); + return this; + } + + public BottomDialog setTitle(int titleResId) { + this.title = getString(titleResId); + refreshUI(); + return this; + } + + public CharSequence getMessage() { + return message; + } + + public BottomDialog setMessage(CharSequence message) { + this.message = message; + refreshUI(); + return this; + } + + public BottomDialog setMessage(int messageResId) { + this.message = getString(messageResId); + refreshUI(); + return this; + } + + public CharSequence getCancelButton() { + return cancelText; + } + + public BottomDialog setCancelButton(CharSequence cancelText) { + this.cancelText = cancelText; + refreshUI(); + return this; + } + + public BottomDialog setCancelButton(int cancelTextResId) { + this.cancelText = getString(cancelTextResId); + refreshUI(); + return this; + } + + public BottomDialog setCancelButton(OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + public BottomDialog setCancelButton(CharSequence cancelText, OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelText = cancelText; + this.cancelButtonClickListener = cancelButtonClickListener; + refreshUI(); + return this; + } + + public BottomDialog setCancelButton(int cancelTextResId, OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelText = getString(cancelTextResId); + this.cancelButtonClickListener = cancelButtonClickListener; + refreshUI(); + return this; + } + + public BottomDialog setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + refreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) return null; + return onBindView.getCustomView(); + } + + public BottomDialog removeCustomView() { + this.onBindView.clean(); + refreshUI(); + return this; + } + + public boolean isAllowInterceptTouch() { + if (style.overrideBottomDialogRes() == null) { + return false; + } else { + return allowInterceptTouch && style.overrideBottomDialogRes().touchSlide(); + } + } + + public BottomDialog setAllowInterceptTouch(boolean allowInterceptTouch) { + this.allowInterceptTouch = allowInterceptTouch; + return this; + } + + public OnDialogButtonClickListener getCancelButtonClickListener() { + return (OnDialogButtonClickListener) cancelButtonClickListener; + } + + public BottomDialog setCancelButtonClickListener(OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + public TextInfo getTitleTextInfo() { + return titleTextInfo; + } + + public BottomDialog setTitleTextInfo(TextInfo titleTextInfo) { + this.titleTextInfo = titleTextInfo; + refreshUI(); + return this; + } + + public TextInfo getMessageTextInfo() { + return messageTextInfo; + } + + public BottomDialog setMessageTextInfo(TextInfo messageTextInfo) { + this.messageTextInfo = messageTextInfo; + refreshUI(); + return this; + } + + public TextInfo getCancelTextInfo() { + return cancelTextInfo; + } + + public BottomDialog setCancelTextInfo(TextInfo cancelTextInfo) { + this.cancelTextInfo = cancelTextInfo; + refreshUI(); + return this; + } + + public TextInfo getOkTextInfo() { + return okTextInfo; + } + + public BottomDialog setOkTextInfo(TextInfo okTextInfo) { + this.okTextInfo = okTextInfo; + return this; + } + + public TextInfo getOtherTextInfo() { + return otherTextInfo; + } + + public BottomDialog setOtherTextInfo(TextInfo otherTextInfo) { + this.otherTextInfo = otherTextInfo; + return this; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public BottomDialog setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + refreshUI(); + return this; + } + + public BottomDialog setBackgroundColorRes(@ColorRes int backgroundRes) { + this.backgroundColor = getColor(backgroundRes); + refreshUI(); + return this; + } + + public CharSequence getOkButton() { + return okText; + } + + public BottomDialog setOkButton(CharSequence okText) { + this.okText = okText; + refreshUI(); + return this; + } + + public BottomDialog setOkButton(int OkTextResId) { + this.okText = getString(OkTextResId); + refreshUI(); + return this; + } + + public BottomDialog setOkButton(OnDialogButtonClickListener OkButtonClickListener) { + this.okButtonClickListener = OkButtonClickListener; + return this; + } + + public BottomDialog setOkButton(CharSequence OkText, OnDialogButtonClickListener OkButtonClickListener) { + this.okText = OkText; + this.okButtonClickListener = OkButtonClickListener; + refreshUI(); + return this; + } + + public BottomDialog setOkButton(int OkTextResId, OnDialogButtonClickListener OkButtonClickListener) { + this.okText = getString(OkTextResId); + this.okButtonClickListener = OkButtonClickListener; + refreshUI(); + return this; + } + + public CharSequence getOtherButton() { + return otherText; + } + + public BottomDialog setOtherButton(CharSequence otherText) { + this.otherText = otherText; + refreshUI(); + return this; + } + + public BottomDialog setOtherButton(int OtherTextResId) { + this.otherText = getString(OtherTextResId); + refreshUI(); + return this; + } + + public BottomDialog setOtherButton(OnDialogButtonClickListener OtherButtonClickListener) { + this.otherButtonClickListener = OtherButtonClickListener; + return this; + } + + public BottomDialog setOtherButton(CharSequence OtherText, OnDialogButtonClickListener OtherButtonClickListener) { + this.otherText = OtherText; + this.otherButtonClickListener = OtherButtonClickListener; + refreshUI(); + return this; + } + + public BottomDialog setOtherButton(int OtherTextResId, OnDialogButtonClickListener OtherButtonClickListener) { + this.otherText = getString(OtherTextResId); + this.otherButtonClickListener = OtherButtonClickListener; + refreshUI(); + return this; + } + + public BottomDialog setMaskColor(@ColorInt int maskColor) { + this.maskColor = maskColor; + refreshUI(); + return this; + } + + public long getEnterAnimDuration() { + return enterAnimDuration; + } + + public BottomDialog setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public long getExitAnimDuration() { + return exitAnimDuration; + } + + public BottomDialog setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + @Override + public void restartDialog() { + if (getDialogView() != null) { + dismiss(getDialogView()); + isShow = false; + } + if (getDialogImpl().boxCustom != null) { + getDialogImpl().boxCustom.removeAllViews(); + } + if (getDialogImpl().boxList != null) { + getDialogImpl().boxList.removeAllViews(); + } + int layoutId = isLightTheme() ? R.layout.layout_dialogx_bottom_material : R.layout.layout_dialogx_bottom_material_dark; + if (style.overrideBottomDialogRes() != null) { + layoutId = style.overrideBottomDialogRes().overrideDialogLayout(isLightTheme()); + } + + enterAnimDuration = 0; + View dialogView = createView(layoutId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } + + protected boolean isHide; + + public void hide() { + isHide = true; + hideWithExitAnim = false; + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + + protected boolean hideWithExitAnim; + + public void hideWithExitAnim() { + hideWithExitAnim = true; + isHide = true; + if (getDialogImpl() != null) { + getDialogImpl().getDialogXAnimImpl().doExitAnim(me, getDialogImpl().bkg); + + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + }, getDialogImpl().getExitAnimationDuration()); + } + } + + @Override + protected void shutdown() { + dismiss(); + } + + public float getBottomDialogMaxHeight() { + return bottomDialogMaxHeight; + } + + public BottomDialog setBottomDialogMaxHeight(float bottomDialogMaxHeight) { + this.bottomDialogMaxHeight = bottomDialogMaxHeight; + return this; + } + + public BottomDialog setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + refreshUI(); + return this; + } + + public BottomDialog setMaxHeight(int maxHeight) { + this.maxHeight = maxHeight; + refreshUI(); + return this; + } + + public BottomDialog setMinHeight(int minHeight) { + this.minHeight = minHeight; + refreshUI(); + return this; + } + + public BottomDialog setMinWidth(int minWidth) { + this.minWidth = minWidth; + refreshUI(); + return this; + } + + public BottomDialog setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public boolean isBkgInterceptTouch() { + return bkgInterceptTouch; + } + + public BottomDialog setBkgInterceptTouch(boolean bkgInterceptTouch) { + this.bkgInterceptTouch = bkgInterceptTouch; + return this; + } + + public OnBackgroundMaskClickListener getOnBackgroundMaskClickListener() { + return (OnBackgroundMaskClickListener) onBackgroundMaskClickListener; + } + + public BottomDialog setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + return this; + } + + public BottomDialog setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public float getRadius() { + return backgroundRadius; + } + + public Drawable getTitleIcon() { + return titleIcon; + } + + public BottomDialog setTitleIcon(Bitmap titleIcon) { + this.titleIcon = new BitmapDrawable(getResources(), titleIcon); + refreshUI(); + return this; + } + + public BottomDialog setTitleIcon(int titleIconResId) { + this.titleIcon = getResources().getDrawable(titleIconResId); + refreshUI(); + return this; + } + + public BottomDialog setTitleIcon(Drawable titleIcon) { + this.titleIcon = titleIcon; + refreshUI(); + return this; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public BottomDialog setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public BottomDialog setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public BottomDialog setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + public BUTTON_SELECT_RESULT getButtonSelectResult() { + return buttonSelectResult; + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new BottomDialog() { + * + * @param dialog self + * @Override public void onShow(BottomDialog dialog) { + * //... + * } + * } + */ + protected void onShow(BottomDialog dialog) { + + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new BottomDialog() { + * + * @param dialog self + * @Override public boolean onDismiss(BottomDialog dialog) { + * WaitDialog.show("Please Wait..."); + * if (dialog.getButtonSelectResult() == BUTTON_SELECT_RESULT.BUTTON_OK) { + * //点击了OK的情况 + * //... + * } else { + * //其他按钮点击、对话框dismiss的情况 + * //... + * } + * return false; + * } + * } + */ + // 用于使用 new 构建实例时,override 的生命周期事件 + protected void onDismiss(BottomDialog dialog) { + + } + + public boolean isBottomNonSafetyAreaBySelf() { + return bottomNonSafetyAreaBySelf; + } + + public BottomDialog setBottomNonSafetyAreaBySelf(boolean bottomNonSafetyAreaBySelf) { + this.bottomNonSafetyAreaBySelf = bottomNonSafetyAreaBySelf; + return this; + } + + public boolean isScrollableWhenContentLargeThanVisibleRange() { + return scrollableWhenContentLargeThanVisibleRange; + } + + public BottomDialog setScrollableWhenContentLargeThanVisibleRange(boolean scrollableWhenContentLargeThanVisibleRange) { + this.scrollableWhenContentLargeThanVisibleRange = scrollableWhenContentLargeThanVisibleRange; + return this; + } + + public BottomDialog setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public BottomDialog setHapticFeedbackEnabled(boolean isHapticFeedbackEnabled) { + this.isHapticFeedbackEnabled = isHapticFeedbackEnabled ? 1 : 0; + return this; + } + + public BottomDialog onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public BottomDialog onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public BottomDialog setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public BottomDialog appendMessage(CharSequence message) { + this.message = TextUtils.concat(this.message, message); + refreshUI(); + return this; + } + + public BottomDialog setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex(" + orderIndex + ") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public BottomDialog bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public BottomDialog setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public BottomDialog cleanAction(int actionId) { + dialogActionRunnableMap.remove(actionId); + return this; + } + + public BottomDialog cleanAllAction() { + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss() { + dismiss(); + } + + public BottomDialog bindDismissWithLifecycleOwner(LifecycleOwner owner) { + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/BottomMenu.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/BottomMenu.java new file mode 100644 index 0000000..134d52a --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/BottomMenu.java @@ -0,0 +1,1605 @@ +package com.kongzue.dialogx.dialogs; + +import static android.view.View.OVER_SCROLL_NEVER; + +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.text.TextUtils; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.LinearLayout; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BottomMenuListViewTouchEvent; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.MenuItemLayoutRefreshCallback; +import com.kongzue.dialogx.interfaces.MenuItemTextInfoInterceptor; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.OnMenuButtonClickListener; +import com.kongzue.dialogx.interfaces.OnDialogButtonClickListener; +import com.kongzue.dialogx.interfaces.OnIconChangeCallBack; +import com.kongzue.dialogx.interfaces.OnMenuItemClickListener; +import com.kongzue.dialogx.interfaces.OnMenuItemSelectListener; +import com.kongzue.dialogx.interfaces.SELECT_MODE; +import com.kongzue.dialogx.util.BottomMenuArrayAdapter; +import com.kongzue.dialogx.util.ItemDivider; +import com.kongzue.dialogx.util.TextInfo; +import com.kongzue.dialogx.util.views.DialogListView; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/6 23:48 + */ +public class BottomMenu extends BottomDialog { + + protected BottomMenu me = this; + protected int selectionIndex = -1; + protected SELECT_MODE selectMode = SELECT_MODE.NONE; + protected ArrayList selectionItems; + protected boolean showSelectedBackgroundTips = false; + protected MenuItemLayoutRefreshCallback menuMenuItemLayoutRefreshCallback; + protected Map menuUsability = new HashMap(); + protected ItemDivider itemDivider; + + protected OnMenuItemClickListener onMenuItemClickListener; + + public static BottomMenu build() { + return new BottomMenu(); + } + + public static BottomMenu build(DialogXStyle style) { + return new BottomMenu().setStyle(style); + } + + public static BottomMenu build(OnBindView onBindView) { + return new BottomMenu().setCustomView(onBindView); + } + + protected BottomMenu() { + super(); + } + + protected OnIconChangeCallBack onIconChangeCallBack; + protected MenuItemTextInfoInterceptor menuItemTextInfoInterceptor; + protected DialogListView listView; + protected BaseAdapter menuListAdapter; + protected List menuList; + protected List iconResIds; + protected boolean autoTintIconInLightOrDarkMode = true; + + public static BottomMenu show(List menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(List menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu showStringList(List menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.setMenuStringList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu showStringList(List menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.setMenuStringList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(String... menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, CharSequence message, List menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, CharSequence message, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, List menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu showStringList(CharSequence title, CharSequence message, List menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuStringList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu showStringList(CharSequence title, CharSequence message, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuStringList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, CharSequence message, String[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, CharSequence message, String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, CharSequence message, CharSequence[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, CharSequence message, CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(String title, String message, List menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(String title, String message, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu showStringList(String title, String message, List menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuStringList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu showStringList(String title, String message, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuStringList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(String title, String message, String[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(String title, String message, String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(String title, String message, CharSequence[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(String title, String message, CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.message = message; + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, int messageResId, List menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.message = bottomMenu.getString(messageResId); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, List menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu showStringList(int titleResId, int messageResId, List menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.message = bottomMenu.getString(messageResId); + bottomMenu.setMenuStringList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, int messageResId, String[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.message = bottomMenu.getString(messageResId); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, int messageResId, CharSequence[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.message = bottomMenu.getString(messageResId); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, int messageResId, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.message = bottomMenu.getString(messageResId); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu showStringList(int titleResId, int messageResId, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.message = bottomMenu.getString(messageResId); + bottomMenu.setMenuStringList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, int messageResId, String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.message = bottomMenu.getString(messageResId); + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, int messageResId, CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.message = bottomMenu.getString(messageResId); + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, CharSequence[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, String[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(CharSequence title, String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = title; + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, CharSequence[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, String[] menuList) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.setMenuList(menuList); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + public static BottomMenu show(int titleResId, String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + BottomMenu bottomMenu = new BottomMenu(); + bottomMenu.title = bottomMenu.getString(titleResId); + bottomMenu.setMenuList(menuList); + bottomMenu.setOnMenuItemClickListener(onMenuItemClickListener); + bottomMenu.show(); + return bottomMenu; + } + + private float touchDownY; + + public static final int ITEM_CLICK_DELAY = 100; + private long lastClickTime = 0; + private int[] resultArray; + private CharSequence[] selectTextArray; + + @Override + protected void onDialogShow() { + if (getDialogImpl() != null) { + getDialogImpl().boxList.setVisibility(View.VISIBLE); + + if (!isAllowInterceptTouch()) { + getDialogImpl().bkg.setMaxHeight((int) bottomDialogMaxHeight); + if (bottomDialogMaxHeight != 0) { + dialogImpl.scrollView.lockScroll(true); + } + } + + int dividerDrawableResId = 0; + int dividerHeight = 1; + if (style.overrideBottomDialogRes() != null) { + dividerDrawableResId = style.overrideBottomDialogRes().overrideMenuDividerDrawableRes(isLightTheme()); + dividerHeight = style.overrideBottomDialogRes().overrideMenuDividerHeight(isLightTheme()); + } + if (dividerDrawableResId == 0) { + dividerDrawableResId = isLightTheme() ? R.drawable.rect_dialogx_material_menu_split_divider : R.drawable.rect_dialogx_material_menu_split_divider_night; + } + + + if (!isLightTheme()) { + listView = new DialogListView(getDialogImpl(), getOwnActivity(), R.style.DialogXCompatThemeDark); + } else { + listView = new DialogListView(getDialogImpl(), getOwnActivity()); + } + listView.setTag("ScrollController"); + listView.setOverScrollMode(OVER_SCROLL_NEVER); + listView.setDivider(getResources().getDrawable(dividerDrawableResId)); + listView.setDividerHeight(dividerHeight); + getDialogImpl().scrollView = listView; + + listView.setBottomMenuListViewTouchEvent(new BottomMenuListViewTouchEvent() { + @Override + public void down(MotionEvent event) { + touchDownY = getDialogImpl().boxBkg.getY(); + log("#TouchDown: " + touchDownY); + } + }); + + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (!isMenuItemEnable(position)) { + return; + } + haptic(view); + long currentTime = System.currentTimeMillis(); + if (currentTime - lastClickTime > ITEM_CLICK_DELAY) { + lastClickTime = currentTime; + float deltaY = Math.abs(touchDownY - getDialogImpl().boxBkg.getY()); + log("#Click:deltaY= " + deltaY); + if (deltaY > dip2px(15)) { + return; + } + selectionIndex = position; + log("### onMenuItemClickListener=" + onMenuItemClickListener); + switch (selectMode) { + case NONE: + if (onMenuItemClickListener != null) { + if (!onMenuItemClickListener.onClick(me, menuList.get(position), position)) { + dismiss(); + } + } else { + dismiss(); + } + break; + case SINGLE: + if (onMenuItemClickListener instanceof OnMenuItemSelectListener) { + OnMenuItemSelectListener onMenuItemSelectListener = (OnMenuItemSelectListener) onMenuItemClickListener; + if (!onMenuItemSelectListener.onClick(me, menuList.get(position), position)) { + dismiss(); + } else { + menuListAdapter.notifyDataSetInvalidated(); + onMenuItemSelectListener.onOneItemSelect(me, menuList.get(position), position, true); + } + } else { + if (onMenuItemClickListener != null) { + if (!onMenuItemClickListener.onClick(me, menuList.get(position), position)) { + dismiss(); + } + } else { + menuListAdapter.notifyDataSetInvalidated(); + } + } + break; + case MULTIPLE: + if (onMenuItemClickListener instanceof OnMenuItemSelectListener) { + OnMenuItemSelectListener onMenuItemSelectListener = (OnMenuItemSelectListener) onMenuItemClickListener; + if (!onMenuItemSelectListener.onClick(me, menuList.get(position), position)) { + dismiss(); + } else { + if (selectionItems.contains(position)) { + selectionItems.remove(new Integer(position)); + } else { + selectionItems.add(position); + } + menuListAdapter.notifyDataSetInvalidated(); + resultArray = new int[selectionItems.size()]; + selectTextArray = new CharSequence[selectionItems.size()]; + for (int i = 0; i < selectionItems.size(); i++) { + resultArray[i] = selectionItems.get(i); + selectTextArray[i] = menuList.get(resultArray[i]); + } + onMenuItemSelectListener.onMultiItemSelect(me, selectTextArray, resultArray); + } + } else { + if (onMenuItemClickListener != null) { + if (!onMenuItemClickListener.onClick(me, menuList.get(position), position)) { + dismiss(); + } + } else { + if (selectionItems.contains(position)) { + selectionItems.remove(new Integer(position)); + } else { + selectionItems.add(position); + } + menuListAdapter.notifyDataSetInvalidated(); + resultArray = new int[selectionItems.size()]; + selectTextArray = new CharSequence[selectionItems.size()]; + for (int i = 0; i < selectionItems.size(); i++) { + resultArray[i] = selectionItems.get(i); + selectTextArray[i] = menuList.get(resultArray[i]); + } + } + } + break; + } + } + } + }); + if (style.overrideBottomDialogRes() != null) { + if (style.overrideBottomDialogRes().overrideMenuItemLayout(true, 0, 0, false) != 0) { + listView.setSelector(R.color.empty); + } + } + + ViewGroup.LayoutParams listViewLp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + getDialogImpl().boxList.addView(listView, listViewLp); + refreshUI(); + } + } + + @Override + public void refreshUI() { + if (getDialogImpl() == null) return; + if (listView != null) { + if (menuListAdapter == null) { + menuListAdapter = new BottomMenuArrayAdapter(me, getOwnActivity(), menuList); + } + if (listView.getAdapter() == null) { + listView.setAdapter(menuListAdapter); + } else { + if (listView.getAdapter() != menuListAdapter) { + listView.setAdapter(menuListAdapter); + } else { + menuListAdapter.notifyDataSetChanged(); + } + } + } + + // 部分主题下选中项默认按下效果 + if (showSelectedBackgroundTips) { + if (listView != null) { + listView.post(new Runnable() { + @Override + public void run() { + if (menuListAdapter instanceof BottomMenuArrayAdapter && showSelectedBackgroundTips) { + BottomMenuArrayAdapter bottomMenuArrayAdapter = ((BottomMenuArrayAdapter) menuListAdapter); + + View selectItemView = listView.getChildAt(getSelection()); + if (selectItemView != null) { + selectItemView.post(new Runnable() { + @Override + public void run() { + selectItemView.setPressed(true); + } + }); + } + } + } + }); + } + + } + + if (itemDivider != null) { + listView.setDivider(itemDivider.createDividerDrawable(getOwnActivity(), isLightTheme())); + listView.setDividerHeight(itemDivider.getWidth()); + } + super.refreshUI(); + } + + public void preRefreshUI() { + if (getDialogImpl() == null) return; + runOnMain(new Runnable() { + @Override + public void run() { + refreshUI(); + } + }); + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + public List getMenuList() { + return menuList; + } + + public BottomMenu setMenuList(List menuList) { + this.menuList = menuList; + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + private boolean isSameSize(int menuListSize) { + if (this.menuList == null || this.menuList.size() == 0) { + return true; + } + return this.menuList.size() == menuListSize; + } + + public BottomMenu setMenuStringList(List menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(menuList); + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + public BottomMenu setMenuList(String[] menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + public BottomMenu setMenuList(CharSequence[] menuList) { + this.menuList = Arrays.asList(menuList); + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + public BottomMenu setMenus(CharSequence... menuList) { + this.menuList = Arrays.asList(menuList); + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + public BottomMenu setMenus(String... menuList) { + this.menuList = Arrays.asList(menuList); + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + public OnIconChangeCallBack getOnIconChangeCallBack() { + return onIconChangeCallBack; + } + + public BottomMenu setOnIconChangeCallBack(OnIconChangeCallBack onIconChangeCallBack) { + this.onIconChangeCallBack = onIconChangeCallBack; + return this; + } + + public OnBackPressedListener getOnBackPressedListener() { + return (OnBackPressedListener) onBackPressedListener; + } + + public BottomMenu setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + preRefreshUI(); + return this; + } + + public BottomMenu setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me); + return this; + } + + public BottomMenu setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public BottomMenu setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public boolean isCancelable() { + if (privateCancelable != null) { + return privateCancelable == BOOLEAN.TRUE; + } + if (overrideCancelable != null) { + return overrideCancelable == BOOLEAN.TRUE; + } + return cancelable; + } + + public BottomMenu setCancelable(boolean cancelable) { + this.privateCancelable = cancelable ? BOOLEAN.TRUE : BOOLEAN.FALSE; + preRefreshUI(); + return this; + } + + public DialogImpl getDialogImpl() { + return dialogImpl; + } + + public CharSequence getTitle() { + return title; + } + + public BottomMenu setTitle(CharSequence title) { + this.title = title; + preRefreshUI(); + return this; + } + + public BottomMenu setTitle(int titleResId) { + this.title = getString(titleResId); + preRefreshUI(); + return this; + } + + public CharSequence getMessage() { + return message; + } + + public BottomMenu setMessage(CharSequence message) { + this.message = message; + preRefreshUI(); + return this; + } + + public BottomMenu setMessage(int messageResId) { + this.message = getString(messageResId); + preRefreshUI(); + return this; + } + + public CharSequence getCancelButton() { + return cancelText; + } + + public BottomMenu setCancelButton(CharSequence cancelText) { + this.cancelText = cancelText; + preRefreshUI(); + return this; + } + + public BottomMenu setCancelButton(int cancelTextResId) { + this.cancelText = getString(cancelTextResId); + preRefreshUI(); + return this; + } + + public BottomMenu setCancelButton(OnMenuButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + public BottomMenu setCancelButton(CharSequence cancelText, OnMenuButtonClickListener cancelButtonClickListener) { + this.cancelText = cancelText; + this.cancelButtonClickListener = cancelButtonClickListener; + preRefreshUI(); + return this; + } + + public BottomMenu setCancelButton(int cancelTextResId, OnMenuButtonClickListener cancelButtonClickListener) { + this.cancelText = getString(cancelTextResId); + this.cancelButtonClickListener = cancelButtonClickListener; + preRefreshUI(); + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.BottomMenu#setCancelButton(OnMenuButtonClickListener )} + */ + @Deprecated + public BottomMenu setCancelButton(OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.BottomMenu#setCancelButton(CharSequence cancelText, OnMenuButtonClickListener )} + */ + @Deprecated + public BottomMenu setCancelButton(CharSequence cancelText, OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelText = cancelText; + this.cancelButtonClickListener = cancelButtonClickListener; + preRefreshUI(); + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.BottomMenu#setCancelButton(int cancelTextResId, OnMenuButtonClickListener )} + */ + @Deprecated + public BottomMenu setCancelButton(int cancelTextResId, OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelText = getString(cancelTextResId); + this.cancelButtonClickListener = cancelButtonClickListener; + preRefreshUI(); + return this; + } + + public BottomMenu setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + preRefreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) return null; + return onBindView.getCustomView(); + } + + public BottomMenu removeCustomView() { + this.onBindView.clean(); + preRefreshUI(); + return this; + } + + public boolean isAllowInterceptTouch() { + return super.isAllowInterceptTouch(); + } + + public BottomMenu setAllowInterceptTouch(boolean allowInterceptTouch) { + this.allowInterceptTouch = allowInterceptTouch; + preRefreshUI(); + return this; + } + + public float getBottomDialogMaxHeight() { + return bottomDialogMaxHeight; + } + + public BottomMenu setBottomDialogMaxHeight(float bottomDialogMaxHeight) { + this.bottomDialogMaxHeight = bottomDialogMaxHeight; + return this; + } + + public OnMenuItemClickListener getOnMenuItemClickListener() { + return onMenuItemClickListener; + } + + public BottomMenu setOnMenuItemClickListener(OnMenuItemClickListener onMenuItemClickListener) { + this.onMenuItemClickListener = onMenuItemClickListener; + return this; + } + + public BaseAdapter getMenuListAdapter() { + return menuListAdapter; + } + + public BottomMenu setMenuListAdapter(BaseAdapter menuListAdapter) { + this.menuListAdapter = menuListAdapter; + return this; + } + + public OnMenuButtonClickListener getBottomMenuCancelButtonClickListener() { + return (OnMenuButtonClickListener) cancelButtonClickListener; + } + + public BottomMenu setCancelButtonClickListener(OnMenuButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + public TextInfo getTitleTextInfo() { + return titleTextInfo; + } + + public BottomMenu setTitleTextInfo(TextInfo titleTextInfo) { + this.titleTextInfo = titleTextInfo; + preRefreshUI(); + return this; + } + + public TextInfo getMessageTextInfo() { + return messageTextInfo; + } + + public BottomMenu setMessageTextInfo(TextInfo messageTextInfo) { + this.messageTextInfo = messageTextInfo; + preRefreshUI(); + return this; + } + + public TextInfo getCancelTextInfo() { + return cancelTextInfo; + } + + public BottomMenu setCancelTextInfo(TextInfo cancelTextInfo) { + this.cancelTextInfo = cancelTextInfo; + preRefreshUI(); + return this; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public BottomMenu setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + preRefreshUI(); + return this; + } + + public int getSelection() { + return selectionIndex; + } + + public ArrayList getSelectionList() { + return selectionItems; + } + + public BottomMenu setSelection(int selectionIndex) { + this.selectMode = SELECT_MODE.SINGLE; + this.selectionIndex = selectionIndex; + this.selectionItems = null; + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public BottomMenu setSingleSelection() { + this.selectMode = SELECT_MODE.SINGLE; + this.selectionIndex = -1; + this.selectionItems = null; + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public BottomMenu setSelection(int[] selectionItems) { + this.selectMode = SELECT_MODE.MULTIPLE; + this.selectionIndex = -1; + this.selectionItems = new ArrayList<>(); + if (selectionItems != null) { + for (int itemIndex : selectionItems) { + this.selectionItems.add(itemIndex); + } + } + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public BottomMenu setMultiSelection() { + this.selectMode = SELECT_MODE.MULTIPLE; + this.selectionIndex = -1; + this.selectionItems = new ArrayList<>(); + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public BottomMenu setSelection(List selectionItems) { + this.selectMode = SELECT_MODE.MULTIPLE; + this.selectionIndex = -1; + this.selectionItems = new ArrayList<>(selectionItems); + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public BottomMenu setNoSelect() { + this.selectMode = SELECT_MODE.NONE; + this.selectionIndex = -1; + this.selectionItems = null; + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public BottomMenu setBackgroundColorRes(@ColorRes int backgroundRes) { + this.backgroundColor = getColor(backgroundRes); + preRefreshUI(); + return this; + } + + public CharSequence getOkButton() { + return okText; + } + + public BottomMenu setOkButton(CharSequence okText) { + this.okText = okText; + preRefreshUI(); + return this; + } + + public BottomMenu setOkButton(int OkTextResId) { + this.okText = getString(OkTextResId); + preRefreshUI(); + return this; + } + + public BottomMenu setOkButton(OnMenuButtonClickListener okButtonClickListener) { + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public BottomMenu setOkButton(CharSequence okText, OnMenuButtonClickListener okButtonClickListener) { + this.okText = okText; + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public BottomMenu setOkButton(int okTextResId, OnMenuButtonClickListener okButtonClickListener) { + this.okText = getString(okTextResId); + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public BottomMenu setHapticFeedbackEnabled(boolean isHapticFeedbackEnabled) { + this.isHapticFeedbackEnabled = isHapticFeedbackEnabled ? 1 : 0; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.BottomMenu#setOkButton(OnMenuButtonClickListener )} + */ + @Deprecated + public BottomMenu setOkButton(OnDialogButtonClickListener okButtonClickListener) { + this.okButtonClickListener = okButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.BottomMenu#setOkButton(CharSequence okText, OnMenuButtonClickListener )} + */ + @Deprecated + public BottomMenu setOkButton(CharSequence okText, OnDialogButtonClickListener okButtonClickListener) { + this.okText = okText; + this.okButtonClickListener = okButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.BottomMenu#setOkButton(int okTextResId, OnMenuButtonClickListener )} + */ + @Deprecated + public BottomMenu setOkButton(int okTextResId, OnDialogButtonClickListener okButtonClickListener) { + this.okText = getString(okTextResId); + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public CharSequence getOtherButton() { + return otherText; + } + + public BottomMenu setOtherButton(CharSequence otherText) { + this.otherText = otherText; + preRefreshUI(); + return this; + } + + public BottomMenu setOtherButton(int OtherTextResId) { + this.otherText = getString(OtherTextResId); + preRefreshUI(); + return this; + } + + public BottomMenu setOtherButton(OnMenuButtonClickListener otherButtonClickListener) { + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + public BottomMenu setOtherButton(CharSequence otherText, OnMenuButtonClickListener otherButtonClickListener) { + this.otherText = otherText; + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + public BottomMenu setOtherButton(int otherTextResId, OnMenuButtonClickListener otherButtonClickListener) { + this.otherText = getString(otherTextResId); + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.BottomMenu#setOtherButton(OnMenuButtonClickListener )} + */ + @Deprecated + public BottomMenu setOtherButton(OnDialogButtonClickListener otherButtonClickListener) { + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.BottomMenu#setOtherButton(CharSequence otherText, OnMenuButtonClickListener )} + */ + @Deprecated + public BottomMenu setOtherButton(CharSequence otherText, OnDialogButtonClickListener otherButtonClickListener) { + this.otherText = otherText; + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.BottomMenu#setOtherButton(int otherTextResId, OnMenuButtonClickListener )} + */ + @Deprecated + public BottomMenu setOtherButton(int otherTextResId, OnDialogButtonClickListener otherButtonClickListener) { + this.otherText = getString(otherTextResId); + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + public BottomMenu setMaskColor(@ColorInt int maskColor) { + this.maskColor = maskColor; + preRefreshUI(); + return this; + } + + public long getEnterAnimDuration() { + return enterAnimDuration; + } + + public BottomMenu setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public long getExitAnimDuration() { + return exitAnimDuration; + } + + public BottomMenu setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + public SELECT_MODE getSelectMode() { + return selectMode; + } + + @Override + protected void shutdown() { + dismiss(); + } + + public BottomMenu setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + refreshUI(); + return this; + } + + public BottomMenu setMaxHeight(int maxHeight) { + this.maxHeight = maxHeight; + refreshUI(); + return this; + } + + public BottomMenu setMinHeight(int minHeight) { + this.minHeight = minHeight; + refreshUI(); + return this; + } + + public BottomMenu setMinWidth(int minWidth) { + this.minWidth = minWidth; + refreshUI(); + return this; + } + + public BottomMenu setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public TextInfo getMenuTextInfo() { + if (menuTextInfo == null) return DialogX.menuTextInfo; + return menuTextInfo; + } + + public BottomMenu setMenuTextInfo(TextInfo menuTextInfo) { + this.menuTextInfo = menuTextInfo; + return this; + } + + public MenuItemTextInfoInterceptor getMenuItemTextInfoInterceptor() { + return menuItemTextInfoInterceptor; + } + + public BottomMenu setMenuItemTextInfoInterceptor(MenuItemTextInfoInterceptor menuItemTextInfoInterceptor) { + this.menuItemTextInfoInterceptor = menuItemTextInfoInterceptor; + return this; + } + + public boolean isBkgInterceptTouch() { + return bkgInterceptTouch; + } + + public BottomMenu setBkgInterceptTouch(boolean bkgInterceptTouch) { + this.bkgInterceptTouch = bkgInterceptTouch; + return this; + } + + public OnBackgroundMaskClickListener getOnBackgroundMaskClickListener() { + return onBackgroundMaskClickListener; + } + + public BottomMenu setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + return this; + } + + public BottomMenu setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public float getRadius() { + return backgroundRadius; + } + + public BottomMenu setTitleIcon(Bitmap titleIcon) { + this.titleIcon = new BitmapDrawable(getResources(), titleIcon); + refreshUI(); + return this; + } + + public BottomMenu setTitleIcon(int titleIconResId) { + this.titleIcon = getResources().getDrawable(titleIconResId); + refreshUI(); + return this; + } + + public BottomMenu setTitleIcon(Drawable titleIcon) { + this.titleIcon = titleIcon; + refreshUI(); + return this; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public BottomMenu setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public BottomMenu setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public BottomMenu setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + public boolean isShowSelectedBackgroundTips() { + return showSelectedBackgroundTips; + } + + public BottomMenu setShowSelectedBackgroundTips(boolean showSelectedBackgroundTips) { + this.showSelectedBackgroundTips = showSelectedBackgroundTips; + refreshUI(); + return this; + } + + // 返回点击的菜单索引 + public int getSelectionIndex() { + return selectionIndex; + } + + // 返回多选时,选择的菜单索引集合 + public int[] getSelectionIndexArray() { + return resultArray; + } + + // 返回多选时,选择的菜单文本集合 + public CharSequence[] getSelectTextArray() { + return selectTextArray; + } + + public MenuItemLayoutRefreshCallback getMenuMenuItemLayoutRefreshCallback() { + return menuMenuItemLayoutRefreshCallback; + } + + public BottomMenu setMenuMenuItemLayoutRefreshCallback(MenuItemLayoutRefreshCallback menuMenuItemLayoutRefreshCallback) { + this.menuMenuItemLayoutRefreshCallback = menuMenuItemLayoutRefreshCallback; + return this; + } + + public TextInfo getOkTextInfo() { + return okTextInfo; + } + + public BottomMenu setOkTextInfo(TextInfo okTextInfo) { + this.okTextInfo = okTextInfo; + return this; + } + + public TextInfo getOtherTextInfo() { + return otherTextInfo; + } + + public BottomMenu setOtherTextInfo(TextInfo otherTextInfo) { + this.otherTextInfo = otherTextInfo; + return this; + } + + public BottomMenu setScrollableWhenContentLargeThanVisibleRange(boolean scrollableWhenContentLargeThanVisibleRange) { + this.scrollableWhenContentLargeThanVisibleRange = scrollableWhenContentLargeThanVisibleRange; + return this; + } + + public BottomMenu setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public BottomMenu onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public BottomMenu onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public BottomMenu setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public List getIconResIds() { + return iconResIds; + } + + public int getIconResIds(int position) { + if (iconResIds != null && position >= 0 && position < iconResIds.size()) { + return iconResIds.get(position); + } + return 0; + } + + public BottomMenu setIconResIds(List iconResIds) { + this.iconResIds = iconResIds; + refreshUI(); + return this; + } + + public BottomMenu setIconResIds(int... resIds) { + if (iconResIds == null) { + iconResIds = new ArrayList<>(); + } + for (int id : resIds) { + iconResIds.add(id); + } + refreshUI(); + return this; + } + + public boolean isAutoTintIconInLightOrDarkMode() { + return autoTintIconInLightOrDarkMode; + } + + public BottomMenu setAutoTintIconInLightOrDarkMode(boolean autoTintIconInLightOrDarkMode) { + this.autoTintIconInLightOrDarkMode = autoTintIconInLightOrDarkMode; + refreshUI(); + return this; + } + + public BottomMenu appendMessage(CharSequence message) { + this.message = TextUtils.concat(this.message, message); + refreshUI(); + return this; + } + + public BottomMenu setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex(" + orderIndex + ") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public BottomMenu bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public BottomMenu enableMenu(int... menuIndex) { + for (int i : menuIndex) { + menuUsability.put(i, true); + } + return this; + } + + public BottomMenu enableMenu(CharSequence... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (CharSequence c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, true); + } + } else { + error("DialogX: " + dialogKey() + " .enableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public BottomMenu enableMenu(String... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (String c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, true); + } + } else { + error("DialogX: " + dialogKey() + " .enableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public BottomMenu disableMenu(int... menuIndex) { + for (int i : menuIndex) { + menuUsability.put(i, false); + } + return this; + } + + public BottomMenu disableMenu(CharSequence... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (CharSequence c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, false); + } + } else { + error("DialogX: " + dialogKey() + " .disableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public BottomMenu disableMenu(String... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (String c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, false); + } + } else { + error("DialogX: " + dialogKey() + " .disableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public boolean isMenuItemEnable(int index) { + Boolean enabled = menuUsability.get(index); + if (enabled == null) { + return true; + } + return enabled; + } + + public BottomMenu setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public BottomMenu cleanAction(int actionId){ + dialogActionRunnableMap.remove(actionId); + return this; + } + + public BottomMenu cleanAllAction(){ + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + protected void callDialogDismissPrivate(){ + dismiss(); + } + + public BottomMenu bindDismissWithLifecycleOwner(LifecycleOwner owner){ + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } + + public BottomMenu setItemDivider(ItemDivider itemDivider) { + this.itemDivider = itemDivider; + refreshUI(); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/CustomDialog.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/CustomDialog.java new file mode 100644 index 0000000..b83d0c2 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/CustomDialog.java @@ -0,0 +1,1226 @@ +package com.kongzue.dialogx.dialogs; + +import android.animation.ValueAnimator; +import android.app.Activity; +import android.graphics.Color; +import android.os.Build; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.DecelerateInterpolator; +import android.widget.RelativeLayout; + +import androidx.annotation.ColorInt; +import androidx.annotation.Nullable; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.DialogConvertViewInterface; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.util.ObjectRunnable; +import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout; +import com.kongzue.dialogx.util.views.MaxRelativeLayout; + +import java.lang.ref.WeakReference; +import java.util.HashMap; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/20 11:59 + */ +public class CustomDialog extends BaseDialog { + + public static int overrideEnterDuration = -1; + public static int overrideExitDuration = -1; + public static int overrideEnterAnimRes = 0; + public static int overrideExitAnimRes = 0; + public static BOOLEAN overrideCancelable; + protected OnBindView onBindView; + protected DialogLifecycleCallback dialogLifecycleCallback; + protected OnBackPressedListener onBackPressedListener; + protected CustomDialog me = this; + protected DialogImpl dialogImpl; + protected int enterAnimResId = R.anim.anim_dialogx_default_enter; + protected int exitAnimResId = R.anim.anim_dialogx_default_exit; + protected ALIGN align = ALIGN.CENTER; + protected int maskColor = Color.TRANSPARENT; + protected BOOLEAN privateCancelable; + protected boolean bkgInterceptTouch = true; + protected OnBackgroundMaskClickListener onBackgroundMaskClickListener; + protected DialogXAnimInterface dialogXAnimImpl; + + protected WeakReference baseViewWeakReference; + protected int alignViewGravity = -1; //指定菜单相对 baseView 的位置 + protected int width = -1; //指定菜单宽度 + protected int height = -1; //指定菜单高度 + protected int[] baseViewLoc; + protected int[] marginRelativeBaseView = new int[4]; + + public enum ALIGN { + CENTER, + TOP, + TOP_CENTER, + TOP_LEFT, + TOP_RIGHT, + BOTTOM, + BOTTOM_CENTER, + BOTTOM_LEFT, + BOTTOM_RIGHT, + LEFT, + LEFT_CENTER, + LEFT_TOP, + LEFT_BOTTOM, + RIGHT, + RIGHT_CENTER, + RIGHT_TOP, + RIGHT_BOTTOM + } + + protected CustomDialog() { + super(); + } + + public static CustomDialog build() { + return new CustomDialog(); + } + + public static CustomDialog build(OnBindView onBindView) { + return new CustomDialog().setCustomView(onBindView); + } + + public CustomDialog(OnBindView onBindView) { + this.onBindView = onBindView; + } + + public static CustomDialog show(OnBindView onBindView) { + CustomDialog customDialog = new CustomDialog(onBindView); + customDialog.show(); + return customDialog; + } + + public static CustomDialog show(OnBindView onBindView, ALIGN align) { + CustomDialog customDialog = new CustomDialog(onBindView); + customDialog.align = align; + customDialog.show(); + return customDialog; + } + + public CustomDialog show() { + if (isHide && getDialogView() != null && isShow) { + if (hideWithExitAnim && getDialogImpl() != null && getDialogImpl().boxCustom != null) { + getDialogView().setVisibility(View.VISIBLE); + getDialogImpl().getDialogXAnimImpl().doShowAnim(CustomDialog.this, getDialogImpl().boxCustom); + getDialogImpl().boxCustom.setVisibility(View.VISIBLE); + getDialogImpl().boxCustom.startAnimation(getEnterAnimation()); + } else { + getDialogView().setVisibility(View.VISIBLE); + } + return this; + } + super.beforeShow(); + if (getDialogView() == null) { + View dialogView = createView(R.layout.layout_dialogx_custom); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } else { + show(getDialogView()); + } + return this; + } + + public CustomDialog show(Activity activity) { + super.beforeShow(); + if (getDialogView() == null) { + View dialogView = createView(R.layout.layout_dialogx_custom); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(activity, dialogView); + } else { + show(activity, getDialogView()); + } + return this; + } + + private ViewTreeObserver viewTreeObserver; + private ViewTreeObserver.OnPreDrawListener baseViewDrawListener; + + public class DialogImpl implements DialogConvertViewInterface { + + public DialogXBaseRelativeLayout boxRoot; + public MaxRelativeLayout boxCustom; + + public DialogImpl(View convertView) { + if (convertView == null) return; + setDialogView(convertView); + boxRoot = convertView.findViewById(R.id.box_root); + boxCustom = convertView.findViewById(R.id.box_custom); + + init(); + dialogImpl = this; + refreshView(); + } + + @Override + public void init() { + if (baseViewLoc == null && baseView() != null) { + baseViewLoc = new int[4]; + baseView().getLocationInWindow(baseViewLoc); + baseViewLoc[2] = baseView().getWidth(); + baseViewLoc[3] = baseView().getHeight(); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(getThisOrderIndex()); + } + + boxRoot.setParentDialog(me); + boxRoot.setOnLifecycleCallBack(new DialogXBaseRelativeLayout.OnLifecycleCallBack() { + @Override + public void onShow() { + isShow = true; + preShow = false; + + setLifecycleState(Lifecycle.State.CREATED); + + getDialogLifecycleCallback().onShow(me); + CustomDialog.this.onShow(me); + onDialogShow(); + + boxCustom.setVisibility(View.GONE); + } + + @Override + public void onDismiss() { + isShow = false; + getDialogLifecycleCallback().onDismiss(me); + CustomDialog.this.onDismiss(me); + setLifecycleState(Lifecycle.State.DESTROYED); + dialogImpl = null; + dialogLifecycleCallback = null; + System.gc(); + } + }); + + boxRoot.setOnBackPressedListener(new DialogXBaseRelativeLayout.PrivateBackPressedListener() { + @Override + public boolean onBackPressed() { + if (onBackPressedListener != null) { + if (onBackPressedListener.onBackPressed(me)) { + dismiss(); + } + } else { + if (isCancelable()) { + dismiss(); + } + } + return true; + } + }); + + boxRoot.post(new Runnable() { + @Override + public void run() { + if (getDialogXAnimImpl() != null) { + getDialogXAnimImpl().doShowAnim(CustomDialog.this, boxCustom); + } + if (getDialogImpl() != null && getDialogImpl().boxCustom != null) { + getDialogImpl().boxCustom.setVisibility(View.VISIBLE); + } + + setLifecycleState(Lifecycle.State.RESUMED); + } + }); + + onDialogInit(); + } + + boolean initSetCustomViewLayoutListener = false; + ALIGN alignCache; + + @Override + public void refreshView() { + if (boxRoot == null || getOwnActivity() == null) { + return; + } + + boxCustom.setMaxWidth(getMaxWidth()); + boxCustom.setMaxHeight(getMaxHeight()); + boxCustom.setMinimumWidth(getMinWidth()); + boxCustom.setMinimumHeight(getMinHeight()); + + boxRoot.setAutoUnsafePlacePadding(isEnableImmersiveMode()); + boxRoot.setRootPadding(screenPaddings[0], screenPaddings[1], screenPaddings[2], screenPaddings[3]); + if (baseView() != null) { + if (!initSetCustomViewLayoutListener) { + if (boxCustom != null) { + RelativeLayout.LayoutParams rlp; + rlp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + boxCustom.setLayoutParams(rlp); + } + + Runnable onLayoutChangeRunnable = new Runnable() { + @Override + public void run() { + int baseViewLeft = baseViewLoc[0] - (int) boxRoot.getX(); + int baseViewTop = baseViewLoc[1] - (int) boxRoot.getY(); + int calX = 0, calY = 0; + if (alignViewGravity != -1) { + if (isAlignBaseViewGravity(Gravity.CENTER_VERTICAL)) { + calY = (baseViewTop + baseView().getMeasuredHeight() / 2 - boxCustom.getHeight() / 2); + } + if (isAlignBaseViewGravity(Gravity.CENTER_HORIZONTAL)) { + calX = (baseViewLeft + baseView().getMeasuredWidth() / 2 - boxCustom.getWidth() / 2); + } + if (isAlignBaseViewGravity(Gravity.CENTER)) { + calX = (baseViewLeft + baseView().getMeasuredWidth() / 2 - boxCustom.getWidth() / 2); + calY = (baseViewTop + baseView().getMeasuredHeight() / 2 - boxCustom.getHeight() / 2); + } + + if (isAlignBaseViewGravity(Gravity.TOP)) { + calY = baseViewTop - boxCustom.getHeight() - marginRelativeBaseView[3]; + } + if (isAlignBaseViewGravity(Gravity.LEFT)) { + calX = baseViewLeft - boxCustom.getWidth() - marginRelativeBaseView[2]; + } + if (isAlignBaseViewGravity(Gravity.RIGHT)) { + calX = baseViewLeft + baseView().getWidth() + marginRelativeBaseView[0]; + } + if (isAlignBaseViewGravity(Gravity.BOTTOM)) { + calY = baseViewTop + baseView().getHeight() + marginRelativeBaseView[1]; + } + int widthCache = width == 0 ? baseView().getWidth() : width; + int heightCache = height == 0 ? baseView().getHeight() : height; + baseViewLoc[2] = widthCache > 0 ? widthCache : baseViewLoc[2]; + baseViewLoc[3] = heightCache > 0 ? heightCache : baseViewLoc[3]; + + if (calX != 0 && calX != boxCustom.getX()) boxCustom.setX(calX); + if (calY != 0 && calY != boxCustom.getY()) boxCustom.setY(calY); + + onGetBaseViewLoc(baseViewLoc); + } + } + }; + + viewTreeObserver = boxCustom.getViewTreeObserver(); + viewTreeObserver.addOnPreDrawListener(baseViewDrawListener = new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + int[] baseViewLocCache = new int[2]; + if (baseView() != null) { + baseView().getLocationInWindow(baseViewLocCache); + if (getDialogImpl() != null && isShow && baseView().getVisibility() == View.VISIBLE) { + if (baseViewLocCache[0] != 0) { + baseViewLoc[0] = baseViewLocCache[0]; + } + if (baseViewLocCache[1] != 0) { + baseViewLoc[1] = baseViewLocCache[1]; + } + onLayoutChangeRunnable.run(); + } + } else { + removeDrawListener(viewTreeObserver, this); + viewTreeObserver = null; + baseViewDrawListener = null; + } + return true; + } + }); + initSetCustomViewLayoutListener = true; + } + } else { + if (boxCustom != null) { + RelativeLayout.LayoutParams rlp; + rlp = ((RelativeLayout.LayoutParams) boxCustom.getLayoutParams()); + if (rlp == null || (alignCache != null && alignCache != align)) { + rlp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + } + switch (align) { + case TOP_LEFT: + case LEFT_TOP: + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_TOP); + rlp.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + break; + case TOP: + case TOP_CENTER: + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_TOP); + rlp.addRule(RelativeLayout.CENTER_HORIZONTAL); + break; + case TOP_RIGHT: + case RIGHT_TOP: + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_TOP); + rlp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + break; + case BOTTOM_LEFT: + case LEFT_BOTTOM: + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + break; + case BOTTOM: + case BOTTOM_CENTER: + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + rlp.addRule(RelativeLayout.CENTER_HORIZONTAL); + break; + case BOTTOM_RIGHT: + case RIGHT_BOTTOM: + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + rlp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + break; + case CENTER: + rlp.removeRule(RelativeLayout.ALIGN_PARENT_TOP); + rlp.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + rlp.addRule(RelativeLayout.CENTER_IN_PARENT); + break; + case LEFT: + case LEFT_CENTER: + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_TOP); + rlp.addRule(RelativeLayout.CENTER_VERTICAL); + break; + case RIGHT: + case RIGHT_CENTER: + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + rlp.addRule(RelativeLayout.CENTER_VERTICAL); + break; + } + alignCache = align; + boxCustom.setLayoutParams(rlp); + } + } + + if (bkgInterceptTouch) { + if (isCancelable()) { + boxRoot.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onBackgroundMaskClickListener == null || !onBackgroundMaskClickListener.onClick(me, v)) { + doDismiss(v); + } + } + }); + } else { + boxRoot.setOnClickListener(null); + } + } else { + boxRoot.setClickable(false); + } + + if (onBindView != null && onBindView.getCustomView() != null && boxCustom != null) { + onBindView.bindParent(boxCustom, me); + } + + if (boxCustom != null) { + if (width != -1) { + boxCustom.setMaxWidth(width); + boxCustom.setMinimumWidth(width); + } + + if (height != -1) { + boxCustom.setMaxHeight(height); + boxCustom.setMinimumHeight(height); + } + } + + boxRoot.setBackgroundColor(getMaskColor()); + + onDialogRefreshUI(); + } + + @Override + public void doDismiss(View v) { + if (CustomDialog.this.preDismiss(CustomDialog.this)){ + return; + } + if (v != null) v.setEnabled(false); + if (!dismissAnimFlag && boxCustom != null) { + dismissAnimFlag = true; + boxCustom.post(new Runnable() { + @Override + public void run() { + getDialogXAnimImpl().doExitAnim(CustomDialog.this, boxCustom); + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (boxRoot != null) boxRoot.setVisibility(View.GONE); + if (baseViewDrawListener != null) { + if (viewTreeObserver != null) { + removeDrawListener(viewTreeObserver, baseViewDrawListener); + } else { + if (boxCustom != null) { + removeDrawListener(boxCustom.getViewTreeObserver(), baseViewDrawListener); + } + } + baseViewDrawListener = null; + viewTreeObserver = null; + } + dismiss(getDialogView()); + } + }, getExitAnimationDuration(null)); + } + }); + } + } + + protected DialogXAnimInterface getDialogXAnimImpl() { + if (dialogXAnimImpl == null) { + dialogXAnimImpl = new DialogXAnimInterface() { + @Override + public void doShowAnim(CustomDialog customDialog, ViewGroup dialogBodyView) { + if (getDialogImpl() == null || getDialogImpl().boxCustom == null) { + return; + } + Animation enterAnim = getEnterAnimation(); + long enterAnimationDuration = getEnterAnimationDuration(enterAnim); + enterAnim.setDuration(enterAnimationDuration); + if (boxCustom != null) { + boxCustom.setVisibility(View.VISIBLE); + boxCustom.startAnimation(enterAnim); + } + + if (maskColor != Color.TRANSPARENT) boxRoot.setBackgroundColor(maskColor); + + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f); + bkgAlpha.setDuration(enterAnimationDuration); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + }); + bkgAlpha.start(); + } + + @Override + public void doExitAnim(CustomDialog customDialog, ViewGroup dialogBodyView) { + if (getDialogImpl() == null || getDialogImpl().boxCustom == null) { + return; + } + int exitAnimResIdTemp = R.anim.anim_dialogx_default_exit; + if (overrideExitAnimRes != 0) { + exitAnimResIdTemp = overrideExitAnimRes; + } + if (exitAnimResId != 0) { + exitAnimResIdTemp = exitAnimResId; + } + + long exitAnimDurationTemp; + if (boxCustom != null) { + Animation exitAnim = AnimationUtils.loadAnimation(getOwnActivity() == null ? boxCustom.getContext() : getOwnActivity(), exitAnimResIdTemp); + exitAnimDurationTemp = getExitAnimationDuration(exitAnim); + exitAnim.setDuration(exitAnimDurationTemp); + boxCustom.startAnimation(exitAnim); + } else { + exitAnimDurationTemp = getExitAnimationDuration(null); + } + + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f); + bkgAlpha.setDuration(exitAnimDurationTemp); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + }); + bkgAlpha.start(); + } + }; + } + return dialogXAnimImpl; + } + + public long getExitAnimationDuration(@Nullable Animation defaultExitAnim) { + if (defaultExitAnim == null && boxCustom.getAnimation() != null) { + defaultExitAnim = boxCustom.getAnimation(); + } + long exitAnimDurationTemp = (defaultExitAnim == null || defaultExitAnim.getDuration() == 0) ? 300 : defaultExitAnim.getDuration(); + if (overrideExitDuration >= 0) { + exitAnimDurationTemp = overrideExitDuration; + } + if (exitAnimDuration != -1) { + exitAnimDurationTemp = exitAnimDuration; + } + return exitAnimDurationTemp; + } + + public long getEnterAnimationDuration(@Nullable Animation defaultEnterAnim) { + if (defaultEnterAnim == null && boxCustom.getAnimation() != null) { + defaultEnterAnim = boxCustom.getAnimation(); + } + long enterAnimDurationTemp = (defaultEnterAnim == null || defaultEnterAnim.getDuration() == 0) ? 300 : defaultEnterAnim.getDuration(); + if (overrideEnterDuration >= 0) { + enterAnimDurationTemp = overrideEnterDuration; + } + if (enterAnimDuration >= 0) { + enterAnimDurationTemp = enterAnimDuration; + } + return enterAnimDurationTemp; + } + } + + private void removeDrawListener(ViewTreeObserver viewTreeObserver, ViewTreeObserver.OnPreDrawListener listener) { + if (viewTreeObserver == null || listener == null || !viewTreeObserver.isAlive()) { + return; + } + try { + viewTreeObserver.removeOnPreDrawListener(listener); + } catch (Exception e) { + } + } + + private Animation getEnterAnimation() { + Animation enterAnim; + if (enterAnimResId == R.anim.anim_dialogx_default_enter && + exitAnimResId == R.anim.anim_dialogx_default_exit && + baseView() == null) { + switch (align) { + case TOP: + case TOP_CENTER: + case TOP_LEFT: + case TOP_RIGHT: + enterAnimResId = R.anim.anim_dialogx_top_enter; + exitAnimResId = R.anim.anim_dialogx_top_exit; + break; + case BOTTOM: + case BOTTOM_CENTER: + case BOTTOM_LEFT: + case BOTTOM_RIGHT: + enterAnimResId = R.anim.anim_dialogx_bottom_enter; + exitAnimResId = R.anim.anim_dialogx_bottom_exit; + break; + case LEFT: + case LEFT_CENTER: + case LEFT_TOP: + case LEFT_BOTTOM: + enterAnimResId = R.anim.anim_dialogx_left_enter; + exitAnimResId = R.anim.anim_dialogx_left_exit; + break; + case RIGHT: + case RIGHT_CENTER: + case RIGHT_TOP: + case RIGHT_BOTTOM: + enterAnimResId = R.anim.anim_dialogx_right_enter; + exitAnimResId = R.anim.anim_dialogx_right_exit; + break; + } + enterAnim = AnimationUtils.loadAnimation(getOwnActivity(), enterAnimResId); + enterAnim.setInterpolator(new DecelerateInterpolator(2f)); + } else { + int enterAnimResIdTemp = R.anim.anim_dialogx_default_enter; + if (overrideEnterAnimRes != 0) { + enterAnimResIdTemp = overrideEnterAnimRes; + } + if (enterAnimResId != 0) { + enterAnimResIdTemp = enterAnimResId; + } + enterAnim = AnimationUtils.loadAnimation(getOwnActivity(), enterAnimResIdTemp); + } + long enterAnimDurationTemp = enterAnim.getDuration(); + if (overrideEnterDuration >= 0) { + enterAnimDurationTemp = overrideEnterDuration; + } + if (enterAnimDuration >= 0) { + enterAnimDurationTemp = enterAnimDuration; + } + enterAnim.setDuration(enterAnimDurationTemp); + return enterAnim; + } + + protected void onGetBaseViewLoc(int[] baseViewLoc) { + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + public void refreshUI() { + if (getDialogImpl() == null) return; + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl != null) dialogImpl.refreshView(); + } + }); + } + + public void dismiss() { + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl == null) return; + dialogImpl.doDismiss(null); + } + }); + } + + public DialogLifecycleCallback getDialogLifecycleCallback() { + return dialogLifecycleCallback == null ? new DialogLifecycleCallback() { + } : dialogLifecycleCallback; + } + + public CustomDialog setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me); + return this; + } + + public OnBackPressedListener getOnBackPressedListener() { + return (OnBackPressedListener) onBackPressedListener; + } + + public CustomDialog setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + refreshUI(); + return this; + } + + public CustomDialog setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public CustomDialog setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public boolean isCancelable() { + if (privateCancelable != null) { + return privateCancelable == BOOLEAN.TRUE; + } + if (overrideCancelable != null) { + return overrideCancelable == BOOLEAN.TRUE; + } + return cancelable; + } + + public CustomDialog setCancelable(boolean cancelable) { + this.privateCancelable = cancelable ? BOOLEAN.TRUE : BOOLEAN.FALSE; + refreshUI(); + return this; + } + + public CustomDialog.DialogImpl getDialogImpl() { + return dialogImpl; + } + + public CustomDialog setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + refreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) return null; + return onBindView.getCustomView(); + } + + public CustomDialog removeCustomView() { + this.onBindView.clean(); + refreshUI(); + return this; + } + + public int getEnterAnimResId() { + return enterAnimResId; + } + + public CustomDialog setEnterAnimResId(int enterAnimResId) { + this.enterAnimResId = enterAnimResId; + return this; + } + + public int getExitAnimResId() { + return exitAnimResId; + } + + public CustomDialog setExitAnimResId(int exitAnimResId) { + this.exitAnimResId = exitAnimResId; + return this; + } + + public CustomDialog setAnimResId(int enterAnimResId, int exitAnimResId) { + this.enterAnimResId = enterAnimResId; + this.exitAnimResId = exitAnimResId; + return this; + } + + public ALIGN getAlign() { + return align; + } + + public CustomDialog setAlign(ALIGN align) { + this.align = align; + refreshUI(); + return this; + } + + public boolean isAutoUnsafePlacePadding() { + return isEnableImmersiveMode(); + } + + /** + * 改为使用 .setEnableImmersiveMode(boolean) 来控制是否适配沉浸式 + * + * @param autoUnsafePlacePadding 是否适配沉浸式 + * @return CustomDialog + */ + @Deprecated + public CustomDialog setAutoUnsafePlacePadding(boolean autoUnsafePlacePadding) { + setEnableImmersiveMode(autoUnsafePlacePadding); + return this; + } + + /** + * 改为使用 .setEnableImmersiveMode(boolean) 来控制是否适配沉浸式 + * + * @param fullscreen 是否适配沉浸式 + * @return CustomDialog + */ + @Deprecated + public CustomDialog setFullScreen(boolean fullscreen) { + setEnableImmersiveMode(!fullscreen); + return this; + } + + public CustomDialog setMaskColor(@ColorInt int maskColor) { + this.maskColor = maskColor; + refreshUI(); + return this; + } + + public int getMaskColor() { + return maskColor; + } + + public long getEnterAnimDuration() { + return enterAnimDuration; + } + + public CustomDialog setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public long getExitAnimDuration() { + return exitAnimDuration; + } + + public CustomDialog setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + @Override + public void restartDialog() { + if (getDialogView() != null) { + if (getDialogImpl() != null && getDialogImpl().boxCustom != null) { + if (baseViewDrawListener != null) { + if (viewTreeObserver != null) { + removeDrawListener(viewTreeObserver, baseViewDrawListener); + } else { + if (getDialogImpl().boxCustom != null) { + removeDrawListener(getDialogImpl().boxCustom.getViewTreeObserver(), baseViewDrawListener); + } + } + baseViewDrawListener = null; + viewTreeObserver = null; + } + } + dismiss(getDialogView()); + isShow = false; + } + if (getDialogImpl() != null && getDialogImpl().boxCustom != null) { + getDialogImpl().boxCustom.removeAllViews(); + } + + enterAnimDuration = 0; + View dialogView = createView(R.layout.layout_dialogx_custom); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } + + public void hide() { + isHide = true; + hideWithExitAnim = false; + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + + protected boolean hideWithExitAnim; + + public void hideWithExitAnim() { + hideWithExitAnim = true; + isHide = true; + if (getDialogImpl() != null) { + getDialogImpl().getDialogXAnimImpl().doExitAnim(CustomDialog.this, getDialogImpl().boxCustom); + + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + }, getDialogImpl().getExitAnimationDuration(null)); + } + } + + @Override + protected void shutdown() { + dismiss(); + } + + public CustomDialog setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public boolean isBkgInterceptTouch() { + return bkgInterceptTouch; + } + + public CustomDialog setBkgInterceptTouch(boolean bkgInterceptTouch) { + this.bkgInterceptTouch = bkgInterceptTouch; + refreshUI(); + return this; + } + + public int getAlignBaseViewGravity() { + return alignViewGravity; + } + + /** + * 判断是否有设置对应的位置关系 + * + * @param gravity 位置关系 + * @return 是否具备位置关系 + */ + public boolean isAlignBaseViewGravity(int gravity) { + return (alignViewGravity & gravity) == gravity; + } + + public CustomDialog setAlignBaseViewGravity(View baseView, int alignGravity) { + this.baseView(baseView); + this.alignViewGravity = alignGravity; + baseViewLoc = new int[4]; + baseView.getLocationInWindow(baseViewLoc); + setFullScreen(true); + return this; + } + + public CustomDialog setAlignBaseView(View baseView) { + this.baseView(baseView); + baseViewLoc = new int[4]; + baseView.getLocationInWindow(baseViewLoc); + setFullScreen(true); + return this; + } + + public CustomDialog setAlignBaseViewGravity(int alignGravity) { + this.alignViewGravity = alignGravity; + if (baseView() != null) { + baseViewLoc = new int[4]; + baseView().getLocationInWindow(baseViewLoc); + } + setFullScreen(true); + return this; + } + + public CustomDialog setAlignBaseViewGravity(View baseView, int alignGravity, int marginLeft, + int marginTop, int marginRight, int marginBottom) { + this.marginRelativeBaseView = new int[]{marginLeft, marginTop, marginRight, marginBottom}; + refreshUI(); + return setAlignBaseViewGravity(baseView, alignGravity); + } + + public int[] getBaseViewMargin() { + return marginRelativeBaseView; + } + + public CustomDialog setBaseViewMargin(int[] marginRelativeBaseView) { + this.marginRelativeBaseView = marginRelativeBaseView; + refreshUI(); + return this; + } + + public CustomDialog setBaseViewMargin(int marginLeft, int marginTop, + int marginRight, int marginBottom) { + this.marginRelativeBaseView = new int[]{marginLeft, marginTop, marginRight, marginBottom}; + refreshUI(); + return this; + } + + public CustomDialog setBaseViewMarginLeft(int marginLeft) { + this.marginRelativeBaseView[0] = marginLeft; + refreshUI(); + return this; + } + + public CustomDialog setBaseViewMarginTop(int marginTop) { + this.marginRelativeBaseView[1] = marginTop; + refreshUI(); + return this; + } + + public CustomDialog setBaseViewMarginRight(int marginRight) { + this.marginRelativeBaseView[2] = marginRight; + refreshUI(); + return this; + } + + public CustomDialog setBaseViewMarginBottom(int marginBottom) { + this.marginRelativeBaseView[3] = marginBottom; + refreshUI(); + return this; + } + + public int getBaseViewMarginLeft(int marginLeft) { + return this.marginRelativeBaseView[0]; + } + + public int getBaseViewMarginTop(int marginLeft) { + return this.marginRelativeBaseView[1]; + } + + public int getBaseViewMarginRight(int marginLeft) { + return this.marginRelativeBaseView[2]; + } + + public int getBaseViewMarginBottom(int marginLeft) { + return this.marginRelativeBaseView[3]; + } + + public View getBaseView() { + return baseView(); + } + + public int getWidth() { + return width; + } + + /** + * 设置对话框 UI 宽度(单位:像素) + * + * @param width 宽度(像素) + * @return CustomDialog实例 + */ + public CustomDialog setWidth(int width) { + this.width = width; + refreshUI(); + return this; + } + + public int getHeight() { + return height; + } + + /** + * 设置对话框 UI 高度(单位:像素) + * + * @param height 高度(像素) + * @return CustomDialog实例 + */ + public CustomDialog setHeight(int height) { + this.height = height; + refreshUI(); + return this; + } + + public OnBackgroundMaskClickListener getOnBackgroundMaskClickListener() { + return onBackgroundMaskClickListener; + } + + public CustomDialog setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + return this; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public CustomDialog setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public CustomDialog setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public CustomDialog setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new CustomDialog() { + * + * @param dialog self + * @Override public void onShow(CustomDialog dialog) { + * //... + * } + * } + */ + protected void onShow(CustomDialog dialog) { + + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new CustomDialog() { + * + * @param dialog self + * @Override public boolean onDismiss(CustomDialog dialog) { + * WaitDialog.show("Please Wait..."); + * if (dialog.getButtonSelectResult() == BUTTON_SELECT_RESULT.BUTTON_OK) { + * //点击了OK的情况 + * //... + * } else { + * //其他按钮点击、对话框dismiss的情况 + * //... + * } + * return false; + * } + * } + */ + //用于使用 new 构建实例时,override 的生命周期事件 + protected void onDismiss(CustomDialog dialog) { + + } + + protected CustomDialog baseView(View view) { + if (view == null && baseViewWeakReference != null) { + baseViewWeakReference.clear(); + baseViewWeakReference = null; + } else { + baseViewWeakReference = new WeakReference<>(view); + } + return this; + } + + protected View baseView() { + return baseViewWeakReference == null ? null : baseViewWeakReference.get(); + } + + public CustomDialog setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public CustomDialog onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public CustomDialog onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public CustomDialog setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public CustomDialog setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex("+orderIndex+") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public CustomDialog bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public CustomDialog setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public CustomDialog cleanAction(int actionId){ + dialogActionRunnableMap.remove(actionId); + return this; + } + + public CustomDialog cleanAllAction(){ + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss(){ + dismiss(); + } + + public CustomDialog bindDismissWithLifecycleOwner(LifecycleOwner owner){ + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } + + public CustomDialog setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + refreshUI(); + return this; + } + + public CustomDialog setMaxHeight(int maxHeight) { + this.maxHeight = maxHeight; + refreshUI(); + return this; + } + + public CustomDialog setMinHeight(int minHeight) { + this.minHeight = minHeight; + refreshUI(); + return this; + } + + public CustomDialog setMinWidth(int minWidth) { + this.minWidth = minWidth; + refreshUI(); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/FullScreenDialog.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/FullScreenDialog.java new file mode 100644 index 0000000..a0606b7 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/FullScreenDialog.java @@ -0,0 +1,1024 @@ +package com.kongzue.dialogx.dialogs; + +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; + +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.app.Activity; +import android.graphics.Outline; +import android.graphics.Rect; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.view.RoundedCorner; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.WindowInsets; +import android.view.animation.DecelerateInterpolator; +import android.widget.RelativeLayout; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.DialogConvertViewInterface; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXBaseBottomDialog; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.OnSafeInsetsChangeListener; +import com.kongzue.dialogx.interfaces.ScrollController; +import com.kongzue.dialogx.util.FullScreenDialogTouchEventInterceptor; +import com.kongzue.dialogx.util.views.ActivityScreenShotImageView; +import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout; +import com.kongzue.dialogx.util.views.MaxRelativeLayout; + +import java.lang.reflect.Method; +import java.util.HashMap; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/6 15:17 + */ +public class FullScreenDialog extends BaseDialog implements DialogXBaseBottomDialog { + + public static final int ACTIVITY_CONTENT_RADIUS_KEEP = -2; + public static final int ACTIVITY_CONTENT_RADIUS_DEFAULT = -1; + + public static int overrideEnterDuration = -1; + public static int overrideExitDuration = -1; + public static BOOLEAN overrideCancelable; + + protected OnBindView onBindView; + protected OnBackPressedListener onBackPressedListener; + protected BOOLEAN privateCancelable; + protected boolean hideZoomBackground; + protected float backgroundRadius = DialogX.defaultFullScreenDialogBackgroundRadius; + protected float activityContentRadius = ACTIVITY_CONTENT_RADIUS_DEFAULT; + protected boolean allowInterceptTouch = true; + protected DialogXAnimInterface dialogXAnimImpl; + protected boolean bottomNonSafetyAreaBySelf = false; + protected boolean hideActivityContentView; + protected Integer maskColor = null; + + protected DialogLifecycleCallback dialogLifecycleCallback; + protected OnBackgroundMaskClickListener onBackgroundMaskClickListener; + + protected FullScreenDialog me = this; + + protected FullScreenDialog() { + super(); + } + + public static FullScreenDialog build() { + return new FullScreenDialog(); + } + + public static FullScreenDialog build(OnBindView onBindView) { + return new FullScreenDialog(onBindView); + } + + public FullScreenDialog(OnBindView onBindView) { + this.onBindView = onBindView; + } + + public static FullScreenDialog show(OnBindView onBindView) { + FullScreenDialog FullScreenDialog = new FullScreenDialog(onBindView); + FullScreenDialog.show(); + return FullScreenDialog; + } + + public FullScreenDialog show() { + if (isHide && getDialogView() != null && isShow) { + if (hideWithExitAnim && getDialogImpl() != null) { + getDialogView().setVisibility(View.VISIBLE); + getDialogImpl().getDialogXAnimImpl().doShowAnim(me, getDialogImpl().bkg); + } else { + getDialogView().setVisibility(View.VISIBLE); + } + return this; + } + super.beforeShow(); + if (getDialogView() == null) { + View dialogView = createView(isLightTheme() ? R.layout.layout_dialogx_fullscreen : R.layout.layout_dialogx_fullscreen_dark); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } else { + show(getDialogView()); + } + return this; + } + + public void show(Activity activity) { + super.beforeShow(); + if (getDialogView() == null) { + View dialogView = createView(isLightTheme() ? R.layout.layout_dialogx_fullscreen : R.layout.layout_dialogx_fullscreen_dark); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(activity, dialogView); + } else { + show(activity, getDialogView()); + } + } + + protected DialogImpl dialogImpl; + + public class DialogImpl implements DialogConvertViewInterface { + + private FullScreenDialogTouchEventInterceptor fullScreenDialogTouchEventInterceptor; + + public ActivityScreenShotImageView imgZoomActivity; + public DialogXBaseRelativeLayout boxRoot; + public RelativeLayout boxBkg; + public MaxRelativeLayout bkg; + public RelativeLayout boxCustom; + public ScrollController scrollView; + + public DialogImpl setScrollView(ScrollController scrollView) { + this.scrollView = scrollView; + return this; + } + + public DialogImpl(View convertView) { + if (convertView == null) return; + setDialogView(convertView); + imgZoomActivity = convertView.findViewById(R.id.img_zoom_activity); + boxRoot = convertView.findViewById(R.id.box_root); + boxBkg = convertView.findViewById(R.id.box_bkg); + bkg = convertView.findViewById(R.id.bkg); + boxCustom = convertView.findViewById(R.id.box_custom); + imgZoomActivity.hideActivityContentView = hideActivityContentView; + + imgZoomActivity.bindDialog(FullScreenDialog.this); + + if (hideZoomBackground) { + convertView.setBackgroundResource(R.color.black20); + imgZoomActivity.setVisibility(View.GONE); + } else { + convertView.setBackgroundResource(R.color.black); + imgZoomActivity.setVisibility(View.VISIBLE); + } + init(); + dialogImpl = this; + refreshView(); + } + + public float bkgEnterAimY = -1; + protected int enterY; + + private Rect mUnsafeRect = new Rect(0, 0, 0, 0); + + public float getEnterY() { + return Math.max(0, boxRoot.getSafeHeight() - enterY); + } + + @Override + public void init() { + boxRoot.setParentDialog(me); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(getThisOrderIndex()); + } + + boxRoot.setOnLifecycleCallBack(new DialogXBaseRelativeLayout.OnLifecycleCallBack() { + @Override + public void onShow() { + isShow = true; + preShow = false; + + setLifecycleState(Lifecycle.State.CREATED); + onDialogShow(); + + getDialogLifecycleCallback().onShow(me); + FullScreenDialog.this.onShow(me); + } + + @Override + public void onDismiss() { + isShow = false; + getDialogLifecycleCallback().onDismiss(me); + FullScreenDialog.this.onDismiss(me); + setLifecycleState(Lifecycle.State.DESTROYED); + fullScreenDialogTouchEventInterceptor = null; + dialogImpl = null; + dialogLifecycleCallback = null; + System.gc(); + } + }); + + boxRoot.setOnBackPressedListener(new DialogXBaseRelativeLayout.PrivateBackPressedListener() { + @Override + public boolean onBackPressed() { + if (onBackPressedListener != null) { + if (onBackPressedListener.onBackPressed(me)) { + dismiss(); + } + } else { + if (isCancelable()) { + dismiss(); + } + } + return true; + } + }); + + fullScreenDialogTouchEventInterceptor = new FullScreenDialogTouchEventInterceptor(me, dialogImpl); + boxRoot.setBkgAlpha(0f); + + boxRoot.post(new Runnable() { + @Override + public void run() { + bkg.setY(boxRoot.getHeight()); + getDialogXAnimImpl().doShowAnim(me, bkg); + setLifecycleState(Lifecycle.State.RESUMED); + } + }); + boxRoot.setOnSafeInsetsChangeListener(new OnSafeInsetsChangeListener() { + @Override + public void onChange(Rect unsafeRect) { + mUnsafeRect.set(unsafeRect); + makeEnterY(); + if (!enterAnimRunning && getEnterY() != 0) { + bkg.setY(getEnterY()); + } + } + }); + + bkg.setOnYChanged(new MaxRelativeLayout.OnYChanged() { + @Override + public void y(float y) { + float realY = y + bkg.getTop(); + float zoomScale = 1 - (boxRoot.getHeight() - realY) * 0.00002f; + if (zoomScale > 1) zoomScale = 1; + if (!hideZoomBackground) { + imgZoomActivity.setScale(zoomScale); + imgZoomActivity.setRadius( + getActivityZoomRadius(getDeviceRadius(), getActivityContentRadius(), ((boxRoot.getHeight() - realY) / boxRoot.getHeight())) + ); + } + } + }); + + /** + * 给自定义布局增加监听,如果布局高度发生改变,则重新计算位置,位置发生变化,则再次使用动画移动布局到指定位置 + * 目的是给自定义布局高度为wrap_content的用于纠正布局的Y轴位置 + */ + boxCustom.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { + int oldHeight = oldBottom - oldTop; + int newHeight = bottom - top; + if (oldHeight != newHeight) { + /** + * 高度发生改变 + * 这里判断是否在两种动画途中 + */ + if (!enterAnimRunning && !boxRoot.getFitSystemBarUtils().isInSmoothingPadding()) { + makeEnterY(); + float newBkgEnterAimY = boxRoot.getSafeHeight() - mUnsafeRect.bottom - enterY - boxRoot.getUnsafePlace().top; + if (newBkgEnterAimY < 0) newBkgEnterAimY = 0; + if (newBkgEnterAimY != bkgEnterAimY && bkg.getY() != newBkgEnterAimY) { + float oldVal = bkgEnterAimY; + bkgEnterAimY = newBkgEnterAimY; + //需要重新定义终点 + doShowAnimRepeat((int) oldVal, (int) newBkgEnterAimY, true); + } else if (bkg.getY() != newBkgEnterAimY && newBkgEnterAimY != 0) { + bkg.setY(newBkgEnterAimY); + } + } + } + } + }); + onDialogInit(); + } + + private boolean isMatchParentHeightCustomView() { + if (onBindView != null && onBindView.getCustomView() != null) { + ViewGroup.LayoutParams lp = onBindView.getCustomView().getLayoutParams(); + if (lp != null) { + return lp.height == MATCH_PARENT; + } + } + return false; + } + + private void makeEnterY() { + int customViewHeight = boxCustom.getHeight(); + + if (customViewHeight == 0 || isMatchParentHeightCustomView()) { + customViewHeight = ((int) boxRoot.getSafeHeight()); + } + enterY = customViewHeight; + } + + @Override + public void refreshView() { + if (boxRoot == null || getOwnActivity() == null) { + return; + } + boxRoot.setAutoUnsafePlacePadding(isEnableImmersiveMode()); + boxRoot.setRootPadding(screenPaddings[0], screenPaddings[1], screenPaddings[2], screenPaddings[3]); + if (backgroundColor != null) { + tintColor(bkg, backgroundColor); + } + + bkg.setMaxWidth(getMaxWidth()); + bkg.setMaxHeight(getMaxHeight()); + bkg.setMinimumWidth(getMinWidth()); + bkg.setMinimumHeight(getMinHeight()); + + if (isCancelable()) { + boxRoot.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onBackgroundMaskClickListener == null || !onBackgroundMaskClickListener.onClick(me, v)) { + doDismiss(v); + } + } + }); + } else { + boxRoot.setOnClickListener(null); + } + if (backgroundRadius > -1) { + if (bkg.getBackground() instanceof GradientDrawable) { + GradientDrawable gradientDrawable = (GradientDrawable) bkg.getBackground(); + if (gradientDrawable != null) gradientDrawable.setCornerRadii(new float[]{ + backgroundRadius, backgroundRadius, backgroundRadius, backgroundRadius, 0, 0, 0, 0 + }); + } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + bkg.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getWidth(), (int) (view.getHeight() + backgroundRadius), backgroundRadius); + } + }); + bkg.setClipToOutline(true); + } + } + if (maskColor != null) { + boxRoot.setBackgroundColor(maskColor); + } + + if (onBindView != null) { + onBindView.bindParent(boxCustom, me); + if (onBindView.getCustomView() instanceof ScrollController) { + scrollView = (ScrollController) onBindView.getCustomView(); + } else { + View scrollController = onBindView.getCustomView().findViewWithTag("ScrollController"); + if (scrollController instanceof ScrollController) { + scrollView = (ScrollController) scrollController; + } + } + } + + if (hideZoomBackground) { + getDialogView().setBackgroundResource(R.color.black20); + imgZoomActivity.setVisibility(View.GONE); + } else { + getDialogView().setBackgroundResource(R.color.black); + imgZoomActivity.setVisibility(View.VISIBLE); + } + + fullScreenDialogTouchEventInterceptor.refresh(me, this); + + onDialogRefreshUI(); + } + + @Override + public void doDismiss(View v) { + if (FullScreenDialog.this.preDismiss(FullScreenDialog.this)) { + return; + } + if (v != null) v.setEnabled(false); + if (getOwnActivity() == null) return; + + if (!dismissAnimFlag && getDialogXAnimImpl() != null) { + dismissAnimFlag = true; + getDialogXAnimImpl().doExitAnim(me, bkg); + + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (boxRoot != null) { + boxRoot.setVisibility(View.GONE); + } + dismiss(getDialogView()); + } + }, getExitAnimationDuration()); + } + } + + public void preDismiss() { + if (isCancelable()) { + doDismiss(boxRoot); + } else { + long exitAnimDurationTemp = 300; + if (overrideExitDuration >= 0) { + exitAnimDurationTemp = overrideExitDuration; + } + if (exitAnimDuration >= 0) { + exitAnimDurationTemp = exitAnimDuration; + } + + ObjectAnimator exitAnim = ObjectAnimator.ofFloat(bkg, "y", bkg.getY(), bkgEnterAimY); + exitAnim.setDuration(exitAnimDurationTemp); + exitAnim.start(); + } + } + + private boolean enterAnimRunning = true; + + /** + * 弹窗显示的动画 + * 动画执行途中实时检测终点是否改变,如果改变则中断这次动画重新设置新终点的动画并执行 + * + * @param start 起点位置 + * @param end 终点位置 + */ + private void doShowAnimRepeat(int start, int end, boolean isRepeat) { + enterAnimRunning = true; + long enterAnimDurationTemp = getEnterAnimationDuration(); + + ValueAnimator enterAnimVal = ValueAnimator.ofInt(start, end); + enterAnimVal.setDuration(enterAnimDurationTemp); + enterAnimVal.setInterpolator(new DecelerateInterpolator()); + enterAnimVal.addUpdateListener(animation -> { + int thisVal = (int) animation.getAnimatedValue(); + bkg.setY(thisVal); + + makeEnterY(); + float newBkgEnterAimY = boxRoot.getSafeHeight() - enterY; + if (newBkgEnterAimY < 0) newBkgEnterAimY = 0; + if (newBkgEnterAimY != bkgEnterAimY) { + bkgEnterAimY = newBkgEnterAimY; + //需要重新定义终点 + animation.cancel(); + doShowAnimRepeat(thisVal, (int) newBkgEnterAimY, true); + } else if (thisVal >= end) { + enterAnimRunning = false; + } + }); + enterAnimVal.start(); + bkg.setVisibility(View.VISIBLE); + + if (!isRepeat) { + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f); + bkgAlpha.setDuration(enterAnimDurationTemp); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float value = (float) animation.getAnimatedValue(); + boxRoot.setBkgAlpha(value); + } + }); + bkgAlpha.start(); + } + } + + protected DialogXAnimInterface getDialogXAnimImpl() { + if (dialogXAnimImpl == null) { + dialogXAnimImpl = new DialogXAnimInterface() { + @Override + public void doShowAnim(FullScreenDialog dialog, ViewGroup dialogBodyView) { +// long enterAnimDurationTemp = getEnterAnimationDuration(); + makeEnterY(); + bkgEnterAimY = boxRoot.getSafeHeight() - enterY; + if (bkgEnterAimY < 0) bkgEnterAimY = 0; + //启动带监控终点位置变化的动画 + doShowAnimRepeat(boxRoot.getHeight(), (int) bkgEnterAimY, false); +// ObjectAnimator enterAnim = ObjectAnimator.ofFloat(bkg, "y", boxRoot.getHeight(), bkgEnterAimY); +// enterAnim.setDuration(enterAnimDurationTemp); +// enterAnim.setInterpolator(new DecelerateInterpolator()); +// enterAnim.start(); +// bkg.setVisibility(View.VISIBLE); +// +// ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f); +// bkgAlpha.setDuration(enterAnimDurationTemp); +// bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { +// @Override +// public void onAnimationUpdate(ValueAnimator animation) { +// float value = (float) animation.getAnimatedValue(); +// boxRoot.setBkgAlpha(value); +// enterAnimRunning = !(value == 1f); +// } +// }); +// bkgAlpha.start(); + } + + @Override + public void doExitAnim(FullScreenDialog dialog, ViewGroup dialogBodyView) { + long exitAnimDurationTemp = getExitAnimationDuration(); + + ObjectAnimator exitAnim = ObjectAnimator.ofFloat(bkg, "y", bkg.getY(), boxBkg.getHeight()); + exitAnim.setDuration(exitAnimDurationTemp); + exitAnim.start(); + + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f); + bkgAlpha.setDuration(exitAnimDurationTemp); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float value = (float) animation.getAnimatedValue(); + boxRoot.setBkgAlpha(value); + enterAnimRunning = !(value == 1f); + } + }); + bkgAlpha.start(); + } + }; + } + return dialogXAnimImpl; + } + + public long getExitAnimationDuration() { + long exitAnimDurationTemp = 300; + if (overrideExitDuration >= 0) { + exitAnimDurationTemp = overrideExitDuration; + } + if (exitAnimDuration != -1) { + exitAnimDurationTemp = exitAnimDuration; + } + return exitAnimDurationTemp; + } + + public long getEnterAnimationDuration() { + long enterAnimDurationTemp = 300; + if (overrideEnterDuration >= 0) { + enterAnimDurationTemp = overrideEnterDuration; + } + if (enterAnimDuration >= 0) { + enterAnimDurationTemp = enterAnimDuration; + } + return enterAnimDurationTemp; + } + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + public void refreshUI() { + if (getDialogImpl() == null) return; + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl != null) dialogImpl.refreshView(); + } + }); + } + + public void dismiss() { + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl == null) return; + dialogImpl.doDismiss(null); + } + }); + } + + public DialogLifecycleCallback getDialogLifecycleCallback() { + return dialogLifecycleCallback == null ? new DialogLifecycleCallback() { + } : dialogLifecycleCallback; + } + + public FullScreenDialog setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me); + return this; + } + + public OnBackPressedListener getOnBackPressedListener() { + return (OnBackPressedListener) onBackPressedListener; + } + + public FullScreenDialog setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + refreshUI(); + return this; + } + + public FullScreenDialog setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public FullScreenDialog setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public boolean isCancelable() { + if (privateCancelable != null) { + return privateCancelable == BOOLEAN.TRUE; + } + if (overrideCancelable != null) { + return overrideCancelable == BOOLEAN.TRUE; + } + return cancelable; + } + + public FullScreenDialog setCancelable(boolean cancelable) { + this.privateCancelable = cancelable ? BOOLEAN.TRUE : BOOLEAN.FALSE; + refreshUI(); + return this; + } + + public DialogImpl getDialogImpl() { + return dialogImpl; + } + + public FullScreenDialog setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + refreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) return null; + return onBindView.getCustomView(); + } + + public FullScreenDialog removeCustomView() { + this.onBindView.clean(); + refreshUI(); + return this; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public FullScreenDialog setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + refreshUI(); + return this; + } + + public FullScreenDialog setBackgroundColorRes(@ColorRes int backgroundColorRes) { + this.backgroundColor = getColor(backgroundColorRes); + refreshUI(); + return this; + } + + public long getEnterAnimDuration() { + return enterAnimDuration; + } + + public FullScreenDialog setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public long getExitAnimDuration() { + return exitAnimDuration; + } + + public FullScreenDialog setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + public boolean isHideZoomBackground() { + return hideZoomBackground; + } + + public FullScreenDialog setHideZoomBackground(boolean hideZoomBackground) { + this.hideZoomBackground = hideZoomBackground; + refreshUI(); + return this; + } + + @Override + public void restartDialog() { + if (getDialogView() != null) { + dismiss(getDialogView()); + isShow = false; + } + if (getDialogImpl().boxCustom != null) { + getDialogImpl().boxCustom.removeAllViews(); + } + enterAnimDuration = 0; + View dialogView = createView(isLightTheme() ? R.layout.layout_dialogx_fullscreen : R.layout.layout_dialogx_fullscreen_dark); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } + + public void hide() { + isHide = true; + hideWithExitAnim = false; + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + + protected boolean hideWithExitAnim; + + public void hideWithExitAnim() { + hideWithExitAnim = true; + isHide = true; + if (getDialogImpl() != null) { + getDialogImpl().getDialogXAnimImpl().doExitAnim(me, getDialogImpl().bkg); + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + }, getDialogImpl().getExitAnimationDuration()); + } + } + + @Override + protected void shutdown() { + dismiss(); + } + + public FullScreenDialog setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + refreshUI(); + return this; + } + + public FullScreenDialog setMaxHeight(int maxHeight) { + this.maxHeight = maxHeight; + refreshUI(); + return this; + } + + public FullScreenDialog setMinHeight(int minHeight) { + this.minHeight = minHeight; + refreshUI(); + return this; + } + + public FullScreenDialog setMinWidth(int minWidth) { + this.minWidth = minWidth; + refreshUI(); + return this; + } + + public FullScreenDialog setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public OnBackgroundMaskClickListener getOnBackgroundMaskClickListener() { + return onBackgroundMaskClickListener; + } + + public FullScreenDialog setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + return this; + } + + public FullScreenDialog setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public float getRadius() { + return backgroundRadius; + } + + public boolean isAllowInterceptTouch() { + return allowInterceptTouch; + } + + public FullScreenDialog setAllowInterceptTouch(boolean allowInterceptTouch) { + this.allowInterceptTouch = allowInterceptTouch; + refreshUI(); + return this; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public FullScreenDialog setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public FullScreenDialog setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public FullScreenDialog setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new FullScreenDialog() { + * + * @param dialog self + * @Override public void onShow(FullScreenDialog dialog) { + * //... + * } + * } + */ + protected void onShow(FullScreenDialog dialog) { + + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new FullScreenDialog() { + * + * @param dialog self + * @Override public boolean onDismiss(FullScreenDialog dialog) { + * WaitDialog.show("Please Wait..."); + * if (dialog.getButtonSelectResult() == BUTTON_SELECT_RESULT.BUTTON_OK) { + * //点击了OK的情况 + * //... + * } else { + * //其他按钮点击、对话框dismiss的情况 + * //... + * } + * return false; + * } + * } + */ + //用于使用 new 构建实例时,override 的生命周期事件 + protected void onDismiss(FullScreenDialog dialog) { + + } + + public boolean isBottomNonSafetyAreaBySelf() { + return bottomNonSafetyAreaBySelf; + } + + public FullScreenDialog setBottomNonSafetyAreaBySelf(boolean bottomNonSafetyAreaBySelf) { + this.bottomNonSafetyAreaBySelf = bottomNonSafetyAreaBySelf; + return this; + } + + /** + * 是否在显示 FullScreenDialog 时不对 activity 的界面内容进行渲染,这将提升一定的性能 + * 只可以在使用 build 方法构建且在执行show方法之前使用 + * 但这将引发一些问题,例如输入法弹出时 FullScreenDialog 无法上浮等 + * + * @param hideActivityContentView 是否显示 activity 的界面内容 + * @return this + */ + public FullScreenDialog hideActivityContentView(boolean hideActivityContentView) { + this.hideActivityContentView = hideActivityContentView; + return this; + } + + public FullScreenDialog setMaskColor(@ColorInt int maskColor) { + this.maskColor = maskColor; + refreshUI(); + return this; + } + + public float getActivityContentRadius() { + return activityContentRadius >= 0 ? activityContentRadius : activityContentRadius == ACTIVITY_CONTENT_RADIUS_KEEP ? getDeviceRadius() : (getRadius() >= 0 ? getRadius() : dip2px(15)); + } + + private Integer deviceRadiusCache; + + public int getDeviceRadius() { + if (deviceRadiusCache == null) { + deviceRadiusCache = 0; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { + WindowInsets rootInsets = getRootFrameLayout() == null ? publicWindowInsets() : getRootFrameLayout().getRootWindowInsets(); + if (rootInsets != null) { + RoundedCorner lT = rootInsets.getRoundedCorner(RoundedCorner.POSITION_TOP_LEFT); + RoundedCorner rT = rootInsets.getRoundedCorner(RoundedCorner.POSITION_TOP_RIGHT); + if (lT != null && rT != null) { + deviceRadiusCache = Math.max(lT.getRadius(), rT.getRadius()); + } + } + } + if (deviceRadiusCache == 0) { + String manufacturer = Build.MANUFACTURER.toLowerCase(); + if ("xiaomi".equals(manufacturer)) { + try { + Class systemPropertiesClass = Class.forName("android.os.SystemProperties"); + Method getIntMethod = systemPropertiesClass.getMethod("getInt", String.class, int.class); + deviceRadiusCache = (int) getIntMethod.invoke(null, "ro.miui.notch.radius", 0); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + if (deviceRadiusCache == 0) { + try { + int resourceId = me.getResources().getIdentifier("rounded_corner_radius", "dimen", "android"); + if (resourceId > 0) { + deviceRadiusCache = me.getResources().getDimensionPixelSize(resourceId); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + return deviceRadiusCache; + } + + public FullScreenDialog setDeviceRadius(int deviceRadiusPx) { + deviceRadiusCache = deviceRadiusPx; + return this; + } + + public FullScreenDialog setActivityContentRadius(float activityContentRadius) { + this.activityContentRadius = activityContentRadius; + return this; + } + + private float getActivityZoomRadius(float startValue, float endValue, float progressValue) { + return startValue + progressValue * (endValue - startValue); + } + + public FullScreenDialog setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public FullScreenDialog onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public FullScreenDialog onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public FullScreenDialog setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public FullScreenDialog setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex(" + orderIndex + ") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public FullScreenDialog bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public FullScreenDialog setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public FullScreenDialog cleanAction(int actionId) { + dialogActionRunnableMap.remove(actionId); + return this; + } + + public FullScreenDialog cleanAllAction() { + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss() { + dismiss(); + } + + public FullScreenDialog bindDismissWithLifecycleOwner(LifecycleOwner owner) { + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/GuideDialog.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/GuideDialog.java new file mode 100644 index 0000000..5928d9c --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/GuideDialog.java @@ -0,0 +1,852 @@ +package com.kongzue.dialogx.dialogs; + +import android.app.Activity; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.RelativeLayout; + +import androidx.annotation.ColorInt; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.OnDialogButtonClickListener; +import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout; + +import java.util.Arrays; +import java.util.HashMap; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2022/8/19 16:35 + */ +public class GuideDialog extends CustomDialog { + + public enum STAGE_LIGHT_TYPE { + RECTANGLE, //矩形 + SQUARE_OUTSIDE, //方形(外围) + SQUARE_INSIDE, //方形(内围) + CIRCLE_OUTSIDE, //圆形(外围) + CIRCLE_INSIDE, //圆形(内围) + } + + protected STAGE_LIGHT_TYPE stageLightType = STAGE_LIGHT_TYPE.CIRCLE_OUTSIDE; + protected Drawable tipImage; + protected float stageLightFilletRadius; //舞台灯光部分的圆角 + protected Integer maskColor = null; + protected OnDialogButtonClickListener onStageLightPathClickListener; + protected int[] baseViewLocationCoordinateCompensation = new int[4]; + + protected GuideDialog() { + super(); + enterAnimResId = R.anim.anim_dialogx_alpha_enter; + exitAnimResId = R.anim.anim_dialogx_default_exit; + this.alignViewGravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; + } + + public static GuideDialog build() { + return new GuideDialog(); + } + + public GuideDialog(View baseView, STAGE_LIGHT_TYPE stageLightType) { + this(); + this.baseView(baseView); + this.stageLightType = stageLightType; + } + + public GuideDialog(View baseView, STAGE_LIGHT_TYPE stageLightType, OnBindView onBindView, int alignBaseViewGravity) { + this(); + this.baseView(baseView); + this.stageLightType = stageLightType; + this.onBindView = onBindView; + this.alignViewGravity = alignBaseViewGravity; + } + + public GuideDialog(View baseView, STAGE_LIGHT_TYPE stageLightType, int tipImageResId, int alignBaseViewGravity) { + this(); + this.baseView(baseView); + this.tipImage = getResources().getDrawable(tipImageResId); + this.stageLightType = stageLightType; + this.alignViewGravity = alignBaseViewGravity; + } + + public GuideDialog(View baseView, STAGE_LIGHT_TYPE stageLightType, Bitmap tipImage, int alignBaseViewGravity) { + this(); + this.baseView(baseView); + this.tipImage = new BitmapDrawable(getResources(), tipImage); + this.stageLightType = stageLightType; + this.alignViewGravity = alignBaseViewGravity; + } + + public GuideDialog(View baseView, STAGE_LIGHT_TYPE stageLightType, Drawable tipImage, int alignBaseViewGravity) { + this(); + this.baseView(baseView); + this.tipImage = tipImage; + this.stageLightType = stageLightType; + this.alignViewGravity = alignBaseViewGravity; + } + + public GuideDialog(int tipImageResId) { + this(); + this.tipImage = getResources().getDrawable(tipImageResId); + } + + public GuideDialog(Bitmap tipImage) { + this(); + this.tipImage = new BitmapDrawable(getResources(), tipImage); + } + + public GuideDialog(Drawable tipImage) { + this(); + this.tipImage = tipImage; + } + + public GuideDialog(int tipImageResId, ALIGN align) { + this(); + this.tipImage = getResources().getDrawable(tipImageResId); + this.align = align; + } + + public GuideDialog(Bitmap tipImage, ALIGN align) { + this(); + this.tipImage = new BitmapDrawable(getResources(), tipImage); + this.align = align; + } + + public GuideDialog(Drawable tipImage, ALIGN align) { + this(); + this.tipImage = tipImage; + this.align = align; + } + + public GuideDialog(OnBindView onBindView) { + this(); + this.onBindView = onBindView; + } + + public GuideDialog(OnBindView onBindView, ALIGN align) { + this(); + this.onBindView = onBindView; + this.align = align; + } + + public GuideDialog(View baseView, int tipImageResId) { + this(); + this.baseView(baseView); + this.tipImage = getResources().getDrawable(tipImageResId); + } + + public GuideDialog(View baseView, Bitmap tipImage) { + this(); + this.baseView(baseView); + this.tipImage = new BitmapDrawable(getResources(), tipImage); + } + + public GuideDialog(View baseView, Drawable tipImage) { + this(); + this.baseView(baseView); + this.tipImage = tipImage; + } + + public GuideDialog(View baseView, STAGE_LIGHT_TYPE stageLightType, int tipImageResId) { + this(); + this.baseView(baseView); + this.stageLightType = stageLightType; + this.tipImage = getResources().getDrawable(tipImageResId); + } + + public GuideDialog(View baseView, STAGE_LIGHT_TYPE stageLightType, Bitmap tipImage) { + this(); + this.baseView(baseView); + this.stageLightType = stageLightType; + this.tipImage = new BitmapDrawable(getResources(), tipImage); + } + + public GuideDialog(View baseView, STAGE_LIGHT_TYPE stageLightType, Drawable tipImage) { + this(); + this.baseView(baseView); + this.stageLightType = stageLightType; + this.tipImage = tipImage; + } + + public GuideDialog(View baseView, int tipImageResId, int alignBaseViewGravity) { + this(); + this.baseView(baseView); + this.alignViewGravity = alignBaseViewGravity; + this.tipImage = getResources().getDrawable(tipImageResId); + } + + public GuideDialog(View baseView, Bitmap tipImage, int alignBaseViewGravity) { + this(); + this.baseView(baseView); + this.alignViewGravity = alignBaseViewGravity; + this.tipImage = new BitmapDrawable(getResources(), tipImage); + } + + public GuideDialog(View baseView, Drawable tipImage, int alignBaseViewGravity) { + this(); + this.baseView(baseView); + this.alignViewGravity = alignBaseViewGravity; + this.tipImage = tipImage; + } + + //静态方法 + public static GuideDialog show(OnBindView onBindView) { + GuideDialog guideDialog = new GuideDialog(onBindView); + guideDialog.show(); + return guideDialog; + } + + public static GuideDialog show(OnBindView onBindView, ALIGN align) { + GuideDialog guideDialog = new GuideDialog(onBindView); + guideDialog.align = align; + guideDialog.show(); + return guideDialog; + } + + public static GuideDialog show(int tipImageResId) { + return new GuideDialog(tipImageResId).show(); + } + + public static GuideDialog show(Bitmap tipImage) { + return new GuideDialog(tipImage).show(); + } + + public static GuideDialog show(Drawable tipImage) { + return new GuideDialog(tipImage).show(); + } + + public static GuideDialog show(int tipImageResId, ALIGN align) { + GuideDialog guideDialog = new GuideDialog(tipImageResId, align); + guideDialog.align = align; + return guideDialog.show(); + } + + public static GuideDialog show(Bitmap tipImage, ALIGN align) { + GuideDialog guideDialog = new GuideDialog(tipImage, align); + guideDialog.align = align; + return guideDialog.show(); + } + + public static GuideDialog show(Drawable tipImage, ALIGN align) { + return new GuideDialog(tipImage, align).show(); + } + + public static GuideDialog show(View baseView, STAGE_LIGHT_TYPE stageLightType) { + return new GuideDialog(baseView, stageLightType).show(); + } + + public static GuideDialog show(View baseView, STAGE_LIGHT_TYPE stageLightType, OnBindView onBindView, int alignBaseViewGravity) { + return new GuideDialog(baseView, stageLightType, onBindView, alignBaseViewGravity).show(); + } + + public static GuideDialog show(View baseView, STAGE_LIGHT_TYPE stageLightType, int tipImageResId, int alignBaseViewGravity) { + return new GuideDialog(baseView, stageLightType, tipImageResId, alignBaseViewGravity).show(); + } + + public static GuideDialog show(View baseView, STAGE_LIGHT_TYPE stageLightType, Bitmap tipImage, int alignBaseViewGravity) { + return new GuideDialog(baseView, stageLightType, tipImage, alignBaseViewGravity).show(); + } + + public static GuideDialog show(View baseView, STAGE_LIGHT_TYPE stageLightType, Drawable tipImage, int alignBaseViewGravity) { + return new GuideDialog(baseView, stageLightType, tipImage, alignBaseViewGravity).show(); + } + + public static GuideDialog show(View baseView, int tipImageResId) { + return new GuideDialog(baseView, tipImageResId).show(); + } + + public static GuideDialog show(View baseView, Bitmap tipImage) { + return new GuideDialog(baseView, tipImage).show(); + } + + public static GuideDialog show(View baseView, Drawable tipImage) { + return new GuideDialog(baseView, tipImage).show(); + } + + public static GuideDialog show(View baseView, STAGE_LIGHT_TYPE stageLightType, int tipImageResId) { + return new GuideDialog(baseView, stageLightType, tipImageResId).show(); + } + + public static GuideDialog show(View baseView, STAGE_LIGHT_TYPE stageLightType, Bitmap tipImage) { + return new GuideDialog(baseView, stageLightType, tipImage).show(); + } + + public static GuideDialog show(View baseView, STAGE_LIGHT_TYPE stageLightType, Drawable tipImage) { + return new GuideDialog(baseView, stageLightType, tipImage).show(); + } + + public static GuideDialog show(View baseView, int tipImageResId, int alignBaseViewGravity) { + return new GuideDialog(baseView, tipImageResId, alignBaseViewGravity).show(); + } + + public static GuideDialog show(View baseView, Bitmap tipImage, int alignBaseViewGravity) { + return new GuideDialog(baseView, tipImage, alignBaseViewGravity).show(); + } + + public static GuideDialog show(View baseView, Drawable tipImage, int alignBaseViewGravity) { + return new GuideDialog(baseView, tipImage, alignBaseViewGravity).show(); + } + + //执行方法 + public GuideDialog show() { + super.show(); + return this; + } + + public GuideDialog show(Activity activity) { + super.show(activity); + return this; + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + public GuideDialog setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me); + return this; + } + + public GuideDialog setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + refreshUI(); + return this; + } + + public GuideDialog setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public GuideDialog setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public GuideDialog setCancelable(boolean cancelable) { + this.privateCancelable = cancelable ? BOOLEAN.TRUE : BOOLEAN.FALSE; + refreshUI(); + return this; + } + + public GuideDialog.DialogImpl getDialogImpl() { + return dialogImpl; + } + + public GuideDialog setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + refreshUI(); + return this; + } + + public GuideDialog removeCustomView() { + this.onBindView.clean(); + refreshUI(); + return this; + } + + public GuideDialog setEnterAnimResId(int enterAnimResId) { + this.enterAnimResId = enterAnimResId; + return this; + } + + public GuideDialog setExitAnimResId(int exitAnimResId) { + this.exitAnimResId = exitAnimResId; + return this; + } + + public GuideDialog setAnimResId(int enterAnimResId, int exitAnimResId) { + this.enterAnimResId = enterAnimResId; + this.exitAnimResId = exitAnimResId; + return this; + } + + public GuideDialog setAlign(ALIGN align) { + this.align = align; + return this; + } + + public GuideDialog setAutoUnsafePlacePadding(boolean autoUnsafePlacePadding) { + super.setAutoUnsafePlacePadding(autoUnsafePlacePadding); + return this; + } + + public GuideDialog setFullScreen(boolean fullscreen) { + super.setFullScreen(fullscreen); + return this; + } + + public GuideDialog setMaskColor(@ColorInt int maskColor) { + this.maskColor = maskColor; + refreshUI(); + return this; + } + + public GuideDialog setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public GuideDialog setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + public GuideDialog setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public GuideDialog setBkgInterceptTouch(boolean bkgInterceptTouch) { + this.bkgInterceptTouch = bkgInterceptTouch; + return this; + } + + public GuideDialog setAlignBaseViewGravity(View baseView, int alignGravity) { + this.baseView(baseView); + this.alignViewGravity = alignGravity; + baseViewLoc = new int[4]; + baseView.getLocationInWindow(baseViewLoc); + setFullScreen(true); + return this; + } + + public GuideDialog setAlignBaseViewGravity(View baseView) { + this.baseView(baseView); + baseViewLoc = new int[4]; + baseView.getLocationInWindow(baseViewLoc); + setFullScreen(true); + return this; + } + + public GuideDialog setAlignBaseViewGravity(int alignGravity) { + this.alignViewGravity = alignGravity; + if (baseView() != null) { + baseViewLoc = new int[4]; + baseView().getLocationInWindow(baseViewLoc); + } + setFullScreen(true); + return this; + } + + public GuideDialog setAlignBaseViewGravity(View baseView, int alignGravity, int marginLeft, + int marginTop, int marginRight, int marginBottom) { + this.marginRelativeBaseView = new int[]{marginLeft, marginTop, marginRight, marginBottom}; + return setAlignBaseViewGravity(baseView, alignGravity); + } + + public GuideDialog setBaseViewMargin(int[] marginRelativeBaseView) { + this.marginRelativeBaseView = marginRelativeBaseView; + return this; + } + + public GuideDialog setBaseViewMargin(int marginLeft, int marginTop, + int marginRight, int marginBottom) { + this.marginRelativeBaseView = new int[]{marginLeft, marginTop, marginRight, marginBottom}; + return this; + } + + public GuideDialog setBaseViewMarginLeft(int marginLeft) { + this.marginRelativeBaseView[0] = marginLeft; + return this; + } + + public GuideDialog setBaseViewMarginTop(int marginTop) { + this.marginRelativeBaseView[1] = marginTop; + return this; + } + + public GuideDialog setBaseViewMarginRight(int marginRight) { + this.marginRelativeBaseView[2] = marginRight; + return this; + } + + public GuideDialog setBaseViewMarginBottom(int marginBottom) { + this.marginRelativeBaseView[3] = marginBottom; + return this; + } + + /** + * 设置对话框 UI 宽度(单位:像素) + * + * @param width 宽度(像素) + * @return CustomDialog实例 + */ + public GuideDialog setWidth(int width) { + this.width = width; + refreshUI(); + return this; + } + + /** + * 设置对话框 UI 高度(单位:像素) + * + * @param height 高度(像素) + * @return CustomDialog实例 + */ + public GuideDialog setHeight(int height) { + this.height = height; + refreshUI(); + return this; + } + + public GuideDialog setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + return this; + } + + @Override + protected void onDialogShow() { + super.onDialogShow(); + if (baseView() == null) { + super.setMaskColor(maskColor == null ? getColor(R.color.black50) : maskColor); + } + } + + View stageLightPathStub; + + @Override + protected void onDialogRefreshUI() { + super.onDialogRefreshUI(); + if (onBindView == null && tipImage != null) { + getDialogImpl().boxCustom.setFocusable(false); + getDialogImpl().boxCustom.setFocusableInTouchMode(false); + getDialogImpl().boxCustom.setOnClickListener(null); + getDialogImpl().boxCustom.setClickable(false); + + ImageView imageView = new ImageView(getOwnActivity()); + imageView.setImageDrawable(tipImage); + imageView.setAdjustViewBounds(true); + imageView.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + onBindView = new OnBindView(imageView) { + @Override + public void onBind(CustomDialog dialog, View v) { + + } + }; + onBindView.bindParent(getDialogImpl().boxCustom, me); + } + if (getOnStageLightPathClickListener() != null && baseView() != null) { + stageLightPathStub = new View(getOwnActivity()); + stageLightPathStub.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (!getOnStageLightPathClickListener().onClick(GuideDialog.this, v)) { + dismiss(); + } + } + }); + getDialogImpl().boxRoot.addView(stageLightPathStub); + } else { + if (stageLightPathStub != null && stageLightPathStub.getParent() instanceof ViewGroup) { + ((ViewGroup) stageLightPathStub.getParent()).removeView(stageLightPathStub); + } + } + } + + int[] baseViewLocCache; + + @Override + protected void onGetBaseViewLoc(int[] baseViewLoc) { + if (Arrays.equals(baseViewLoc, baseViewLocCache)) { + return; + } + if (getDialogImpl() == null) { + return; + } + Bitmap bkg = Bitmap.createBitmap(getDialogImpl().boxRoot.getWidth(), getDialogImpl().boxRoot.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bkg); + + int x = baseViewLoc[0] + baseViewLocationCoordinateCompensation[0]; + int y = baseViewLoc[1] + baseViewLocationCoordinateCompensation[1]; + int w = baseViewLoc[2] + baseViewLocationCoordinateCompensation[2]; + int h = baseViewLoc[3] + baseViewLocationCoordinateCompensation[3]; + int hW = w / 2; + int hH = h / 2; + + if (stageLightPathStub != null && (stageLightPathStub.getX() != x || stageLightPathStub.getY() != y)) { + RelativeLayout.LayoutParams rLp = (RelativeLayout.LayoutParams) stageLightPathStub.getLayoutParams(); + if (rLp == null) { + rLp = new RelativeLayout.LayoutParams(w, h); + } else { + rLp.width = w; + rLp.height = h; + } + stageLightPathStub.setLayoutParams(rLp); + stageLightPathStub.setX(x); + stageLightPathStub.setY(y); + } + + switch (stageLightType) { + case CIRCLE_OUTSIDE: { + int r = (int) Math.sqrt(hW * hW + hH * hH); + canvas.drawCircle(x + hW, y + hH, r, getStageLightPaint()); + } + break; + case CIRCLE_INSIDE: { + int r = Math.min(w, h) / 2; + canvas.drawCircle(x + hW, y + hH, r, getStageLightPaint()); + } + break; + case RECTANGLE: { + canvas.drawRoundRect(new RectF(x, y, x + w, y + h), stageLightFilletRadius, stageLightFilletRadius, getStageLightPaint()); + } + break; + case SQUARE_INSIDE: { + int r = Math.min(w, h); + canvas.drawRoundRect(new RectF(x + hW - r / 2, y + hH - r / 2, x + hW - r / 2 + r, y + hH - r / 2 + r), stageLightFilletRadius, stageLightFilletRadius, getStageLightPaint()); + } + break; + case SQUARE_OUTSIDE: { + int r = Math.max(w, h); + canvas.drawRoundRect(new RectF(x + hW - r / 2, y + hH - r / 2, x + hW - r / 2 + r, y + hH - r / 2 + r), stageLightFilletRadius, stageLightFilletRadius, getStageLightPaint()); + } + break; + } + stageLightPaint.setXfermode(null); + canvas.drawColor(maskColor == null ? getColor(R.color.black50) : maskColor, PorterDuff.Mode.SRC_OUT); + + BitmapDrawable bkgDrawable = new BitmapDrawable(getResources(), bkg); + getDialogImpl().boxRoot.setBackground(bkgDrawable); + baseViewLocCache = Arrays.copyOf(baseViewLoc, 4); + } + + Paint stageLightPaint; + + private Paint getStageLightPaint() { + if (stageLightPaint == null) { + stageLightPaint = new Paint(); + stageLightPaint.setColor(Color.RED); + stageLightPaint.setStyle(Paint.Style.FILL); + stageLightPaint.setAntiAlias(true); + } + return stageLightPaint; + } + + public STAGE_LIGHT_TYPE getStageLightType() { + return stageLightType; + } + + public GuideDialog setStageLightType(STAGE_LIGHT_TYPE stageLightType) { + this.stageLightType = stageLightType; + refreshUI(); + return this; + } + + public Drawable getTipImage() { + return tipImage; + } + + public GuideDialog setTipImage(int tipImageResId) { + this.tipImage = getResources().getDrawable(tipImageResId); + refreshUI(); + return this; + } + + public GuideDialog setTipImage(Bitmap tipImage) { + this.tipImage = new BitmapDrawable(getResources(), tipImage); + refreshUI(); + return this; + } + + public GuideDialog setTipImage(Drawable tipImage) { + this.tipImage = tipImage; + refreshUI(); + return this; + } + + public float getStageLightFilletRadius() { + return stageLightFilletRadius; + } + + public GuideDialog setStageLightFilletRadius(float stageLightFilletRadius) { + this.stageLightFilletRadius = stageLightFilletRadius; + refreshUI(); + return this; + } + + public OnDialogButtonClickListener getOnStageLightPathClickListener() { + return onStageLightPathClickListener; + } + + public GuideDialog setOnStageLightPathClickListener(OnDialogButtonClickListener onStageLightPathClickListener) { + this.onStageLightPathClickListener = onStageLightPathClickListener; + refreshUI(); + return this; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public GuideDialog setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public GuideDialog setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public GuideDialog setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + public int[] getBaseViewLocationCoordinateCompensation() { + return baseViewLocationCoordinateCompensation; + } + + public GuideDialog setBaseViewLocationCoordinateCompensation(int[] baseViewLocationCoordinateCompensation) { + this.baseViewLocationCoordinateCompensation = baseViewLocationCoordinateCompensation; + return this; + } + + public GuideDialog setBaseViewLocationCoordinateCompensation(int px) { + this.baseViewLocationCoordinateCompensation = new int[]{px, px, px, px}; + refreshUI(); + return this; + } + + public GuideDialog setBaseViewLocationCoordinateCompensation(int pxX, int pxY, int pxR, int pxB) { + this.baseViewLocationCoordinateCompensation = new int[]{pxX, pxY, pxR, pxB}; + refreshUI(); + return this; + } + + public GuideDialog setBaseViewLocationCoordinateCompensationLeft(int pxX) { + this.baseViewLocationCoordinateCompensation[0] = pxX; + refreshUI(); + return this; + } + + public GuideDialog setBaseViewLocationCoordinateCompensationTop(int pxY) { + this.baseViewLocationCoordinateCompensation[1] = pxY; + refreshUI(); + return this; + } + + public GuideDialog setBaseViewLocationCoordinateCompensationRight(int pxR) { + this.baseViewLocationCoordinateCompensation[2] = pxR; + refreshUI(); + return this; + } + + public GuideDialog setBaseViewLocationCoordinateCompensationBottom(int pxB) { + this.baseViewLocationCoordinateCompensation[3] = pxB; + refreshUI(); + return this; + } + + public int getBaseViewLocationCoordinateCompensationLeft() { + return baseViewLocationCoordinateCompensation[0]; + } + + public int getBaseViewLocationCoordinateCompensationTop() { + return baseViewLocationCoordinateCompensation[1]; + } + + public int getBaseViewLocationCoordinateCompensationRight() { + return baseViewLocationCoordinateCompensation[2]; + } + + public int getBaseViewLocationCoordinateCompensationBottom() { + return baseViewLocationCoordinateCompensation[3]; + } + + public GuideDialog setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public GuideDialog onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public GuideDialog onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public GuideDialog setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public GuideDialog setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex("+orderIndex+") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public GuideDialog bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public GuideDialog setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public GuideDialog cleanAction(int actionId){ + dialogActionRunnableMap.remove(actionId); + return this; + } + + public GuideDialog cleanAllAction(){ + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss(){ + dismiss(); + } + + public GuideDialog bindDismissWithLifecycleOwner(LifecycleOwner owner){ + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/InputDialog.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/InputDialog.java new file mode 100644 index 0000000..1a492e8 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/InputDialog.java @@ -0,0 +1,759 @@ +package com.kongzue.dialogx.dialogs; + +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.text.TextUtils; +import android.view.View; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.OnInputDialogButtonClickListener; +import com.kongzue.dialogx.util.InputInfo; +import com.kongzue.dialogx.util.TextInfo; + +import java.util.HashMap; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/24 13:53 + */ +public class InputDialog extends MessageDialog { + + protected InputDialog() { + super(); + } + + public static InputDialog build() { + return new InputDialog(); + } + + public static InputDialog build(DialogXStyle style) { + InputDialog dialog = new InputDialog(); + dialog.setStyle(style); + return dialog; + } + + public static InputDialog build(OnBindView onBindView) { + return new InputDialog().setCustomView(onBindView); + } + + public InputDialog(CharSequence title, CharSequence message, CharSequence okText) { + cancelable = DialogX.cancelable; + this.title = title; + this.message = message; + this.okText = okText; + } + + public InputDialog(int titleResId, int messageResId, int okTextResId) { + cancelable = DialogX.cancelable; + this.title = getString(titleResId); + this.message = getString(messageResId); + this.okText = getString(okTextResId); + } + + public static InputDialog show(CharSequence title, CharSequence message, CharSequence okText) { + InputDialog inputDialog = new InputDialog(title, message, okText); + inputDialog.show(); + return inputDialog; + } + + public static InputDialog show(int titleResId, int messageResId, int okTextResId) { + InputDialog inputDialog = new InputDialog(titleResId, messageResId, okTextResId); + inputDialog.show(); + return inputDialog; + } + + public InputDialog(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText) { + cancelable = DialogX.cancelable; + this.title = title; + this.message = message; + this.okText = okText; + this.cancelText = cancelText; + } + + public InputDialog(int titleResId, int messageResId, int okTextResId, int cancelTextResId) { + cancelable = DialogX.cancelable; + this.title = getString(titleResId); + this.message = getString(messageResId); + this.okText = getString(okTextResId); + this.cancelText = getString(cancelTextResId); + } + + public static InputDialog show(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText) { + InputDialog inputDialog = new InputDialog(title, message, okText, cancelText); + inputDialog.show(); + return inputDialog; + } + + public static InputDialog show(int titleResId, int messageResId, int okTextResId, int cancelTextResId) { + InputDialog inputDialog = new InputDialog(titleResId, messageResId, okTextResId, cancelTextResId); + inputDialog.show(); + return inputDialog; + } + + public InputDialog(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText, String inputText) { + cancelable = DialogX.cancelable; + this.title = title; + this.message = message; + this.okText = okText; + this.cancelText = cancelText; + this.inputText = inputText; + } + + public static InputDialog show(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText, String inputText) { + InputDialog inputDialog = new InputDialog(title, message, okText, cancelText, inputText); + inputDialog.show(); + return inputDialog; + } + + public InputDialog(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText, CharSequence otherText) { + cancelable = DialogX.cancelable; + this.title = title; + this.message = message; + this.okText = okText; + this.cancelText = cancelText; + this.otherText = otherText; + } + + public InputDialog(int titleResId, int messageResId, int okTextResId, int cancelTextResId, int otherTextResId) { + cancelable = DialogX.cancelable; + this.title = getString(titleResId); + this.message = getString(messageResId); + this.okText = getString(okTextResId); + this.cancelText = getString(cancelTextResId); + this.otherText = getString(otherTextResId); + } + + public static InputDialog show(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText, CharSequence otherText) { + InputDialog inputDialog = new InputDialog(title, message, okText, cancelText, otherText); + inputDialog.show(); + return inputDialog; + } + + public static InputDialog show(int titleResId, int messageResId, int okTextResId, int cancelTextResId, int otherTextResId) { + InputDialog inputDialog = new InputDialog(titleResId, messageResId, okTextResId, cancelTextResId, otherTextResId); + inputDialog.show(); + return inputDialog; + } + + public InputDialog(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText, CharSequence otherText, String inputText) { + cancelable = DialogX.cancelable; + this.title = title; + this.message = message; + this.okText = okText; + this.cancelText = cancelText; + this.otherText = otherText; + this.inputText = inputText; + } + + public InputDialog(int titleResId, int messageResId, int okTextResId, int cancelTextResId, int otherTextResId, int inputTextResId) { + cancelable = DialogX.cancelable; + this.title = getString(titleResId); + this.message = getString(messageResId); + this.okText = getString(okTextResId); + this.cancelText = getString(cancelTextResId); + this.otherText = getString(otherTextResId); + this.inputText = getString(inputTextResId); + } + + public static InputDialog show(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText, CharSequence otherText, String inputText) { + InputDialog inputDialog = new InputDialog(title, message, okText, cancelText, otherText, inputText); + inputDialog.show(); + return inputDialog; + } + + public static InputDialog show(int titleResId, int messageResId, int okTextResId, int cancelTextResId, int otherTextResId, int inputTextResId) { + InputDialog inputDialog = new InputDialog(titleResId, messageResId, okTextResId, cancelTextResId, otherTextResId, inputTextResId); + inputDialog.show(); + return inputDialog; + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + public CharSequence getOkButton() { + return okText; + } + + public InputDialog setOkButton(CharSequence okText) { + this.okText = okText; + refreshUI(); + return this; + } + + public InputDialog setOkButton(int okTextResId) { + this.okText = getString(okTextResId); + refreshUI(); + return this; + } + + public InputDialog setOkButton(OnInputDialogButtonClickListener okButtonClickListener) { + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public InputDialog setOkButton(CharSequence okText, OnInputDialogButtonClickListener okButtonClickListener) { + this.okText = okText; + this.okButtonClickListener = okButtonClickListener; + refreshUI(); + return this; + } + + public InputDialog setOkButton(int okTextResId, OnInputDialogButtonClickListener okButtonClickListener) { + this.okText = getString(okTextResId); + this.okButtonClickListener = okButtonClickListener; + refreshUI(); + return this; + } + + public CharSequence getCancelButton() { + return cancelText; + } + + public InputDialog setCancelButton(CharSequence cancelText) { + this.cancelText = cancelText; + refreshUI(); + return this; + } + + public InputDialog setCancelButton(int cancelTextResId) { + this.cancelText = getString(cancelTextResId); + refreshUI(); + return this; + } + + public InputDialog setCancelButton(OnInputDialogButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + public InputDialog setCancelButton(CharSequence cancelText, OnInputDialogButtonClickListener cancelButtonClickListener) { + this.cancelText = cancelText; + this.cancelButtonClickListener = cancelButtonClickListener; + refreshUI(); + return this; + } + + public InputDialog setCancelButton(int cancelTextResId, OnInputDialogButtonClickListener cancelButtonClickListener) { + this.cancelText = getString(cancelTextResId); + this.cancelButtonClickListener = cancelButtonClickListener; + refreshUI(); + return this; + } + + public CharSequence getOtherButton() { + return otherText; + } + + public InputDialog setOtherButton(CharSequence otherText) { + this.otherText = otherText; + refreshUI(); + return this; + } + + public InputDialog setOtherButton(int otherTextResId) { + this.otherText = getString(otherTextResId); + refreshUI(); + return this; + } + + public InputDialog setOtherButton(OnInputDialogButtonClickListener otherButtonClickListener) { + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + public InputDialog setOtherButton(CharSequence otherText, OnInputDialogButtonClickListener otherButtonClickListener) { + this.otherText = otherText; + this.otherButtonClickListener = otherButtonClickListener; + refreshUI(); + return this; + } + + public InputDialog setOtherButton(int otherTextResId, OnInputDialogButtonClickListener otherButtonClickListener) { + this.otherText = getString(otherTextResId); + this.otherButtonClickListener = otherButtonClickListener; + refreshUI(); + return this; + } + + public OnInputDialogButtonClickListener getInputOkButtonClickListener() { + return (OnInputDialogButtonClickListener) okButtonClickListener; + } + + public InputDialog setOkButtonClickListener(OnInputDialogButtonClickListener okButtonClickListener) { + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public OnInputDialogButtonClickListener getInputCancelButtonClickListener() { + return (OnInputDialogButtonClickListener) cancelButtonClickListener; + } + + public InputDialog setCancelButtonClickListener(OnInputDialogButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + public OnInputDialogButtonClickListener getInputOtherButtonClickListener() { + return (OnInputDialogButtonClickListener) otherButtonClickListener; + } + + public InputDialog setOtherButtonClickListener(OnInputDialogButtonClickListener otherButtonClickListener) { + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + public CharSequence getTitle() { + return title; + } + + public InputDialog setTitle(CharSequence title) { + this.title = title; + refreshUI(); + return this; + } + + public InputDialog setTitle(int titleResId) { + this.title = getString(titleResId); + refreshUI(); + return this; + } + + public CharSequence getMessage() { + return message; + } + + public InputDialog setMessage(CharSequence message) { + this.message = message; + refreshUI(); + return this; + } + + public InputDialog setMessage(int messageResId) { + this.message = getString(messageResId); + refreshUI(); + return this; + } + + public String getInputText() { + if (getDialogImpl() != null && getDialogImpl().txtInput != null) { + return getDialogImpl().txtInput.getText().toString(); + } + return inputText; + } + + public InputDialog setInputText(String inputText) { + this.inputText = inputText; + refreshUI(); + return this; + } + + public InputDialog setInputText(int inputTextResId) { + this.inputText = getString(inputTextResId); + refreshUI(); + return this; + } + + public String getInputHintText() { + return inputHintText; + } + + public InputDialog setInputHintText(String inputHintText) { + this.inputHintText = inputHintText; + refreshUI(); + return this; + } + + public InputDialog setInputHintText(int inputHintTextResId) { + this.inputHintText = getString(inputHintTextResId); + refreshUI(); + return this; + } + + public TextInfo getTitleTextInfo() { + return titleTextInfo; + } + + public InputDialog setTitleTextInfo(TextInfo titleTextInfo) { + this.titleTextInfo = titleTextInfo; + refreshUI(); + return this; + } + + public TextInfo getMessageTextInfo() { + return messageTextInfo; + } + + public InputDialog setMessageTextInfo(TextInfo messageTextInfo) { + this.messageTextInfo = messageTextInfo; + refreshUI(); + return this; + } + + public TextInfo getOkTextInfo() { + return okTextInfo; + } + + public InputDialog setOkTextInfo(TextInfo okTextInfo) { + this.okTextInfo = okTextInfo; + refreshUI(); + return this; + } + + public TextInfo getCancelTextInfo() { + return cancelTextInfo; + } + + public InputDialog setCancelTextInfo(TextInfo cancelTextInfo) { + this.cancelTextInfo = cancelTextInfo; + refreshUI(); + return this; + } + + public TextInfo getOtherTextInfo() { + return otherTextInfo; + } + + public InputDialog setOtherTextInfo(TextInfo otherTextInfo) { + this.otherTextInfo = otherTextInfo; + refreshUI(); + return this; + } + + public InputInfo getInputInfo() { + return inputInfo; + } + + public InputDialog setInputInfo(InputInfo inputInfo) { + this.inputInfo = inputInfo; + refreshUI(); + return this; + } + + public int getButtonOrientation() { + return buttonOrientation; + } + + public InputDialog setButtonOrientation(int buttonOrientation) { + this.buttonOrientation = buttonOrientation; + refreshUI(); + return this; + } + + public boolean isCancelable() { + if (privateCancelable != null) { + return privateCancelable == BOOLEAN.TRUE; + } + if (overrideCancelable != null) { + return overrideCancelable == BOOLEAN.TRUE; + } + return cancelable; + } + + public InputDialog setCancelable(boolean cancelable) { + this.privateCancelable = cancelable ? BOOLEAN.TRUE : BOOLEAN.FALSE; + refreshUI(); + return this; + } + + public OnBackPressedListener getOnBackPressedListener() { + return (OnBackPressedListener) onBackPressedListener; + } + + public InputDialog setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + return this; + } + + public boolean isAutoShowInputKeyboard() { + return autoShowInputKeyboard; + } + + public InputDialog setAutoShowInputKeyboard(boolean autoShowInputKeyboard) { + this.autoShowInputKeyboard = autoShowInputKeyboard; + return this; + } + + public InputDialog setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + refreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) return null; + return onBindView.getCustomView(); + } + + public InputDialog removeCustomView() { + this.onBindView.clean(); + refreshUI(); + return this; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public InputDialog setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + refreshUI(); + return this; + } + + public InputDialog setBackgroundColorRes(@ColorRes int backgroundColorResId) { + this.backgroundColor = getColor(backgroundColorResId); + refreshUI(); + return this; + } + + public InputDialog setMaskColor(@ColorInt int maskColor) { + this.maskColor = maskColor; + refreshUI(); + return this; + } + + public long getEnterAnimDuration() { + return enterAnimDuration; + } + + public InputDialog setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public long getExitAnimDuration() { + return exitAnimDuration; + } + + public InputDialog setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + @Override + public void restartDialog() { + if (getDialogView() != null) { + dismiss(getDialogView()); + isShow = false; + } + if (getDialogImpl().boxCustom != null) { + getDialogImpl().boxCustom.removeAllViews(); + } + int layoutId = style.layout(isLightTheme()); + layoutId = layoutId == 0 ? (isLightTheme() ? R.layout.layout_dialogx_material : R.layout.layout_dialogx_material_dark) : layoutId; + + String inputText = getInputText(); + enterAnimDuration = 0; + View dialogView = createView(layoutId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + setInputText(inputText); + } + + public InputDialog setAnimResId(int enterResId, int exitResId) { + customEnterAnimResId = enterResId; + customExitAnimResId = exitResId; + return this; + } + + public InputDialog setEnterAnimResId(int enterResId) { + customEnterAnimResId = enterResId; + return this; + } + + public InputDialog setExitAnimResId(int exitResId) { + customExitAnimResId = exitResId; + return this; + } + + public InputDialog setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + refreshUI(); + return this; + } + + public InputDialog setMaxHeight(int maxHeight) { + this.maxHeight = maxHeight; + refreshUI(); + return this; + } + + public InputDialog setMinHeight(int minHeight) { + this.minHeight = minHeight; + refreshUI(); + return this; + } + + public InputDialog setMinWidth(int minWidth) { + this.minWidth = minWidth; + refreshUI(); + return this; + } + + public InputDialog setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public boolean isBkgInterceptTouch() { + return bkgInterceptTouch; + } + + public InputDialog setBkgInterceptTouch(boolean bkgInterceptTouch) { + this.bkgInterceptTouch = bkgInterceptTouch; + return this; + } + + public OnBackgroundMaskClickListener getOnBackgroundMaskClickListener() { + return onBackgroundMaskClickListener; + } + + public InputDialog setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + return this; + } + + public InputDialog setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public InputDialog setTitleIcon(Bitmap titleIcon) { + this.titleIcon = new BitmapDrawable(getResources(), titleIcon); + refreshUI(); + return this; + } + + public InputDialog setTitleIcon(int titleIconResId) { + this.titleIcon = getResources().getDrawable(titleIconResId); + refreshUI(); + return this; + } + + public InputDialog setTitleIcon(Drawable titleIcon) { + this.titleIcon = titleIcon; + refreshUI(); + return this; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public InputDialog setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public InputDialog setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public InputDialog setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + public InputDialog setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me); + return this; + } + + public InputDialog setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public InputDialog onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public InputDialog onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public InputDialog setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public InputDialog appendMessage(CharSequence message){ + this.message = TextUtils.concat(this.message, message); + refreshUI(); + return this; + } + + public InputDialog setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex("+orderIndex+") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public InputDialog bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public InputDialog setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public InputDialog cleanAction(int actionId){ + dialogActionRunnableMap.remove(actionId); + return this; + } + + public InputDialog cleanAllAction(){ + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss(){ + dismiss(); + } + + public InputDialog bindDismissWithLifecycleOwner(LifecycleOwner owner){ + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/MessageDialog.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/MessageDialog.java new file mode 100644 index 0000000..ebafd6f --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/MessageDialog.java @@ -0,0 +1,1612 @@ +package com.kongzue.dialogx.dialogs; + +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.graphics.Bitmap; +import android.graphics.Outline; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.text.InputFilter; +import android.text.InputType; +import android.text.TextUtils; +import android.text.method.LinkMovementMethod; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.DecelerateInterpolator; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.Space; +import android.widget.TextView; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.annotation.Nullable; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.BaseOnDialogClickCallback; +import com.kongzue.dialogx.interfaces.BlurViewType; +import com.kongzue.dialogx.interfaces.DialogConvertViewInterface; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.OnDialogButtonClickListener; +import com.kongzue.dialogx.interfaces.OnInputDialogButtonClickListener; +import com.kongzue.dialogx.interfaces.OnMenuButtonClickListener; +import com.kongzue.dialogx.interfaces.ScrollController; +import com.kongzue.dialogx.style.MaterialStyle; +import com.kongzue.dialogx.util.InputInfo; +import com.kongzue.dialogx.util.TextInfo; +import com.kongzue.dialogx.util.views.DialogScrollView; +import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout; +import com.kongzue.dialogx.util.views.MaxRelativeLayout; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.List; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/21 17:08 + */ +public class MessageDialog extends BaseDialog { + + public static int overrideEnterDuration = -1; + public static int overrideExitDuration = -1; + public static int overrideEnterAnimRes = 0; + public static int overrideExitAnimRes = 0; + public static BOOLEAN overrideCancelable; + protected boolean bkgInterceptTouch = true; + protected OnBindView onBindView; + protected MessageDialog me = this; + protected BOOLEAN privateCancelable; + protected int customEnterAnimResId; + protected int customExitAnimResId; + protected DialogXAnimInterface dialogXAnimImpl; + protected OnBackPressedListener onBackPressedListener; + protected BUTTON_SELECT_RESULT buttonSelectResult = BUTTON_SELECT_RESULT.NONE; + + protected DialogLifecycleCallback dialogLifecycleCallback; + protected OnBackgroundMaskClickListener onBackgroundMaskClickListener; + + protected MessageDialog() { + super(); + } + + protected CharSequence title; + protected CharSequence message; + protected CharSequence okText; + protected CharSequence cancelText; + protected CharSequence otherText; + protected String inputText; + protected String inputHintText; + protected Integer maskColor = null; + protected float backgroundRadius = DialogX.defaultMessageDialogBackgroundRadius; + protected Drawable titleIcon; + + protected TextInfo titleTextInfo; + protected TextInfo messageTextInfo; + protected TextInfo okTextInfo; + protected TextInfo cancelTextInfo; + protected TextInfo otherTextInfo; + protected InputInfo inputInfo; + + protected BaseOnDialogClickCallback okButtonClickListener; + protected BaseOnDialogClickCallback cancelButtonClickListener; + protected BaseOnDialogClickCallback otherButtonClickListener; + + protected int buttonOrientation; + + public static MessageDialog build() { + return new MessageDialog(); + } + + public static MessageDialog build(DialogXStyle style) { + return new MessageDialog().setStyle(style); + } + + public static MessageDialog build(OnBindView onBindView) { + return new MessageDialog().setCustomView(onBindView); + } + + public MessageDialog(CharSequence message) { + this.message = message; + } + + public MessageDialog(CharSequence title, CharSequence message) { + this.title = title; + this.message = message; + } + + public MessageDialog(CharSequence title, CharSequence message, CharSequence okText) { + this.title = title; + this.message = message; + this.okText = okText; + } + + public MessageDialog(int titleResId, int messageResId, int okTextResId) { + this.title = getString(titleResId); + this.message = getString(messageResId); + this.okText = getString(okTextResId); + } + + public MessageDialog(int titleResId, int messageResId) { + this.title = getString(titleResId); + this.message = getString(messageResId); + } + + public static MessageDialog show(CharSequence title, CharSequence message, CharSequence okText) { + MessageDialog messageDialog = new MessageDialog(title, message, okText); + messageDialog.show(); + return messageDialog; + } + + public static MessageDialog show(int titleResId, int messageResId, int okTextResId) { + MessageDialog messageDialog = new MessageDialog(titleResId, messageResId, okTextResId); + messageDialog.show(); + return messageDialog; + } + + public static MessageDialog show(CharSequence title, CharSequence message) { + MessageDialog messageDialog = new MessageDialog(title, message); + messageDialog.show(); + return messageDialog; + } + + public static MessageDialog show(int titleResId, int messageResId) { + MessageDialog messageDialog = new MessageDialog(titleResId, messageResId); + messageDialog.show(); + return messageDialog; + } + + public MessageDialog(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText) { + this.title = title; + this.message = message; + this.okText = okText; + this.cancelText = cancelText; + } + + public MessageDialog(int titleResId, int messageResId, int okTextResId, int cancelTextResId) { + this.title = getString(titleResId); + this.message = getString(messageResId); + this.okText = getString(okTextResId); + this.cancelText = getString(cancelTextResId); + } + + public static MessageDialog show(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText) { + MessageDialog messageDialog = new MessageDialog(title, message, okText, cancelText); + messageDialog.show(); + return messageDialog; + } + + public static MessageDialog show(int titleResId, int messageResId, int okTextResId, int cancelTextResId) { + MessageDialog messageDialog = new MessageDialog(titleResId, messageResId, okTextResId, cancelTextResId); + messageDialog.show(); + return messageDialog; + } + + public MessageDialog(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText, CharSequence otherText) { + this.title = title; + this.message = message; + this.okText = okText; + this.cancelText = cancelText; + this.otherText = otherText; + } + + public MessageDialog(int titleResId, int messageResId, int okTextResId, int cancelTextResId, int otherTextResId) { + this.title = getString(titleResId); + this.message = getString(messageResId); + this.okText = getString(okTextResId); + this.cancelText = getString(cancelTextResId); + this.otherText = getString(otherTextResId); + } + + public static MessageDialog show(CharSequence title, CharSequence message, CharSequence okText, CharSequence cancelText, CharSequence otherText) { + MessageDialog messageDialog = new MessageDialog(title, message, okText, cancelText, otherText); + messageDialog.show(); + return messageDialog; + } + + public static MessageDialog show(int titleResId, int messageResId, int okTextResId, int cancelTextResId, int otherTextResId) { + MessageDialog messageDialog = new MessageDialog(titleResId, messageResId, okTextResId, cancelTextResId, otherTextResId); + messageDialog.show(); + return messageDialog; + } + + protected DialogImpl dialogImpl; + + public MessageDialog show() { + if (isHide && getDialogView() != null && isShow) { + if (hideWithExitAnim && getDialogImpl() != null) { + getDialogView().setVisibility(View.VISIBLE); + getDialogImpl().getDialogXAnimImpl().doShowAnim(me, getDialogImpl().bkg); + } else { + getDialogView().setVisibility(View.VISIBLE); + } + return this; + } + super.beforeShow(); + if (getDialogView() == null) { + int layoutId = style.layout(isLightTheme()); + layoutId = layoutId == 0 ? (isLightTheme() ? R.layout.layout_dialogx_material : R.layout.layout_dialogx_material_dark) : layoutId; + + View dialogView = createView(layoutId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } else { + show(getDialogView()); + } + return this; + } + + public void show(Activity activity) { + super.beforeShow(); + if (getDialogView() == null) { + int layoutId = style.layout(isLightTheme()); + layoutId = layoutId == 0 ? (isLightTheme() ? R.layout.layout_dialogx_material : R.layout.layout_dialogx_material_dark) : layoutId; + + View dialogView = createView(layoutId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(activity, dialogView); + } else { + show(activity, getDialogView()); + } + } + + public void refreshUI() { + if (getDialogImpl() == null) return; + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl != null) dialogImpl.refreshView(); + } + }); + } + + public class DialogImpl implements DialogConvertViewInterface { + + private List blurViews; + + public DialogXBaseRelativeLayout boxRoot; + public MaxRelativeLayout bkg; + public TextView txtDialogTitle; + public ScrollController scrollView; + public TextView txtDialogTip; + public ViewGroup boxList; + public RelativeLayout boxCustom; + public EditText txtInput; + public LinearLayout boxButton; + public TextView btnSelectOther; + public View spaceOtherButton; + public View splitHorizontal; + public TextView btnSelectNegative; + public TextView btnSelectPositive; + + public DialogImpl(View convertView) { + if (convertView == null) return; + setDialogView(convertView); + boxRoot = convertView.findViewById(R.id.box_root); + bkg = convertView.findViewById(R.id.bkg); + txtDialogTitle = convertView.findViewById(R.id.txt_dialog_title); + scrollView = convertView.findViewById(R.id.scrollView); + txtDialogTip = convertView.findViewById(R.id.txt_dialog_tip); + boxList = convertView.findViewById(R.id.box_list); + boxCustom = convertView.findViewById(R.id.box_custom); + txtInput = convertView.findViewById(R.id.txt_input); + boxButton = convertView.findViewById(R.id.box_button); + btnSelectOther = convertView.findViewById(R.id.btn_selectOther); + spaceOtherButton = convertView.findViewById(R.id.space_other_button); + splitHorizontal = convertView.findViewWithTag("split"); + btnSelectNegative = convertView.findViewById(R.id.btn_selectNegative); + btnSelectPositive = convertView.findViewById(R.id.btn_selectPositive); + + blurViews = findAllBlurView(convertView); + + init(); + + dialogImpl = this; + refreshView(); + } + + public void init() { + buttonSelectResult = BUTTON_SELECT_RESULT.NONE; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(getThisOrderIndex()); + } + + if (titleTextInfo == null) titleTextInfo = DialogX.titleTextInfo; + if (messageTextInfo == null) messageTextInfo = DialogX.messageTextInfo; + if (okTextInfo == null) okTextInfo = DialogX.okButtonTextInfo; + if (okTextInfo == null) okTextInfo = DialogX.buttonTextInfo; + if (cancelTextInfo == null) cancelTextInfo = DialogX.buttonTextInfo; + if (otherTextInfo == null) otherTextInfo = DialogX.buttonTextInfo; + if (inputInfo == null) inputInfo = DialogX.inputInfo; + if (backgroundColor == null) backgroundColor = DialogX.backgroundColor; + + txtDialogTitle.getPaint().setFakeBoldText(true); + btnSelectNegative.getPaint().setFakeBoldText(true); + btnSelectPositive.getPaint().setFakeBoldText(true); + btnSelectOther.getPaint().setFakeBoldText(true); + + txtDialogTip.setMovementMethod(LinkMovementMethod.getInstance()); + + boxRoot.setBkgAlpha(0f); + boxRoot.setParentDialog(me); + boxRoot.setOnLifecycleCallBack(new DialogXBaseRelativeLayout.OnLifecycleCallBack() { + @Override + public void onShow() { + isShow = true; + preShow = false; + + setLifecycleState(Lifecycle.State.CREATED); + + onDialogShow(); + getDialogLifecycleCallback().onShow(me); + MessageDialog.this.onShow(me); + + getDialogXAnimImpl().doShowAnim(me, bkg); + + if (style.messageDialogBlurSettings() != null && style.messageDialogBlurSettings().blurBackground()) { + bkg.post(new Runnable() { + @Override + public void run() { + Integer blurFrontColor = null; + Float dialogXRadius = null; + if (style.messageDialogBlurSettings() != null) { + blurFrontColor = getColorNullable(getIntStyleAttr(style.messageDialogBlurSettings().blurForwardColorRes(isLightTheme()))); + dialogXRadius = getFloatStyleAttr((float) style.messageDialogBlurSettings().blurBackgroundRoundRadiusPx()); + } + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setOverlayColor(backgroundColor == null ? blurFrontColor : backgroundColor); + ((BlurViewType) blurView).setRadiusPx(dialogXRadius); + } + } + + setLifecycleState(Lifecycle.State.RESUMED); + } + }); + } + + if (autoShowInputKeyboard) { + txtInput.postDelayed(new Runnable() { + @Override + public void run() { + if (txtInput == null) return; + txtInput.requestFocus(); + txtInput.setFocusableInTouchMode(true); + imeShow(txtInput, true); + txtInput.setSelection(txtInput.getText().length()); + if (inputInfo != null && inputInfo.isSelectAllText()) { + txtInput.selectAll(); + } + } + }, 300); + } else { + if (inputInfo != null && inputInfo.isSelectAllText()) { + txtInput.clearFocus(); + txtInput.requestFocus(); + txtInput.selectAll(); + } + } + } + + @Override + public void onDismiss() { + isShow = false; + getDialogLifecycleCallback().onDismiss(me); + MessageDialog.this.onDismiss(me); + setLifecycleState(Lifecycle.State.DESTROYED); + dialogLifecycleCallback = null; + + System.gc(); + } + }); + + boxRoot.setOnBackPressedListener(new DialogXBaseRelativeLayout.PrivateBackPressedListener() { + @Override + public boolean onBackPressed() { + if (onBackPressedListener != null) { + if (onBackPressedListener.onBackPressed(me)) { + dismiss(); + } + } else { + if (isCancelable()) { + dismiss(); + } + } + return true; + } + }); + btnSelectPositive.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + buttonSelectResult = BUTTON_SELECT_RESULT.BUTTON_OK; + if (txtInput != null) { + imeShow(txtInput, false); + } + haptic(v); + if (okButtonClickListener != null) { + if (okButtonClickListener instanceof OnInputDialogButtonClickListener) { + String s = txtInput == null ? "" : txtInput.getText().toString(); + if (!((OnInputDialogButtonClickListener) okButtonClickListener).onClick(me, v, s)) { + doDismiss(v); + } + } else if (okButtonClickListener instanceof OnDialogButtonClickListener) { + if (!((OnDialogButtonClickListener) okButtonClickListener).onClick(me, v)) { + doDismiss(v); + } + } else if (okButtonClickListener instanceof OnMenuButtonClickListener) { + if (!((OnMenuButtonClickListener) okButtonClickListener).onClick(me, v)) { + doDismiss(v); + } + } + } else { + doDismiss(v); + } + } + }); + btnSelectNegative.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + buttonSelectResult = BUTTON_SELECT_RESULT.BUTTON_CANCEL; + if (txtInput != null) { + imeShow(txtInput, false); + } + haptic(v); + if (cancelButtonClickListener != null) { + if (cancelButtonClickListener instanceof OnInputDialogButtonClickListener) { + String s = txtInput == null ? "" : txtInput.getText().toString(); + if (!((OnInputDialogButtonClickListener) cancelButtonClickListener).onClick(me, v, s)) { + doDismiss(v); + } + } else if (cancelButtonClickListener instanceof OnMenuButtonClickListener) { + if (!((OnMenuButtonClickListener) cancelButtonClickListener).onClick(me, v)) { + doDismiss(v); + } + } else { + if (!((OnDialogButtonClickListener) cancelButtonClickListener).onClick(me, v)) { + doDismiss(v); + } + } + } else { + doDismiss(v); + } + } + }); + btnSelectOther.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + buttonSelectResult = BUTTON_SELECT_RESULT.BUTTON_OTHER; + if (txtInput != null) { + imeShow(txtInput, false); + } + haptic(v); + if (otherButtonClickListener != null) { + if (otherButtonClickListener instanceof OnInputDialogButtonClickListener) { + String s = txtInput == null ? "" : txtInput.getText().toString(); + if (!((OnInputDialogButtonClickListener) otherButtonClickListener).onClick(me, v, s)) { + doDismiss(v); + } + } else if (otherButtonClickListener instanceof OnMenuButtonClickListener) { + if (!((OnMenuButtonClickListener) otherButtonClickListener).onClick(me, v)) { + doDismiss(v); + } + } else { + if (!((OnDialogButtonClickListener) otherButtonClickListener).onClick(me, v)) { + doDismiss(v); + } + } + } else { + doDismiss(v); + } + } + }); + + onDialogInit(); + } + + public void refreshView() { + if (boxRoot == null || getOwnActivity() == null) { + return; + } + + boxRoot.setAutoUnsafePlacePadding(isEnableImmersiveMode()); + // 修改下划线颜色 + if (inputInfo != null && inputInfo.getBottomLineColor() != null) { + txtInput.getBackground().mutate().setColorFilter(inputInfo.getBottomLineColor(), PorterDuff.Mode.SRC_ATOP); + } + // 修改光标颜色 + if (inputInfo != null && inputInfo.getCursorColor() != null) { + int cursorColor = inputInfo.getCursorColor(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + if (txtInput.getTextCursorDrawable() != null) { + txtInput.getTextCursorDrawable().mutate().setColorFilter((new PorterDuffColorFilter(cursorColor, PorterDuff.Mode.SRC_ATOP))); + } else { + try { + @SuppressLint("SoonBlockedPrivateApi") Field field = TextView.class.getDeclaredField("mCursorDrawableRes"); + field.setAccessible(true); + field.set(txtInput, R.drawable.rect_dialogx_defalut_edittxt_cursor); + txtInput.getTextCursorDrawable().mutate().setColorFilter((new PorterDuffColorFilter(cursorColor, PorterDuff.Mode.SRC_ATOP))); + } catch (Throwable throwable) { + log("DialogX: 在对话框" + dialogKey() + "中设置光标颜色时发生错误!"); + if (DialogX.DEBUGMODE) { + throwable.printStackTrace(); + } + } + } + } else { + // Thanks for @Jared Rummler https://stackoverflow.com/questions/11554078/set-textcursordrawable-programmatically/57555148#57555148 + try { + Field fCursorDrawableRes = TextView.class.getDeclaredField("mCursorDrawableRes"); + fCursorDrawableRes.setAccessible(true); + int mCursorDrawableRes = fCursorDrawableRes.getInt(txtInput); + Field fEditor = TextView.class.getDeclaredField("mEditor"); + fEditor.setAccessible(true); + Object editor = fEditor.get(txtInput); + Class clazz = editor.getClass(); + Field fCursorDrawable = clazz.getDeclaredField("mCursorDrawable"); + fCursorDrawable.setAccessible(true); + Drawable[] drawables = new Drawable[2]; + drawables[0] = txtInput.getContext().getResources().getDrawable(mCursorDrawableRes); + drawables[1] = txtInput.getContext().getResources().getDrawable(mCursorDrawableRes); + drawables[0].setColorFilter(cursorColor, PorterDuff.Mode.SRC_IN); + drawables[1].setColorFilter(cursorColor, PorterDuff.Mode.SRC_IN); + fCursorDrawable.set(editor, drawables); + } catch (Throwable throwable) { + log("DialogX: 在对话框" + dialogKey() + "中设置光标颜色时发生错误!"); + if (DialogX.DEBUGMODE) { + throwable.printStackTrace(); + } + } + } + } + + boxRoot.setRootPadding(screenPaddings[0], screenPaddings[1], screenPaddings[2], screenPaddings[3]); + if (backgroundColor != null) { + tintColor(bkg, backgroundColor); + if (style.tintButtonBackground()) { + tintColor(btnSelectOther, backgroundColor); + tintColor(btnSelectNegative, backgroundColor); + tintColor(btnSelectPositive, backgroundColor); + } + + if (blurViews != null) { + log("#blurViews != null"); + for (View blurView : blurViews) { + log("#blurView: " + blurView); + ((BlurViewType) blurView).setOverlayColor(backgroundColor); + } + } + } + + bkg.setMaxWidth(getMaxWidth()); + bkg.setMaxHeight(getMaxHeight()); + bkg.setMinimumWidth(getMinWidth()); + bkg.setMinimumHeight(getMinHeight()); + + View inputBoxView = boxRoot.findViewWithTag("dialogx_editbox"); + if (me instanceof InputDialog) { + if (inputBoxView != null) { + inputBoxView.setVisibility(View.VISIBLE); + } + txtInput.setVisibility(View.VISIBLE); + boxRoot.bindFocusView(txtInput); + } else { + if (inputBoxView != null) { + inputBoxView.setVisibility(View.GONE); + } + txtInput.setVisibility(View.GONE); + } + boxRoot.setClickable(true); + if (maskColor != null) { + boxRoot.setBackgroundColor(maskColor); + } + if (backgroundRadius > -1) { + // GradientDrawable gradientDrawable = (GradientDrawable) bkg.getBackground(); + // if (gradientDrawable != null) gradientDrawable.setCornerRadius(backgroundRadius); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + bkg.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), backgroundRadius); + } + }); + bkg.setClipToOutline(true); + } + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setRadiusPx(backgroundRadius); + } + } + } + + showText(txtDialogTitle, title == null ? DialogX.defaultMessageDialogTitleText : title); + showText(txtDialogTip, message); + showText(btnSelectPositive, okText); + showText(btnSelectNegative, cancelText); + showText(btnSelectOther, otherText); + + txtInput.setText(inputText); + txtInput.setHint(inputHintText); + if (spaceOtherButton != null) { + if (otherText == null) { + spaceOtherButton.setVisibility(View.GONE); + } else { + spaceOtherButton.setVisibility(View.VISIBLE); + } + } + + useTextInfo(txtDialogTitle, titleTextInfo); + useTextInfo(txtDialogTip, messageTextInfo); + useTextInfo(btnSelectPositive, okTextInfo); + useTextInfo(btnSelectNegative, cancelTextInfo); + useTextInfo(btnSelectOther, otherTextInfo); + + if (boxButton != null) { + boxButton.setVisibility((btnSelectNegative != null && btnSelectNegative.getVisibility() == View.VISIBLE) || + (btnSelectOther != null && btnSelectOther.getVisibility() == View.VISIBLE) || + (btnSelectPositive != null && btnSelectPositive.getVisibility() == View.VISIBLE) ? + View.VISIBLE : View.GONE); + } + if (titleIcon != null) { + int size = (int) txtDialogTitle.getTextSize(); + titleIcon.setBounds(0, 0, size, size); + txtDialogTitle.setCompoundDrawablePadding(dip2px(10)); + txtDialogTitle.setCompoundDrawables(titleIcon, null, null, null); + } + + if (inputInfo != null) { + int inputType = inputInfo.getInputType(); + if (inputInfo.getMAX_LENGTH() != -1) { + int inputClass = inputType & InputType.TYPE_MASK_CLASS; + if (inputClass != InputType.TYPE_CLASS_TEXT && + inputClass != InputType.TYPE_CLASS_NUMBER && + inputClass != InputType.TYPE_CLASS_PHONE && + inputClass != InputType.TYPE_CLASS_DATETIME) { + inputType = (inputType & ~InputType.TYPE_MASK_CLASS) | InputType.TYPE_CLASS_TEXT; + } + txtInput.setFilters(new InputFilter[]{new InputFilter.LengthFilter(inputInfo.getMAX_LENGTH())}); + } + if (inputInfo.isMultipleLines()) { + inputType = inputType | InputType.TYPE_TEXT_FLAG_MULTI_LINE; + } + txtInput.setInputType(inputType); + if (inputInfo.getTextInfo() != null) { + useTextInfo(txtInput, inputInfo.getTextInfo()); + } + if (inputInfo.getInputFilters() != null && inputInfo.getInputFilters().length > 0) { + txtInput.setFilters(inputInfo.getInputFilters()); + } + } + + int visibleButtonCount = 0; + if (!isNull(okText)) { + visibleButtonCount = visibleButtonCount + 1; + } + if (!isNull(cancelText)) { + visibleButtonCount = visibleButtonCount + 1; + } + if (!isNull(otherText)) { + visibleButtonCount = visibleButtonCount + 1; + } + + if (splitHorizontal != null) { + splitHorizontal.setBackgroundColor(getColor(style.splitColorRes(isLightTheme()))); + } + + boxButton.setOrientation(buttonOrientation); + if (buttonOrientation == LinearLayout.VERTICAL) { + // 纵向 + if (style.verticalButtonOrder() != null && style.verticalButtonOrder().length != 0) { + boxButton.removeAllViews(); + for (int buttonType : style.verticalButtonOrder()) { + switch (buttonType) { + case DialogXStyle.BUTTON_OK: + boxButton.addView(btnSelectPositive); + if (style.overrideVerticalButtonRes() != null) { + btnSelectPositive.setBackgroundResource(style.overrideVerticalButtonRes().overrideVerticalOkButtonBackgroundRes(visibleButtonCount, isLightTheme())); + } + break; + case DialogXStyle.BUTTON_OTHER: + boxButton.addView(btnSelectOther); + if (style.overrideVerticalButtonRes() != null) { + btnSelectOther.setBackgroundResource(style.overrideVerticalButtonRes().overrideVerticalOtherButtonBackgroundRes(visibleButtonCount, isLightTheme())); + } + break; + case DialogXStyle.BUTTON_CANCEL: + boxButton.addView(btnSelectNegative); + if (style.overrideVerticalButtonRes() != null) { + btnSelectNegative.setBackgroundResource(style.overrideVerticalButtonRes().overrideVerticalCancelButtonBackgroundRes(visibleButtonCount, isLightTheme())); + } + break; + case DialogXStyle.SPACE: + Space space = new Space(getOwnActivity()); + LinearLayout.LayoutParams spaceLp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + spaceLp.weight = 1; + boxButton.addView(space, spaceLp); + break; + case DialogXStyle.SPLIT: + View splitView = new View(getOwnActivity()); + splitView.setBackgroundColor(getResources().getColor(style.splitColorRes(isLightTheme()))); + LinearLayout.LayoutParams viewLp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, style.splitWidthPx()); + boxButton.addView(splitView, viewLp); + break; + } + } + } + } else { + // 横向 + if (style.horizontalButtonOrder() != null && style.horizontalButtonOrder().length != 0) { + boxButton.removeAllViews(); + for (int buttonType : style.horizontalButtonOrder()) { + switch (buttonType) { + case DialogXStyle.BUTTON_OK: + boxButton.addView(btnSelectPositive); + if (style.overrideHorizontalButtonRes() != null) { + btnSelectPositive.setBackgroundResource(style.overrideHorizontalButtonRes().overrideHorizontalOkButtonBackgroundRes(visibleButtonCount, isLightTheme())); + } + break; + case DialogXStyle.BUTTON_OTHER: + boxButton.addView(btnSelectOther); + if (style.overrideHorizontalButtonRes() != null) { + btnSelectOther.setBackgroundResource(style.overrideHorizontalButtonRes().overrideHorizontalOtherButtonBackgroundRes(visibleButtonCount, isLightTheme())); + } + break; + case DialogXStyle.BUTTON_CANCEL: + boxButton.addView(btnSelectNegative); + if (style.overrideHorizontalButtonRes() != null) { + btnSelectNegative.setBackgroundResource(style.overrideHorizontalButtonRes().overrideHorizontalCancelButtonBackgroundRes(visibleButtonCount, isLightTheme())); + } + break; + case DialogXStyle.SPACE: + if (boxButton.getChildCount() >= 1) { + if (boxButton.getChildAt(boxButton.getChildCount() - 1).getVisibility() == View.GONE) { + break; + } + } else { + break; + } + Space space = new Space(getOwnActivity()); + LinearLayout.LayoutParams spaceLp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + spaceLp.weight = 1; + boxButton.addView(space, spaceLp); + break; + case DialogXStyle.SPLIT: + if (boxButton.getChildCount() >= 1) { + if (boxButton.getChildAt(boxButton.getChildCount() - 1).getVisibility() == View.GONE) { + break; + } + } else { + break; + } + View splitView = new View(getOwnActivity()); + splitView.setBackgroundColor(getResources().getColor(style.splitColorRes(isLightTheme()))); + LinearLayout.LayoutParams viewLp = new LinearLayout.LayoutParams(style.splitWidthPx(), ViewGroup.LayoutParams.MATCH_PARENT); + boxButton.addView(splitView, viewLp); + break; + } + } + } + } + + // Events + if (bkgInterceptTouch) { + if (isCancelable()) { + boxRoot.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onBackgroundMaskClickListener == null || !onBackgroundMaskClickListener.onClick(me, v)) { + doDismiss(v); + } + } + }); + } else { + boxRoot.setOnClickListener(null); + } + } else { + boxRoot.setClickable(false); + } + + if (onBindView != null && onBindView.getCustomView() != null) { + onBindView.bindParent(boxCustom, me); + boxCustom.setVisibility(View.VISIBLE); + if (onBindView.getCustomView() instanceof ScrollController) { + if (scrollView instanceof DialogScrollView) { + ((DialogScrollView) scrollView).setVerticalScrollBarEnabled(false); + } + scrollView = (ScrollController) onBindView.getCustomView(); + } else { + View scrollController = onBindView.getCustomView().findViewWithTag("ScrollController"); + if (scrollController instanceof ScrollController) { + if (scrollView instanceof DialogScrollView) { + ((DialogScrollView) scrollView).setVerticalScrollBarEnabled(false); + } + scrollView = (ScrollController) scrollController; + } + } + } else { + boxCustom.setVisibility(View.GONE); + } + onDialogRefreshUI(); + } + + public void doDismiss(View v) { + if (MessageDialog.this.preDismiss(MessageDialog.this)) { + return; + } + if (v != null) v.setEnabled(false); + if (getOwnActivity() == null) return; + + if (!dismissAnimFlag && getDialogXAnimImpl() != null) { + dismissAnimFlag = true; + getDialogXAnimImpl().doExitAnim(MessageDialog.this, bkg); + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (boxRoot != null) { + boxRoot.setVisibility(View.GONE); + } + dismiss(getDialogView()); + } + }, getExitAnimationDuration(null)); + } + } + + protected DialogXAnimInterface getDialogXAnimImpl() { + if (dialogXAnimImpl == null) { + dialogXAnimImpl = new DialogXAnimInterface() { + @Override + public void doShowAnim(MessageDialog dialog, ViewGroup dialogBodyView) { + int enterAnimResId = style.enterAnimResId() == 0 ? R.anim.anim_dialogx_default_enter : style.enterAnimResId(); + if (overrideEnterAnimRes != 0) { + enterAnimResId = overrideEnterAnimRes; + } + if (customEnterAnimResId != 0) { + enterAnimResId = customEnterAnimResId; + } + Animation enterAnim = AnimationUtils.loadAnimation(getOwnActivity(), enterAnimResId); + long enterAnimationDuration = getEnterAnimationDuration(enterAnim); + enterAnim.setDuration(enterAnimationDuration); + enterAnim.setInterpolator(new DecelerateInterpolator()); + bkg.startAnimation(enterAnim); + + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f); + bkgAlpha.setDuration(enterAnimationDuration); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + }); + bkgAlpha.start(); + } + + @Override + public void doExitAnim(MessageDialog dialog, ViewGroup dialogBodyView) { + int exitAnimResId = style.exitAnimResId() == 0 ? R.anim.anim_dialogx_default_exit : style.exitAnimResId(); + if (overrideExitAnimRes != 0) { + exitAnimResId = overrideExitAnimRes; + } + if (customExitAnimResId != 0) { + exitAnimResId = customExitAnimResId; + } + Animation exitAnim = AnimationUtils.loadAnimation(getOwnActivity(), exitAnimResId); + long exitAnimationDuration = getExitAnimationDuration(exitAnim); + exitAnim.setInterpolator(new AccelerateInterpolator()); + exitAnim.setDuration(exitAnimationDuration); + bkg.startAnimation(exitAnim); + + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f); + bkgAlpha.setDuration(exitAnimationDuration); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + }); + bkgAlpha.start(); + } + }; + } + return dialogXAnimImpl; + } + + public long getExitAnimationDuration(@Nullable Animation defaultExitAnim) { + if (defaultExitAnim == null && bkg.getAnimation() != null) { + defaultExitAnim = bkg.getAnimation(); + } + long exitAnimDurationTemp = (defaultExitAnim == null || defaultExitAnim.getDuration() == 0) ? 300 : defaultExitAnim.getDuration(); + if (overrideExitDuration >= 0) { + exitAnimDurationTemp = overrideExitDuration; + } + if (exitAnimDuration != -1) { + exitAnimDurationTemp = exitAnimDuration; + } + return exitAnimDurationTemp; + } + + public long getEnterAnimationDuration(@Nullable Animation defaultEnterAnim) { + if (defaultEnterAnim == null && bkg.getAnimation() != null) { + defaultEnterAnim = bkg.getAnimation(); + } + long enterAnimDurationTemp = (defaultEnterAnim == null || defaultEnterAnim.getDuration() == 0) ? 300 : defaultEnterAnim.getDuration(); + if (overrideEnterDuration >= 0) { + enterAnimDurationTemp = overrideEnterDuration; + } + if (enterAnimDuration >= 0) { + enterAnimDurationTemp = enterAnimDuration; + } + return enterAnimDurationTemp; + } + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + public void dismiss() { + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl == null) return; + dialogImpl.doDismiss(dialogImpl.bkg); + } + }); + } + + public DialogLifecycleCallback getDialogLifecycleCallback() { + return dialogLifecycleCallback == null ? new DialogLifecycleCallback() { + } : dialogLifecycleCallback; + } + + public MessageDialog setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me); + return this; + } + + public MessageDialog setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public MessageDialog setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public CharSequence getOkButton() { + return okText; + } + + public MessageDialog setOkButton(CharSequence okText) { + this.okText = okText; + refreshUI(); + return this; + } + + public MessageDialog setOkButton(int okTextRedId) { + this.okText = getString(okTextRedId); + refreshUI(); + return this; + } + + public MessageDialog setOkButton(OnDialogButtonClickListener okButtonClickListener) { + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public MessageDialog setOkButton(CharSequence okText, OnDialogButtonClickListener okButtonClickListener) { + this.okText = okText; + this.okButtonClickListener = okButtonClickListener; + refreshUI(); + return this; + } + + public MessageDialog setOkButton(int okTextRedId, OnDialogButtonClickListener okButtonClickListener) { + this.okText = getString(okTextRedId); + this.okButtonClickListener = okButtonClickListener; + refreshUI(); + return this; + } + + public CharSequence getCancelButton() { + return cancelText; + } + + public MessageDialog setCancelButton(CharSequence cancelText) { + this.cancelText = cancelText; + refreshUI(); + return this; + } + + public MessageDialog setCancelButton(int cancelTextResId) { + this.cancelText = getString(cancelTextResId); + refreshUI(); + return this; + } + + public MessageDialog setCancelButton(OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + public MessageDialog setCancelButton(CharSequence cancelText, OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelText = cancelText; + this.cancelButtonClickListener = cancelButtonClickListener; + refreshUI(); + return this; + } + + public MessageDialog setCancelButton(int cancelTextResId, OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelText = getString(cancelTextResId); + this.cancelButtonClickListener = cancelButtonClickListener; + refreshUI(); + return this; + } + + public CharSequence getOtherButton() { + return otherText; + } + + public MessageDialog setOtherButton(CharSequence otherText) { + this.otherText = otherText; + refreshUI(); + return this; + } + + public MessageDialog setOtherButton(int otherTextResId) { + this.otherText = getString(otherTextResId); + refreshUI(); + return this; + } + + public MessageDialog setOtherButton(OnDialogButtonClickListener otherButtonClickListener) { + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + public MessageDialog setOtherButton(CharSequence otherText, OnDialogButtonClickListener otherButtonClickListener) { + this.otherText = otherText; + this.otherButtonClickListener = otherButtonClickListener; + refreshUI(); + return this; + } + + public MessageDialog setOtherButton(int otherTextResId, OnDialogButtonClickListener otherButtonClickListener) { + this.otherText = getString(otherTextResId); + this.otherButtonClickListener = otherButtonClickListener; + refreshUI(); + return this; + } + + public OnDialogButtonClickListener getOkButtonClickListener() { + return (OnDialogButtonClickListener) okButtonClickListener; + } + + public MessageDialog setOkButtonClickListener(OnDialogButtonClickListener okButtonClickListener) { + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public OnDialogButtonClickListener getCancelButtonClickListener() { + return (OnDialogButtonClickListener) cancelButtonClickListener; + } + + public MessageDialog setCancelButtonClickListener(OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + public OnDialogButtonClickListener getOtherButtonClickListener() { + return (OnDialogButtonClickListener) otherButtonClickListener; + } + + public MessageDialog setOtherButtonClickListener(OnDialogButtonClickListener otherButtonClickListener) { + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + public CharSequence getTitle() { + return title; + } + + public MessageDialog setTitle(CharSequence title) { + this.title = title; + refreshUI(); + return this; + } + + public MessageDialog setTitle(int titleResId) { + this.title = getString(titleResId); + refreshUI(); + return this; + } + + public CharSequence getMessage() { + return message; + } + + public MessageDialog setMessage(CharSequence message) { + this.message = message; + refreshUI(); + return this; + } + + public MessageDialog setMessage(int messageResId) { + this.message = getString(messageResId); + refreshUI(); + return this; + } + + public TextInfo getTitleTextInfo() { + return titleTextInfo; + } + + public MessageDialog setTitleTextInfo(TextInfo titleTextInfo) { + this.titleTextInfo = titleTextInfo; + refreshUI(); + return this; + } + + public TextInfo getMessageTextInfo() { + return messageTextInfo; + } + + public MessageDialog setMessageTextInfo(TextInfo messageTextInfo) { + this.messageTextInfo = messageTextInfo; + refreshUI(); + return this; + } + + public TextInfo getOkTextInfo() { + return okTextInfo; + } + + public MessageDialog setOkTextInfo(TextInfo okTextInfo) { + this.okTextInfo = okTextInfo; + refreshUI(); + return this; + } + + public MessageDialog setHapticFeedbackEnabled(boolean isHapticFeedbackEnabled) { + this.isHapticFeedbackEnabled = isHapticFeedbackEnabled ? 1 : 0; + return this; + } + + public TextInfo getCancelTextInfo() { + return cancelTextInfo; + } + + public MessageDialog setCancelTextInfo(TextInfo cancelTextInfo) { + this.cancelTextInfo = cancelTextInfo; + refreshUI(); + return this; + } + + public TextInfo getOtherTextInfo() { + return otherTextInfo; + } + + public MessageDialog setOtherTextInfo(TextInfo otherTextInfo) { + this.otherTextInfo = otherTextInfo; + refreshUI(); + return this; + } + + public int getButtonOrientation() { + return buttonOrientation; + } + + public MessageDialog setButtonOrientation(int buttonOrientation) { + this.buttonOrientation = buttonOrientation; + refreshUI(); + return this; + } + + public boolean isCancelable() { + if (privateCancelable != null) { + return privateCancelable == BOOLEAN.TRUE; + } + if (overrideCancelable != null) { + return overrideCancelable == BOOLEAN.TRUE; + } + return cancelable; + } + + public MessageDialog setCancelable(boolean cancelable) { + this.privateCancelable = cancelable ? BOOLEAN.TRUE : BOOLEAN.FALSE; + refreshUI(); + return this; + } + + public OnBackPressedListener getOnBackPressedListener() { + return (OnBackPressedListener) onBackPressedListener; + } + + public MessageDialog setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + return this; + } + + public DialogImpl getDialogImpl() { + return dialogImpl; + } + + public MessageDialog setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + refreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) return null; + return onBindView.getCustomView(); + } + + public MessageDialog removeCustomView() { + this.onBindView.clean(); + refreshUI(); + return this; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public MessageDialog setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + refreshUI(); + return this; + } + + public String getInputText() { + if (dialogImpl.txtInput != null) { + return dialogImpl.txtInput.getText().toString(); + } else { + return ""; + } + } + + public MessageDialog setBackgroundColorRes(@ColorRes int backgroundColorResId) { + this.backgroundColor = getColor(backgroundColorResId); + refreshUI(); + return this; + } + + public MessageDialog setMaskColor(@ColorInt int maskColor) { + this.maskColor = maskColor; + refreshUI(); + return this; + } + + public long getEnterAnimDuration() { + return enterAnimDuration; + } + + public MessageDialog setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public long getExitAnimDuration() { + return exitAnimDuration; + } + + public MessageDialog setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + @Override + public void restartDialog() { + if (getDialogView() != null) { + dismiss(getDialogView()); + isShow = false; + } + if (getDialogImpl().boxCustom != null) { + getDialogImpl().boxCustom.removeAllViews(); + } + if (getDialogImpl().boxList != null) { + getDialogImpl().boxList.removeAllViews(); + } + int layoutId = style.layout(isLightTheme()); + layoutId = layoutId == 0 ? (isLightTheme() ? R.layout.layout_dialogx_material : R.layout.layout_dialogx_material_dark) : layoutId; + + enterAnimDuration = 0; + View dialogView = createView(layoutId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } + + public void hide() { + isHide = true; + hideWithExitAnim = false; + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + + protected boolean hideWithExitAnim; + + public void hideWithExitAnim() { + hideWithExitAnim = true; + isHide = true; + if (getDialogImpl() != null) { + getDialogImpl().getDialogXAnimImpl().doExitAnim(me, getDialogImpl().bkg); + + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + }, getDialogImpl().getExitAnimationDuration(null)); + } + } + + public MessageDialog setAnimResId(int enterResId, int exitResId) { + customEnterAnimResId = enterResId; + customExitAnimResId = exitResId; + return this; + } + + public MessageDialog setEnterAnimResId(int enterResId) { + customEnterAnimResId = enterResId; + return this; + } + + public MessageDialog setExitAnimResId(int exitResId) { + customExitAnimResId = exitResId; + return this; + } + + @Override + protected void shutdown() { + dismiss(); + } + + public MessageDialog setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + refreshUI(); + return this; + } + + public MessageDialog setMaxHeight(int maxHeight) { + this.maxHeight = maxHeight; + refreshUI(); + return this; + } + + public MessageDialog setMinHeight(int minHeight) { + this.minHeight = minHeight; + refreshUI(); + return this; + } + + public MessageDialog setMinWidth(int minWidth) { + this.minWidth = minWidth; + refreshUI(); + return this; + } + + public MessageDialog setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public boolean isBkgInterceptTouch() { + return bkgInterceptTouch; + } + + public MessageDialog setBkgInterceptTouch(boolean bkgInterceptTouch) { + this.bkgInterceptTouch = bkgInterceptTouch; + return this; + } + + public OnBackgroundMaskClickListener getOnBackgroundMaskClickListener() { + return (OnBackgroundMaskClickListener) onBackgroundMaskClickListener; + } + + public MessageDialog setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + return this; + } + + public MessageDialog setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public float getRadius() { + return backgroundRadius; + } + + public Drawable getTitleIcon() { + return titleIcon; + } + + public MessageDialog setTitleIcon(Bitmap titleIcon) { + this.titleIcon = new BitmapDrawable(getResources(), titleIcon); + refreshUI(); + return this; + } + + public MessageDialog setTitleIcon(int titleIconResId) { + this.titleIcon = getResources().getDrawable(titleIconResId); + refreshUI(); + return this; + } + + public MessageDialog setTitleIcon(Drawable titleIcon) { + this.titleIcon = titleIcon; + refreshUI(); + return this; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public MessageDialog setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public MessageDialog setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public MessageDialog setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + public BUTTON_SELECT_RESULT getButtonSelectResult() { + return buttonSelectResult; + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new MessageDialog() { + * + * @param dialog self + * @Override public void onShow(MessageDialog dialog) { + * //... + * } + * } + */ + protected void onShow(MessageDialog dialog) { + + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new MessageDialog() { + * + * @param dialog self + * @Override public boolean onDismiss(MessageDialog dialog) { + * WaitDialog.show("Please Wait..."); + * if (dialog.getButtonSelectResult() == BUTTON_SELECT_RESULT.BUTTON_OK) { + * //点击了OK的情况 + * //... + * } else { + * //其他按钮点击、对话框dismiss的情况 + * //... + * } + * return false; + * } + * } + */ + // 用于使用 new 构建实例时,override 的生命周期事件 + protected void onDismiss(MessageDialog dialog) { + + } + + public MessageDialog setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public MessageDialog onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public MessageDialog onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public MessageDialog setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public MessageDialog appendMessage(CharSequence message) { + this.message = TextUtils.concat(this.message, message); + refreshUI(); + return this; + } + + public MessageDialog setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex(" + orderIndex + ") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public MessageDialog bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public MessageDialog setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public MessageDialog cleanAction(int actionId){ + dialogActionRunnableMap.remove(actionId); + return this; + } + + public MessageDialog cleanAllAction(){ + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss(){ + dismiss(); + } + + public MessageDialog bindDismissWithLifecycleOwner(LifecycleOwner owner){ + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/MessageMenu.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/MessageMenu.java new file mode 100644 index 0000000..ab51bf5 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/MessageMenu.java @@ -0,0 +1,1601 @@ +package com.kongzue.dialogx.dialogs; + +import static android.view.View.OVER_SCROLL_NEVER; + +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.text.TextUtils; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.LinearLayout; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BottomMenuListViewTouchEvent; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.MenuItemLayoutRefreshCallback; +import com.kongzue.dialogx.interfaces.MenuItemTextInfoInterceptor; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.OnMenuButtonClickListener; +import com.kongzue.dialogx.interfaces.OnDialogButtonClickListener; +import com.kongzue.dialogx.interfaces.OnIconChangeCallBack; +import com.kongzue.dialogx.interfaces.OnMenuItemClickListener; +import com.kongzue.dialogx.interfaces.OnMenuItemSelectListener; +import com.kongzue.dialogx.interfaces.SELECT_MODE; +import com.kongzue.dialogx.util.BottomMenuArrayAdapter; +import com.kongzue.dialogx.util.ItemDivider; +import com.kongzue.dialogx.util.MessageMenuArrayAdapter; +import com.kongzue.dialogx.util.TextInfo; +import com.kongzue.dialogx.util.views.DialogListView; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class MessageMenu extends MessageDialog { + + protected MessageMenu me = this; + protected boolean allowInterceptTouch = true; + protected int selectionIndex = -1; + protected SELECT_MODE selectMode = SELECT_MODE.NONE; + protected ArrayList selectionItems; + + protected boolean showSelectedBackgroundTips = false; + protected MenuItemLayoutRefreshCallback menuMenuItemLayoutRefreshCallback; + protected Map menuUsability = new HashMap(); + protected ItemDivider itemDivider; + + protected OnMenuItemClickListener onMenuItemClickListener; + + public static MessageMenu build() { + return new MessageMenu(); + } + + public static MessageMenu build(DialogXStyle style) { + return new MessageMenu().setStyle(style); + } + + public static MessageMenu build(OnBindView onBindView) { + return new MessageMenu().setCustomView(onBindView); + } + + protected MessageMenu() { + super(); + } + + protected OnIconChangeCallBack onIconChangeCallBack; + protected MenuItemTextInfoInterceptor menuItemTextInfoInterceptor; + protected DialogListView listView; + protected TextInfo menuTextInfo; + protected BaseAdapter menuListAdapter; + protected List menuList; + protected List iconResIds; + protected boolean autoTintIconInLightOrDarkMode = true; + + public static MessageMenu show(List menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(List menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu showStringList(List menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.setMenuStringList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu showStringList(List menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.setMenuStringList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(String... menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, CharSequence message, List menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, CharSequence message, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, List menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu showStringList(CharSequence title, CharSequence message, List menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuStringList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu showStringList(CharSequence title, CharSequence message, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuStringList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, CharSequence message, String[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, CharSequence message, String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, CharSequence message, CharSequence[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, CharSequence message, CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(String title, String message, List menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(String title, String message, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu showStringList(String title, String message, List menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuStringList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu showStringList(String title, String message, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuStringList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(String title, String message, String[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(String title, String message, String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(String title, String message, CharSequence[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(String title, String message, CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.message = message; + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, int messageResId, List menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.message = messageMenu.getString(messageResId); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, List menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu showStringList(int titleResId, int messageResId, List menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.message = messageMenu.getString(messageResId); + messageMenu.setMenuStringList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, int messageResId, String[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.message = messageMenu.getString(messageResId); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, int messageResId, CharSequence[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.message = messageMenu.getString(messageResId); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, int messageResId, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.message = messageMenu.getString(messageResId); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu showStringList(int titleResId, int messageResId, List menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.message = messageMenu.getString(messageResId); + messageMenu.setMenuStringList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, int messageResId, String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.message = messageMenu.getString(messageResId); + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, int messageResId, CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.message = messageMenu.getString(messageResId); + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, CharSequence[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, String[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(CharSequence title, String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = title; + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, CharSequence[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, String[] menuList) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.setMenuList(menuList); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, CharSequence[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + public static MessageMenu show(int titleResId, String[] menuList, OnMenuItemClickListener onMenuItemClickListener) { + MessageMenu messageMenu = new MessageMenu(); + messageMenu.title = messageMenu.getString(titleResId); + messageMenu.setMenuList(menuList); + messageMenu.setOnMenuItemClickListener(onMenuItemClickListener); + messageMenu.show(); + return messageMenu; + } + + + private float touchDownY; + + public static final int ITEM_CLICK_DELAY = 100; + private long lastClickTime = 0; + private int[] resultArray; + private CharSequence[] selectTextArray; + + @Override + protected void onDialogShow() { + if (getDialogImpl() != null) { + getDialogImpl().boxList.setVisibility(View.VISIBLE); + + if (!isAllowInterceptTouch()) { + getDialogImpl().bkg.setMaxHeight(maxHeight); + if (maxHeight != 0) { + dialogImpl.scrollView.lockScroll(true); + } + } + + int dividerDrawableResId = 0; + int dividerHeight = 1; + if (style.overrideBottomDialogRes() != null) { + dividerDrawableResId = style.overrideBottomDialogRes().overrideMenuDividerDrawableRes(isLightTheme()); + dividerHeight = style.overrideBottomDialogRes().overrideMenuDividerHeight(isLightTheme()); + } + if (dividerDrawableResId == 0) { + dividerDrawableResId = isLightTheme() ? R.drawable.rect_dialogx_material_menu_split_divider : R.drawable.rect_dialogx_material_menu_split_divider_night; + } + + if (!isLightTheme()) { + listView = new DialogListView(getDialogImpl(), getOwnActivity(), R.style.DialogXCompatThemeDark); + } else { + listView = new DialogListView(getDialogImpl(), getOwnActivity()); + } + listView.setOverScrollMode(OVER_SCROLL_NEVER); + listView.setDivider(getResources().getDrawable(dividerDrawableResId)); + listView.setDividerHeight(dividerHeight); + + listView.setBottomMenuListViewTouchEvent(new BottomMenuListViewTouchEvent() { + @Override + public void down(MotionEvent event) { + touchDownY = getDialogImpl().bkg.getY(); + } + }); + + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (!isMenuItemEnable(position)) { + return; + } + haptic(view); + long currentTime = System.currentTimeMillis(); + if (currentTime - lastClickTime > ITEM_CLICK_DELAY) { + lastClickTime = currentTime; + float deltaY = Math.abs(touchDownY - getDialogImpl().bkg.getY()); + if (deltaY > dip2px(15)) { + return; + } + selectionIndex = position; + switch (selectMode) { + case NONE: + if (onMenuItemClickListener != null) { + if (!onMenuItemClickListener.onClick(me, menuList.get(position), position)) { + dismiss(); + } + } else { + dismiss(); + } + break; + case SINGLE: + if (onMenuItemClickListener instanceof OnMenuItemSelectListener) { + OnMenuItemSelectListener onMenuItemSelectListener = (OnMenuItemSelectListener) onMenuItemClickListener; + if (!onMenuItemSelectListener.onClick(me, menuList.get(position), position)) { + dismiss(); + } else { + menuListAdapter.notifyDataSetInvalidated(); + onMenuItemSelectListener.onOneItemSelect(me, menuList.get(position), position, true); + } + } else { + if (onMenuItemClickListener != null) { + if (!onMenuItemClickListener.onClick(me, menuList.get(position), position)) { + dismiss(); + } + } else { + menuListAdapter.notifyDataSetInvalidated(); + } + } + break; + case MULTIPLE: + if (onMenuItemClickListener instanceof OnMenuItemSelectListener) { + OnMenuItemSelectListener onMenuItemSelectListener = (OnMenuItemSelectListener) onMenuItemClickListener; + if (!onMenuItemSelectListener.onClick(me, menuList.get(position), position)) { + dismiss(); + } else { + if (selectionItems.contains(position)) { + selectionItems.remove(new Integer(position)); + } else { + selectionItems.add(position); + } + menuListAdapter.notifyDataSetInvalidated(); + resultArray = new int[selectionItems.size()]; + selectTextArray = new CharSequence[selectionItems.size()]; + for (int i = 0; i < selectionItems.size(); i++) { + resultArray[i] = selectionItems.get(i); + selectTextArray[i] = menuList.get(resultArray[i]); + } + onMenuItemSelectListener.onMultiItemSelect(me, selectTextArray, resultArray); + } + } else { + if (onMenuItemClickListener != null) { + if (!onMenuItemClickListener.onClick(me, menuList.get(position), position)) { + dismiss(); + } + } else { + if (selectionItems.contains(position)) { + selectionItems.remove(new Integer(position)); + } else { + selectionItems.add(position); + } + menuListAdapter.notifyDataSetInvalidated(); + resultArray = new int[selectionItems.size()]; + selectTextArray = new CharSequence[selectionItems.size()]; + for (int i = 0; i < selectionItems.size(); i++) { + resultArray[i] = selectionItems.get(i); + selectTextArray[i] = menuList.get(resultArray[i]); + } + } + } + break; + } + } + } + }); + if (style.overrideBottomDialogRes() != null) { + if (style.overrideBottomDialogRes().overrideMenuItemLayout(true, 0, 0, false) != 0) { + listView.setSelector(R.color.empty); + } + } + + ViewGroup.LayoutParams listViewLp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + getDialogImpl().boxList.addView(listView, listViewLp); + refreshUI(); + } + } + + @Override + public void refreshUI() { + if (getDialogImpl() == null) return; + if (listView != null) { + if (menuListAdapter == null) { + menuListAdapter = new MessageMenuArrayAdapter(me, getOwnActivity(), menuList); + } + if (listView.getAdapter() == null) { + listView.setAdapter(menuListAdapter); + } else { + if (listView.getAdapter() != menuListAdapter) { + listView.setAdapter(menuListAdapter); + } else { + menuListAdapter.notifyDataSetChanged(); + } + } + } + + // 部分主题下选中项默认按下效果 + if (showSelectedBackgroundTips) { + if (listView != null) { + listView.post(new Runnable() { + @Override + public void run() { + if (menuListAdapter instanceof BottomMenuArrayAdapter && showSelectedBackgroundTips) { + BottomMenuArrayAdapter bottomMenuArrayAdapter = ((BottomMenuArrayAdapter) menuListAdapter); + + View selectItemView = listView.getChildAt(getSelection()); + if (selectItemView != null) { + selectItemView.post(new Runnable() { + @Override + public void run() { + selectItemView.setPressed(true); + } + }); + } + } + } + }); + } + + } + + if (itemDivider != null && listView != null) { + listView.setDivider(itemDivider.createDividerDrawable(getOwnActivity(), isLightTheme())); + listView.setDividerHeight(itemDivider.getWidth()); + } + super.refreshUI(); + } + + public void preRefreshUI() { + if (getDialogImpl() == null) return; + runOnMain(new Runnable() { + @Override + public void run() { + refreshUI(); + } + }); + } + + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + public List getMenuList() { + return menuList; + } + + public MessageMenu setMenuList(List menuList) { + this.menuList = menuList; + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + private boolean isSameSize(int menuListSize) { + if (this.menuList == null || this.menuList.size() == 0) { + return true; + } + return this.menuList.size() == menuListSize; + } + + public MessageMenu setMenuStringList(List menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(menuList); + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + public MessageMenu setMenuList(String[] menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + public MessageMenu setMenuList(CharSequence[] menuList) { + this.menuList = Arrays.asList(menuList); + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + public MessageMenu setMenus(CharSequence... menuList) { + this.menuList = Arrays.asList(menuList); + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + public MessageMenu setMenus(String... menuList) { + this.menuList = Arrays.asList(menuList); + this.menuListAdapter = null; + preRefreshUI(); + return this; + } + + public OnIconChangeCallBack getOnIconChangeCallBack() { + return onIconChangeCallBack; + } + + public MessageMenu setOnIconChangeCallBack(OnIconChangeCallBack onIconChangeCallBack) { + this.onIconChangeCallBack = onIconChangeCallBack; + return this; + } + + public OnBackPressedListener getOnBackPressedListener() { + return (OnBackPressedListener) onBackPressedListener; + } + + public MessageMenu setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + preRefreshUI(); + return this; + } + + public MessageMenu setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me); + return this; + } + + public MessageMenu setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public MessageMenu setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public boolean isCancelable() { + if (privateCancelable != null) { + return privateCancelable == BOOLEAN.TRUE; + } + if (overrideCancelable != null) { + return overrideCancelable == BOOLEAN.TRUE; + } + return cancelable; + } + + public MessageMenu setCancelable(boolean cancelable) { + this.privateCancelable = cancelable ? BOOLEAN.TRUE : BOOLEAN.FALSE; + preRefreshUI(); + return this; + } + + public MessageDialog.DialogImpl getDialogImpl() { + return dialogImpl; + } + + public CharSequence getTitle() { + return title; + } + + public MessageMenu setTitle(CharSequence title) { + this.title = title; + preRefreshUI(); + return this; + } + + public MessageMenu setTitle(int titleResId) { + this.title = getString(titleResId); + preRefreshUI(); + return this; + } + + public CharSequence getMessage() { + return message; + } + + public MessageMenu setMessage(CharSequence message) { + this.message = message; + preRefreshUI(); + return this; + } + + public MessageMenu setMessage(int messageResId) { + this.message = getString(messageResId); + preRefreshUI(); + return this; + } + + public CharSequence getCancelButton() { + return cancelText; + } + + public MessageMenu setCancelButton(CharSequence cancelText) { + this.cancelText = cancelText; + preRefreshUI(); + return this; + } + + public MessageMenu setCancelButton(int cancelTextResId) { + this.cancelText = getString(cancelTextResId); + preRefreshUI(); + return this; + } + + public MessageMenu setCancelButton(OnMenuButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + public MessageMenu setCancelButton(CharSequence cancelText, OnMenuButtonClickListener cancelButtonClickListener) { + this.cancelText = cancelText; + this.cancelButtonClickListener = cancelButtonClickListener; + preRefreshUI(); + return this; + } + + public MessageMenu setCancelButton(int cancelTextResId, OnMenuButtonClickListener cancelButtonClickListener) { + this.cancelText = getString(cancelTextResId); + this.cancelButtonClickListener = cancelButtonClickListener; + preRefreshUI(); + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.MessageMenu#setCancelButton(OnMenuButtonClickListener)} + */ + @Deprecated + public MessageMenu setCancelButton(OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.MessageMenu#setCancelButton(CharSequence cancelText, OnMenuButtonClickListener)} + */ + @Deprecated + public MessageMenu setCancelButton(CharSequence cancelText, OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelText = cancelText; + this.cancelButtonClickListener = cancelButtonClickListener; + preRefreshUI(); + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.MessageMenu#setCancelButton(int cancelTextResId, OnMenuButtonClickListener)} + */ + @Deprecated + public MessageMenu setCancelButton(int cancelTextResId, OnDialogButtonClickListener cancelButtonClickListener) { + this.cancelText = getString(cancelTextResId); + this.cancelButtonClickListener = cancelButtonClickListener; + preRefreshUI(); + return this; + } + + public MessageMenu setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + preRefreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) return null; + return onBindView.getCustomView(); + } + + public MessageMenu removeCustomView() { + this.onBindView.clean(); + preRefreshUI(); + return this; + } + + public boolean isAllowInterceptTouch() { + if (style.overrideBottomDialogRes() == null) { + return false; + } else { + return allowInterceptTouch && style.overrideBottomDialogRes().touchSlide(); + } + } + + public MessageMenu setAllowInterceptTouch(boolean allowInterceptTouch) { + this.allowInterceptTouch = allowInterceptTouch; + preRefreshUI(); + return this; + } + + public float getMessageDialogMaxHeight() { + return maxHeight; + } + + public MessageMenu setMessageDialogMaxHeight(float dialogMaxHeight) { + this.maxHeight = (int) dialogMaxHeight; + return this; + } + + public OnMenuItemClickListener getOnMenuItemClickListener() { + return onMenuItemClickListener; + } + + public MessageMenu setOnMenuItemClickListener(OnMenuItemClickListener onMenuItemClickListener) { + this.onMenuItemClickListener = onMenuItemClickListener; + return this; + } + + public BaseAdapter getMenuListAdapter() { + return menuListAdapter; + } + + public MessageMenu setMenuListAdapter(BaseAdapter menuListAdapter) { + this.menuListAdapter = menuListAdapter; + return this; + } + + public OnMenuButtonClickListener getMessageMenuCancelButtonClickListener() { + return (OnMenuButtonClickListener) cancelButtonClickListener; + } + + public MessageMenu setCancelButtonClickListener(OnMenuButtonClickListener cancelButtonClickListener) { + this.cancelButtonClickListener = cancelButtonClickListener; + return this; + } + + public TextInfo getTitleTextInfo() { + return titleTextInfo; + } + + public MessageMenu setTitleTextInfo(TextInfo titleTextInfo) { + this.titleTextInfo = titleTextInfo; + preRefreshUI(); + return this; + } + + public TextInfo getMessageTextInfo() { + return messageTextInfo; + } + + public MessageMenu setMessageTextInfo(TextInfo messageTextInfo) { + this.messageTextInfo = messageTextInfo; + preRefreshUI(); + return this; + } + + public TextInfo getCancelTextInfo() { + return cancelTextInfo; + } + + public MessageMenu setCancelTextInfo(TextInfo cancelTextInfo) { + this.cancelTextInfo = cancelTextInfo; + preRefreshUI(); + return this; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public MessageMenu setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + preRefreshUI(); + return this; + } + + public int getSelection() { + return selectionIndex; + } + + public ArrayList getSelectionList() { + return selectionItems; + } + + public MessageMenu setSelection(int selectionIndex) { + this.selectMode = SELECT_MODE.SINGLE; + this.selectionIndex = selectionIndex; + this.selectionItems = null; + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public MessageMenu setSingleSelection() { + this.selectMode = SELECT_MODE.SINGLE; + this.selectionIndex = -1; + this.selectionItems = null; + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public MessageMenu setSelection(int[] selectionItems) { + this.selectMode = SELECT_MODE.MULTIPLE; + this.selectionIndex = -1; + this.selectionItems = new ArrayList<>(); + if (selectionItems != null) { + for (int itemIndex : selectionItems) { + this.selectionItems.add(itemIndex); + } + } + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public MessageMenu setMultiSelection() { + this.selectMode = SELECT_MODE.MULTIPLE; + this.selectionIndex = -1; + this.selectionItems = new ArrayList<>(); + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public MessageMenu setSelection(List selectionItems) { + this.selectMode = SELECT_MODE.MULTIPLE; + this.selectionIndex = -1; + this.selectionItems = new ArrayList<>(selectionItems); + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public MessageMenu setNoSelect() { + this.selectMode = SELECT_MODE.NONE; + this.selectionIndex = -1; + this.selectionItems = null; + menuListAdapter = null; + preRefreshUI(); + return this; + } + + public MessageMenu setBackgroundColorRes(@ColorRes int backgroundRes) { + this.backgroundColor = getColor(backgroundRes); + preRefreshUI(); + return this; + } + + public CharSequence getOkButton() { + return okText; + } + + public MessageMenu setOkButton(CharSequence okText) { + this.okText = okText; + preRefreshUI(); + return this; + } + + public MessageMenu setOkButton(int OkTextResId) { + this.okText = getString(OkTextResId); + preRefreshUI(); + return this; + } + + public MessageMenu setOkButton(OnMenuButtonClickListener okButtonClickListener) { + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public MessageMenu setOkButton(CharSequence okText, OnMenuButtonClickListener okButtonClickListener) { + this.okText = okText; + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public MessageMenu setOkButton(int okTextResId, OnMenuButtonClickListener okButtonClickListener) { + this.okText = getString(okTextResId); + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public MessageMenu setHapticFeedbackEnabled(boolean isHapticFeedbackEnabled) { + this.isHapticFeedbackEnabled = isHapticFeedbackEnabled ? 1 : 0; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.MessageMenu#setOkButton(OnMenuButtonClickListener)} + */ + @Deprecated + public MessageMenu setOkButton(OnDialogButtonClickListener okButtonClickListener) { + this.okButtonClickListener = okButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.MessageMenu#setOkButton(CharSequence okText, OnMenuButtonClickListener)} + */ + @Deprecated + public MessageMenu setOkButton(CharSequence okText, OnDialogButtonClickListener okButtonClickListener) { + this.okText = okText; + this.okButtonClickListener = okButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.MessageMenu#setOkButton(int okTextResId, OnMenuButtonClickListener)} + */ + @Deprecated + public MessageMenu setOkButton(int okTextResId, OnDialogButtonClickListener okButtonClickListener) { + this.okText = getString(okTextResId); + this.okButtonClickListener = okButtonClickListener; + return this; + } + + public CharSequence getOtherButton() { + return otherText; + } + + public MessageMenu setOtherButton(CharSequence otherText) { + this.otherText = otherText; + preRefreshUI(); + return this; + } + + public MessageMenu setOtherButton(int OtherTextResId) { + this.otherText = getString(OtherTextResId); + preRefreshUI(); + return this; + } + + public MessageMenu setOtherButton(OnMenuButtonClickListener otherButtonClickListener) { + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + public MessageMenu setOtherButton(CharSequence otherText, OnMenuButtonClickListener otherButtonClickListener) { + this.otherText = otherText; + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + public MessageMenu setOtherButton(int otherTextResId, OnMenuButtonClickListener otherButtonClickListener) { + this.otherText = getString(otherTextResId); + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.MessageMenu#setOtherButton(OnMenuButtonClickListener)} + */ + @Deprecated + public MessageMenu setOtherButton(OnDialogButtonClickListener otherButtonClickListener) { + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.MessageMenu#setOtherButton(CharSequence otherText, OnMenuButtonClickListener)} + */ + @Deprecated + public MessageMenu setOtherButton(CharSequence otherText, OnDialogButtonClickListener otherButtonClickListener) { + this.otherText = otherText; + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + /** + * 建议使用 {@link com.kongzue.dialogx.dialogs.MessageMenu#setOtherButton(int otherTextResId, OnMenuButtonClickListener)} + */ + @Deprecated + public MessageMenu setOtherButton(int otherTextResId, OnDialogButtonClickListener otherButtonClickListener) { + this.otherText = getString(otherTextResId); + this.otherButtonClickListener = otherButtonClickListener; + return this; + } + + public MessageMenu setMaskColor(@ColorInt int maskColor) { + this.maskColor = maskColor; + preRefreshUI(); + return this; + } + + public long getEnterAnimDuration() { + return enterAnimDuration; + } + + public MessageMenu setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public long getExitAnimDuration() { + return exitAnimDuration; + } + + public MessageMenu setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + public SELECT_MODE getSelectMode() { + return selectMode; + } + + @Override + protected void shutdown() { + dismiss(); + } + + public MessageMenu setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + refreshUI(); + return this; + } + + public MessageMenu setMaxHeight(int maxHeight) { + this.maxHeight = maxHeight; + refreshUI(); + return this; + } + + public MessageMenu setMinHeight(int minHeight) { + this.minHeight = minHeight; + refreshUI(); + return this; + } + + public MessageMenu setMinWidth(int minWidth) { + this.minWidth = minWidth; + refreshUI(); + return this; + } + + public MessageMenu setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public TextInfo getMenuTextInfo() { + if (menuTextInfo == null) return DialogX.menuTextInfo; + return menuTextInfo; + } + + public MessageMenu setMenuTextInfo(TextInfo menuTextInfo) { + this.menuTextInfo = menuTextInfo; + return this; + } + + public MenuItemTextInfoInterceptor getMenuItemTextInfoInterceptor() { + return menuItemTextInfoInterceptor; + } + + public MessageMenu setMenuItemTextInfoInterceptor(MenuItemTextInfoInterceptor menuItemTextInfoInterceptor) { + this.menuItemTextInfoInterceptor = menuItemTextInfoInterceptor; + return this; + } + + public boolean isBkgInterceptTouch() { + return bkgInterceptTouch; + } + + public MessageMenu setBkgInterceptTouch(boolean bkgInterceptTouch) { + this.bkgInterceptTouch = bkgInterceptTouch; + return this; + } + + public OnBackgroundMaskClickListener getOnBackgroundMaskClickListener() { + return onBackgroundMaskClickListener; + } + + public MessageMenu setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + return this; + } + + public MessageMenu setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public float getRadius() { + return backgroundRadius; + } + + public MessageMenu setTitleIcon(Bitmap titleIcon) { + this.titleIcon = new BitmapDrawable(getResources(), titleIcon); + refreshUI(); + return this; + } + + public MessageMenu setTitleIcon(int titleIconResId) { + this.titleIcon = getResources().getDrawable(titleIconResId); + refreshUI(); + return this; + } + + public MessageMenu setTitleIcon(Drawable titleIcon) { + this.titleIcon = titleIcon; + refreshUI(); + return this; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public MessageMenu setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public MessageMenu setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public MessageMenu setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + public boolean isShowSelectedBackgroundTips() { + return showSelectedBackgroundTips; + } + + public MessageMenu setShowSelectedBackgroundTips(boolean showSelectedBackgroundTips) { + this.showSelectedBackgroundTips = showSelectedBackgroundTips; + refreshUI(); + return this; + } + + // 返回点击的菜单索引 + public int getSelectionIndex() { + return selectionIndex; + } + + // 返回多选时,选择的菜单索引集合 + public int[] getSelectionIndexArray() { + return resultArray; + } + + // 返回多选时,选择的菜单文本集合 + public CharSequence[] getSelectTextArray() { + return selectTextArray; + } + + public MenuItemLayoutRefreshCallback getMenuMenuItemLayoutRefreshCallback() { + return menuMenuItemLayoutRefreshCallback; + } + + public MessageMenu setMenuMenuItemLayoutRefreshCallback(MenuItemLayoutRefreshCallback menuMenuItemLayoutRefreshCallback) { + this.menuMenuItemLayoutRefreshCallback = menuMenuItemLayoutRefreshCallback; + return this; + } + + public TextInfo getOkTextInfo() { + return okTextInfo; + } + + public MessageMenu setOkTextInfo(TextInfo okTextInfo) { + this.okTextInfo = okTextInfo; + return this; + } + + public TextInfo getOtherTextInfo() { + return otherTextInfo; + } + + public MessageMenu setOtherTextInfo(TextInfo otherTextInfo) { + this.otherTextInfo = otherTextInfo; + return this; + } + + public MessageMenu setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public MessageMenu onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public MessageMenu onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public MessageMenu setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public List getIconResIds() { + return iconResIds; + } + + public int getIconResIds(int position) { + if (iconResIds != null && position >= 0 && position < iconResIds.size()) { + return iconResIds.get(position); + } + return 0; + } + + public MessageMenu setIconResIds(List iconResIds) { + this.iconResIds = iconResIds; + refreshUI(); + return this; + } + + public MessageMenu setIconResIds(int... resIds) { + if (iconResIds == null) { + iconResIds = new ArrayList<>(); + } + for (int id : resIds) { + iconResIds.add(id); + } + refreshUI(); + return this; + } + + public boolean isAutoTintIconInLightOrDarkMode() { + return autoTintIconInLightOrDarkMode; + } + + public MessageMenu setAutoTintIconInLightOrDarkMode(boolean autoTintIconInLightOrDarkMode) { + this.autoTintIconInLightOrDarkMode = autoTintIconInLightOrDarkMode; + refreshUI(); + return this; + } + + public MessageMenu appendMessage(CharSequence message) { + this.message = TextUtils.concat(this.message, message); + refreshUI(); + return this; + } + + public MessageMenu setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex(" + orderIndex + ") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public MessageMenu bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public MessageMenu enableMenu(int... menuIndex) { + for (int i : menuIndex) { + menuUsability.put(i, true); + } + return this; + } + + public MessageMenu enableMenu(CharSequence... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (CharSequence c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, true); + } + } else { + error("DialogX: " + dialogKey() + " .enableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public MessageMenu enableMenu(String... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (String c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, true); + } + } else { + error("DialogX: " + dialogKey() + " .enableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public MessageMenu disableMenu(int... menuIndex) { + for (int i : menuIndex) { + menuUsability.put(i, false); + } + return this; + } + + public MessageMenu disableMenu(CharSequence... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (CharSequence c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, false); + } + } else { + error("DialogX: " + dialogKey() + " .disableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public MessageMenu disableMenu(String... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (String c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, false); + } + } else { + error("DialogX: " + dialogKey() + " .disableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public boolean isMenuItemEnable(int index) { + Boolean enabled = menuUsability.get(index); + if (enabled == null) { + return true; + } + return enabled; + } + + public MessageMenu setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public MessageMenu cleanAction(int actionId) { + dialogActionRunnableMap.remove(actionId); + return this; + } + + public MessageMenu cleanAllAction() { + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss() { + dismiss(); + } + + public MessageMenu bindDismissWithLifecycleOwner(LifecycleOwner owner) { + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } + + public ItemDivider getItemDivider() { + return itemDivider; + } + + public MessageMenu setItemDivider(ItemDivider itemDivider) { + this.itemDivider = itemDivider; + refreshUI(); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopMenu.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopMenu.java new file mode 100644 index 0000000..c8fbeb3 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopMenu.java @@ -0,0 +1,1632 @@ +package com.kongzue.dialogx.dialogs; + +import static android.view.View.OVER_SCROLL_NEVER; +import static android.view.View.VISIBLE; + +import android.animation.ValueAnimator; +import android.app.Activity; +import android.graphics.Outline; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.ViewTreeObserver; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.DecelerateInterpolator; +import android.widget.AdapterView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.annotation.Nullable; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.BlurViewType; +import com.kongzue.dialogx.interfaces.DialogConvertViewInterface; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.MenuItemLayoutRefreshCallback; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.OnIconChangeCallBack; +import com.kongzue.dialogx.interfaces.OnMenuItemClickListener; +import com.kongzue.dialogx.util.DialogXViewLoc; +import com.kongzue.dialogx.util.ItemDivider; +import com.kongzue.dialogx.util.PopMenuArrayAdapter; +import com.kongzue.dialogx.util.TextInfo; +import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout; +import com.kongzue.dialogx.util.views.MaxRelativeLayout; +import com.kongzue.dialogx.util.views.PopMenuListView; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/8/18 14:27 + */ +public class PopMenu extends BaseDialog { + + public static long overrideEnterDuration = -1; + public static long overrideExitDuration = -1; + + protected PopMenu me = this; + protected boolean bkgInterceptTouch = true; + protected OnBindView onBindView; // 自定义布局 + protected DialogLifecycleCallback dialogLifecycleCallback; // 对话框生命周期 + protected OnBackgroundMaskClickListener onBackgroundMaskClickListener; + protected List menuList; + protected List iconResIds; + protected boolean autoTintIconInLightOrDarkMode = true; + protected DialogImpl dialogImpl; + protected WeakReference baseViewWeakReference; + protected boolean overlayBaseView = true; // 允许菜单覆盖在 baseView 上 + protected OnMenuItemClickListener onMenuItemClickListener; + protected OnIconChangeCallBack onIconChangeCallBack; // 设置图标 + protected int width = -1; // 指定菜单宽度 + protected int height = -1; // 指定菜单高度 + protected TextInfo menuTextInfo; + protected boolean offScreen = false; // 超出屏幕 + protected float backgroundRadius = DialogX.defaultPopMenuBackgroundRadius; + protected DialogXAnimInterface dialogXAnimImpl; + protected OnBackPressedListener onBackPressedListener; + protected MenuItemLayoutRefreshCallback menuMenuItemLayoutRefreshCallback; + protected int pressedIndex = -1; + protected Map menuUsability = new HashMap(); + protected ItemDivider itemDivider; + + protected int alignGravity = -1; // 指定菜单相对 baseView 的位置 + + // 记录 baseView 位置 + protected DialogXViewLoc baseViewLoc = new DialogXViewLoc(); + private int selectIndex; + public boolean notCheckHash = false; + public int lastHash = -1; + + public PopMenu() { + super(); + } + + public PopMenu(View baseView, List menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(menuList); + this.baseView(baseView); + } + + public PopMenu(View baseView, CharSequence[] menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + this.baseView(baseView); + } + + public PopMenu(List menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(menuList); + } + + public PopMenu(CharSequence... menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + } + + public PopMenu(String... menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + } + + public PopMenu(OnBindView onBindView) { + this.onBindView = onBindView; + } + + public PopMenu(View baseView, OnBindView onBindView) { + this.baseView(baseView); + this.onBindView = onBindView; + } + + public PopMenu(View baseView, List menuList, OnBindView onBindView) { + this.baseView(baseView); + this.menuList = new ArrayList<>(); + this.menuList.addAll(menuList); + this.onBindView = onBindView; + } + + public PopMenu(View baseView, CharSequence[] menuList, OnBindView onBindView) { + this.baseView(baseView); + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + this.onBindView = onBindView; + } + + public PopMenu(List menuList, OnBindView onBindView) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(menuList); + this.onBindView = onBindView; + } + + public PopMenu(CharSequence[] menuList, OnBindView onBindView) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + this.onBindView = onBindView; + } + + public static PopMenu build() { + return new PopMenu(); + } + + public static PopMenu build(DialogXStyle style) { + return new PopMenu().setStyle(style); + } + + public static PopMenu show(CharSequence... menus) { + PopMenu popMenu = new PopMenu(menus); + popMenu.show(); + return popMenu; + } + + public static PopMenu show(String... menus) { + PopMenu popMenu = new PopMenu(menus); + popMenu.show(); + return popMenu; + } + + public static PopMenu show(List menuList) { + PopMenu popMenu = new PopMenu(menuList); + popMenu.show(); + return popMenu; + } + + public static PopMenu show(View baseView, CharSequence[] menus) { + PopMenu popMenu = new PopMenu(baseView, menus); + popMenu.show(); + return popMenu; + } + + public static PopMenu show(View baseView, List menuList) { + PopMenu popMenu = new PopMenu(baseView, menuList); + popMenu.show(); + return popMenu; + } + + public static PopMenu show(View baseView, CharSequence[] menus, OnBindView onBindView) { + PopMenu popMenu = new PopMenu(baseView, menus, onBindView); + popMenu.show(); + return popMenu; + } + + public static PopMenu show(View baseView, List menuList, OnBindView onBindView) { + PopMenu popMenu = new PopMenu(baseView, menuList, onBindView); + popMenu.show(); + return popMenu; + } + + public static PopMenu show(CharSequence[] menus, OnBindView onBindView) { + PopMenu popMenu = new PopMenu(menus, onBindView); + popMenu.show(); + return popMenu; + } + + public static PopMenu show(List menuList, OnBindView onBindView) { + PopMenu popMenu = new PopMenu(menuList, onBindView); + popMenu.show(); + return popMenu; + } + + private ViewTreeObserver viewTreeObserver; + private ViewTreeObserver.OnDrawListener baseViewDrawListener; + + public PopMenu show() { + if (isHide && getDialogView() != null && isShow) { + if (hideWithExitAnim && getDialogImpl() != null) { + getDialogImpl().boxBody.clearAnimation(); + getDialogView().setVisibility(View.VISIBLE); + getDialogImpl().boxRoot.animate().alpha(1f); + getDialogImpl().getDialogXAnimImpl().doShowAnim(me, getDialogImpl().boxBody); + } else { + getDialogView().setVisibility(View.VISIBLE); + } + return this; + } + + super.beforeShow(); + if (getDialogView() == null) { + int layoutId = isLightTheme() ? R.layout.layout_dialogx_popmenu_material : R.layout.layout_dialogx_popmenu_material_dark; + if (getStyle().popMenuSettings() != null) { + if (getStyle().popMenuSettings().layout(isLightTheme()) != 0) { + layoutId = getStyle().popMenuSettings().layout(isLightTheme()); + } + } + + View dialogView = createView(layoutId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) { + dialogView.setTag(me); + } + show(dialogView); + } else { + show(getDialogView()); + } + if (baseView() != null) { + viewTreeObserver = baseView().getViewTreeObserver(); + viewTreeObserver.addOnDrawListener(baseViewDrawListener = new ViewTreeObserver.OnDrawListener() { + @Override + public void onDraw() { + int[] baseViewLocCache = new int[2]; + if (baseView() != null) { + baseView().getLocationInWindow(baseViewLocCache); + if (getDialogImpl() != null && !baseViewLoc.isSameLoc(baseViewLocCache) && baseView().getVisibility() == VISIBLE) { + baseViewLoc.set(baseViewLocCache); + refreshMenuLoc(); + } + } else { + if (viewTreeObserver != null) { + removeDrawListener(viewTreeObserver, this); + viewTreeObserver = null; + baseViewDrawListener = null; + } + } + } + }); + } + return this; + } + + public PopMenu show(Activity activity) { + if (isHide && getDialogView() != null && isShow) { + if (hideWithExitAnim && getDialogImpl() != null) { + getDialogImpl().boxBody.clearAnimation(); + getDialogView().setVisibility(View.VISIBLE); + getDialogImpl().boxRoot.animate().alpha(1f); + getDialogImpl().getDialogXAnimImpl().doShowAnim(me, getDialogImpl().boxBody); + } else { + getDialogView().setVisibility(View.VISIBLE); + } + return this; + } + + super.beforeShow(); + if (getDialogView() == null) { + int layoutId = isLightTheme() ? R.layout.layout_dialogx_popmenu_material : R.layout.layout_dialogx_popmenu_material_dark; + if (getStyle().popMenuSettings() != null) { + if (getStyle().popMenuSettings().layout(isLightTheme()) != 0) { + layoutId = getStyle().popMenuSettings().layout(isLightTheme()); + } + } + + View dialogView = createView(layoutId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) { + dialogView.setTag(me); + } + show(activity, dialogView); + } else { + show(activity, getDialogView()); + } + if (baseView() != null) { + viewTreeObserver = baseView().getViewTreeObserver(); + viewTreeObserver.addOnDrawListener(baseViewDrawListener = new ViewTreeObserver.OnDrawListener() { + @Override + public void onDraw() { + int[] baseViewLocCache = new int[2]; + if (baseView() != null) { + baseView().getLocationInWindow(baseViewLocCache); + if (getDialogImpl() != null && !baseViewLoc.isSameLoc(baseViewLocCache) && baseView().getVisibility() == VISIBLE) { + baseViewLoc.set(baseViewLocCache); + refreshMenuLoc(); + } + } else { + if (viewTreeObserver != null) { + removeDrawListener(viewTreeObserver, this); + viewTreeObserver = null; + baseViewDrawListener = null; + } + } + } + }); + } + return this; + } + + private void refreshMenuLoc() { + if (getDialogImpl() == null || getDialogImpl().boxRoot == null || baseView() == null) { + return; + } + getDialogImpl().boxBody.setTag(null); + DialogXViewLoc loc = getMenuLoc(); + getDialogImpl().boxBody.setTag(loc); + if (!isEnterAnimRunning) { + if (loc.getX() != getDialogImpl().boxBody.getX()) { + getDialogImpl().boxBody.setX(loc.getX()); + } + if (loc.getY() != getDialogImpl().boxBody.getY()) { + getDialogImpl().boxBody.setY(loc.getY()); + } + } + if (getDialogImpl().boxBody.getWidth() != loc.getW()) { + RelativeLayout.LayoutParams rLp = new RelativeLayout.LayoutParams((int) loc.getW(), ViewGroup.LayoutParams.WRAP_CONTENT); + getDialogImpl().boxBody.setLayoutParams(rLp); + } + } + + protected DialogXViewLoc getMenuLoc() { + if (getDialogImpl().boxBody.getTag() instanceof DialogXViewLoc) { + return (DialogXViewLoc) getDialogImpl().boxBody.getTag(); + } + DialogXViewLoc result = new DialogXViewLoc(); + + MaxRelativeLayout boxBody = getDialogImpl().boxBody; + DialogXBaseRelativeLayout boxRoot = getDialogImpl().boxRoot; + // 菜单位置计算逻辑 + int baseViewLeft = (int) baseViewLoc.getX(); + int baseViewTop = (int) baseViewLoc.getY(); + int calX = 0, calY = 0; + if (alignGravity != -1) { + if (isAlignGravity(Gravity.CENTER_VERTICAL)) { + calY = (Math.max(0, baseViewTop + baseView().getMeasuredHeight() / 2 - boxBody.getHeight() / 2)); + } + if (isAlignGravity(Gravity.CENTER_HORIZONTAL)) { + calX = (Math.max(0, baseViewLeft + ( + getWidth() > 0 ? baseView().getMeasuredWidth() / 2 - getWidth() / 2 : 0 + ))); + } + if (isAlignGravity(Gravity.CENTER)) { + calX = (Math.max(0, baseViewLeft + ( + getWidth() > 0 ? (baseView().getMeasuredWidth() / 2 - getWidth() / 2) : 0 + ))); + calY = (Math.max(0, baseViewTop + baseView().getMeasuredHeight() / 2 - boxBody.getHeight() / 2)); + } + if (overlayBaseView) { + // 菜单覆盖在 baseView 上时 + if (isAlignGravity(Gravity.TOP)) { + calY = (baseViewTop + baseView().getMeasuredHeight() - boxBody.getHeight()); + if (calX == 0) { + calX = (Math.max(0, baseViewLeft + ( + getWidth() > 0 ? baseView().getMeasuredWidth() / 2 - getWidth() / 2 : 0 + ))); + } + } + if (isAlignGravity(Gravity.LEFT)) { + calX = Math.max(0, (baseViewLeft + baseView().getMeasuredWidth() - boxBody.getWidth())); + if (calY == 0) { + calY = (Math.max(0, baseViewTop + baseView().getMeasuredHeight() / 2 - boxBody.getHeight() / 2)); + } + } + if (isAlignGravity(Gravity.RIGHT)) { + calX = baseViewLeft; + if (calY == 0) { + calY = (Math.max(0, baseViewTop + baseView().getMeasuredHeight() / 2 - boxBody.getHeight() / 2)); + } + } + if (isAlignGravity(Gravity.BOTTOM)) { + calY = baseViewTop; + if (calX == 0) { + calX = (Math.max(0, baseViewLeft + ( + getWidth() > 0 ? baseView().getMeasuredWidth() / 2 - getWidth() / 2 : 0 + ))); + } + } + } else { + if (isAlignGravity(Gravity.TOP)) { + calY = (Math.max(0, baseViewTop - boxBody.getHeight())); + if (calX == 0) { + calX = (Math.max(0, baseViewLeft + ( + getWidth() > 0 ? baseView().getMeasuredWidth() / 2 - getWidth() / 2 : 0 + ))); + } + } + if (isAlignGravity(Gravity.LEFT)) { + calX = Math.max(0, (baseViewLeft - boxBody.getWidth())); + if (calY == 0) { + calY = (Math.max(0, baseViewTop + baseView().getMeasuredHeight() / 2 - boxBody.getHeight() / 2)); + } + } + if (isAlignGravity(Gravity.RIGHT)) { + calX = (Math.max(0, baseViewLeft + baseView().getWidth())); + if (calY == 0) { + calY = (Math.max(0, baseViewTop + baseView().getMeasuredHeight() / 2 - boxBody.getHeight() / 2)); + } + } + if (isAlignGravity(Gravity.BOTTOM)) { + calY = (Math.max(0, baseViewTop + baseView().getHeight())); + if (calX == 0) { + calX = (Math.max(0, baseViewLeft + ( + getWidth() > 0 ? baseView().getMeasuredWidth() / 2 - getWidth() / 2 : 0 + ))); + } + } + } + if (!offScreen) { + if (calX < 0) { + calX = 0; + } + if ((calX + boxBody.getWidth()) > boxRoot.getUseAreaWidth()) { + calX = boxRoot.getUseAreaWidth() - boxBody.getWidth(); + } + if (calY < 0) { + calY = 0; + } + if ((calY + boxBody.getHeight()) > boxRoot.getUseAreaHeight()) { + calY = boxRoot.getUseAreaHeight() - boxBody.getHeight(); + } + } + result.setX(calX).setY(calY); + } else { + int mHeight = PopMenu.this.height == -1 ? baseView().getHeight() : PopMenu.this.height; + int left = (int) baseViewLoc.getX(); + int top = (int) (baseViewLoc.getY() + (overlayBaseView ? 0 : mHeight) + selectItemYDeviation); + + if (!offScreen) { + if (left < 0) { + left = 0; + } + if ((left + boxBody.getWidth()) > boxRoot.getUseAreaWidth()) { + left = boxRoot.getUseAreaWidth() - boxBody.getWidth(); + } + if (top < 0) { + top = 0; + } + if ((top + boxBody.getHeight()) > boxRoot.getUseAreaHeight()) { + top = boxRoot.getUseAreaHeight() - boxBody.getHeight(); + } + } + result.setX(left).setY(top); + } + + int mWidth = PopMenu.this.width == -1 ? baseView().getWidth() : PopMenu.this.width; + int mHeight = PopMenu.this.height == -1 ? baseView().getHeight() : PopMenu.this.height; + result.setW(mWidth).setH(mHeight); + return result; + } + + protected PopMenuArrayAdapter menuListAdapter; + protected int selectItemYDeviation; // 如果找到了选中菜单,这里记录的是其位置的 Y 偏差值 + protected boolean isEnterAnimRunning; + + public class DialogImpl implements DialogConvertViewInterface { + + private List blurViews; + + public DialogXBaseRelativeLayout boxRoot; + public MaxRelativeLayout boxBody; + public RelativeLayout boxCustom; + public PopMenuListView listMenu; + + public DialogImpl(View convertView) { + if (convertView == null) return; + setDialogView(convertView); + boxRoot = convertView.findViewById(R.id.box_root); + boxBody = convertView.findViewById(R.id.box_body); + boxCustom = convertView.findViewById(R.id.box_custom); + listMenu = convertView.findViewById(R.id.listMenu); + boxBody.setVisibility(View.INVISIBLE); + + blurViews = findAllBlurView(convertView); + + // 先设置为 -1 表示未初始化位置 + boxBody.setX(-1); + boxBody.setY(-1); + init(); + } + + @Override + public void init() { + closing = false; + if (menuListAdapter == null) { + menuListAdapter = new PopMenuArrayAdapter(me, getOwnActivity(), menuList); + } + + boxRoot.setParentDialog(me); + boxRoot.setOnLifecycleCallBack(new DialogXBaseRelativeLayout.OnLifecycleCallBack() { + @Override + public void onShow() { + isShow = true; + preShow = false; + setLifecycleState(Lifecycle.State.CREATED); + onDialogShow(); + getDialogLifecycleCallback().onShow(me); + PopMenu.this.onShow(me); + refreshUI(); + } + + @Override + public void onDismiss() { + isShow = false; + getDialogLifecycleCallback().onDismiss(me); + PopMenu.this.onDismiss(me); + setLifecycleState(Lifecycle.State.DESTROYED); + menuListAdapter = null; + dialogImpl = null; + baseView(null); + dialogLifecycleCallback = null; + System.gc(); + } + }); + + boxRoot.setOnBackPressedListener(new DialogXBaseRelativeLayout.PrivateBackPressedListener() { + @Override + public boolean onBackPressed() { + if (onBackPressedListener != null) { + if (onBackPressedListener.onBackPressed(me)) { + dismiss(); + } + } else { + if (isCancelable()) { + dismiss(); + } + } + return true; + } + }); + listMenu.setMaxHeight(getRootFrameLayout() == null ? dip2px(500) : getRootFrameLayout().getMeasuredHeight() - dip2px(150)); + + boxBody.setVisibility(View.INVISIBLE); + boxBody.post(new Runnable() { + + @Override + public void run() { + getDialogXAnimImpl().doShowAnim(me, boxBody); + setLifecycleState(Lifecycle.State.RESUMED); + + Integer blurFrontColor = null; + Float dialogXRadius = null; + if (style.popMenuSettings() != null && style.popMenuSettings().blurBackgroundSettings() != null) { + blurFrontColor = getColorNullable(getIntStyleAttr(style.popMenuSettings().blurBackgroundSettings().blurForwardColorRes(isLightTheme()))); + dialogXRadius = getFloatStyleAttr((float) style.popMenuSettings().blurBackgroundSettings().blurBackgroundRoundRadiusPx()); + } + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setOverlayColor(backgroundColor == null ? blurFrontColor : backgroundColor); + ((BlurViewType) blurView).setRadiusPx(dialogXRadius); + } + } + } + }); + + int dividerDrawableResId = 0; + int dividerHeight = 0; + if (style.popMenuSettings() != null) { + dividerDrawableResId = style.popMenuSettings().overrideMenuDividerDrawableRes(isLightTheme()); + dividerHeight = style.popMenuSettings().overrideMenuDividerHeight(isLightTheme()); + } + if (dividerDrawableResId == 0) { + dividerDrawableResId = isLightTheme() ? R.drawable.rect_dialogx_material_menu_split_divider : R.drawable.rect_dialogx_material_menu_split_divider_night; + } + + listMenu.setOverScrollMode(OVER_SCROLL_NEVER); + listMenu.setVerticalScrollBarEnabled(false); + listMenu.setDivider(getResources().getDrawable(dividerDrawableResId)); + listMenu.setDividerHeight(dividerHeight); + + listMenu.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (!isMenuItemEnable(position)) { + return; + } + haptic(view); + selectIndex = position; + if (!closing) { + lastHash = menuList.hashCode(); + boolean callBack = getOnMenuItemClickListener().onClick(me, menuList.get(position), position); + if (!notCheckHash) { + if (lastHash == menuList.hashCode()) { + if (callBack) { + callBack = false; + } + } + } + if (!callBack) { + dismiss(); + } + } + } + }); + onDialogInit(); + } + + @Override + public void refreshView() { + if (boxRoot == null || getOwnActivity() == null) { + return; + } + boxRoot.setAutoUnsafePlacePadding(isEnableImmersiveMode()); + boxRoot.setRootPadding(screenPaddings[0], screenPaddings[1], screenPaddings[2], screenPaddings[3]); + if (listMenu.getAdapter() == null) { + listMenu.setAdapter(menuListAdapter); + } else { + if (menuListAdapter.getMenuList() != menuList) { + menuListAdapter = new PopMenuArrayAdapter(me, getOwnActivity(), menuList); + listMenu.setAdapter(menuListAdapter); + } else { + menuListAdapter.notifyDataSetChanged(); + } + } + + if (bkgInterceptTouch) { + if (isCancelable()) { + boxRoot.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onBackgroundMaskClickListener == null || !onBackgroundMaskClickListener.onClick(me, v)) { + doDismiss(v); + } + } + }); + } else { + boxRoot.setOnClickListener(null); + } + } else { + boxRoot.setClickable(false); + } + if (backgroundColor != null) { + tintColor(boxBody, backgroundColor); + } + + if (backgroundRadius > -1) { + if (boxBody.getBackground() instanceof GradientDrawable) { + GradientDrawable gradientDrawable = (GradientDrawable) boxBody.getBackground(); + if (gradientDrawable != null) + gradientDrawable.setCornerRadius(backgroundRadius); + } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + boxBody.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), backgroundRadius); + } + }); + boxBody.setClipToOutline(true); + } + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setRadiusPx(backgroundRadius); + } + } + } + + if (onBindView != null && onBindView.getCustomView() != null) { + onBindView.bindParent(boxCustom, me); + boxCustom.setVisibility(View.VISIBLE); + } else { + boxCustom.setVisibility(View.GONE); + } + + if (width != -1) { + boxBody.setMaxWidth(width); + boxBody.setMinimumWidth(width); + } + + if (height != -1) { + boxBody.setMaxHeight(height); + boxBody.setMinimumHeight(height); + } + + if (backgroundColor != null) { + tintColor(boxBody, backgroundColor); + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setOverlayColor(backgroundColor); + } + } + } + + if (itemDivider != null) { + listMenu.setDivider(itemDivider.createDividerDrawable(getOwnActivity(), isLightTheme())); + listMenu.setDividerHeight(itemDivider.getWidth()); + } + + onDialogRefreshUI(); + } + + @Override + public void doDismiss(View v) { + if (preDismiss(PopMenu.this)) { + return; + } + if (v != null) { + v.setEnabled(false); + } + + if (!dismissAnimFlag && boxRoot != null) { + dismissAnimFlag = true; + boxRoot.post(new Runnable() { + @Override + public void run() { + getDialogXAnimImpl().doExitAnim(me, boxBody); + + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (baseViewDrawListener != null) { + if (viewTreeObserver != null) { + removeDrawListener(viewTreeObserver, baseViewDrawListener); + } else { + if (baseView() != null) { + removeDrawListener(baseView().getViewTreeObserver(), baseViewDrawListener); + } + } + baseViewDrawListener = null; + viewTreeObserver = null; + } + dismiss(getDialogView()); + } + }, getExitAnimationDuration(null)); + } + }); + } + } + + protected DialogXAnimInterface getDialogXAnimImpl() { + if (dialogXAnimImpl == null) { + dialogXAnimImpl = new DialogXAnimInterface() { + + int selectMenuIndex = -1; + + @Override + public void doShowAnim(PopMenu dialog, ViewGroup dialogBodyView) { + long enterAnimDurationTemp = getEnterAnimationDuration(null); + + if (baseView() != null) { + // 有绑定按钮的情况下 + int targetHeight = getBodyRealHeight(); + boxBody.getLayoutParams().height = 1; + + if (overlayBaseView && !listMenu.isCanScroll()) { + if (baseView() instanceof TextView) { + String baseText = ((TextView) baseView()).getText().toString(); + for (CharSequence c : menuList) { + if (TextUtils.equals(c.toString(), baseText)) { + selectMenuIndex = menuList.indexOf(c); + break; + } + } + } + // 找到已选中的项目 + if (selectMenuIndex != -1) { + int[] viewLoc = new int[2]; + if (listMenu.getChildAt(selectMenuIndex) != null) { + int itemHeight = listMenu.getChildAt(selectMenuIndex).getMeasuredHeight(); + listMenu.getChildAt(selectMenuIndex).getLocationInWindow(viewLoc); + selectItemYDeviation = (int) ((baseView().getMeasuredHeight() / 2f) - (viewLoc[1] - boxBody.getY()) - (itemHeight / 2f)); + } + } + } + + refreshMenuLoc(); + selectItemYDeviation = (int) (getMenuLoc().getY() - baseViewLoc.getY()); + + // 展开动画 + ValueAnimator enterAnim = ValueAnimator.ofFloat(0f, 1f); + enterAnim.setInterpolator(new DecelerateInterpolator()); + enterAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (!isShow || getDialogImpl() == null || getDialogImpl().boxBody == null) + return; + float animatedValue = (float) animation.getAnimatedValue(); + isEnterAnimRunning = !(animatedValue == 1f); + DialogXViewLoc loc = getMenuLoc(); + + int aimHeight = animatedValue == 1f ? ViewGroup.LayoutParams.WRAP_CONTENT : (int) (targetHeight * animatedValue); + boxBody.getLayoutParams().height = aimHeight; + boxBody.getLayoutParams().width = getWidth() == -1 ? baseView().getWidth() : getWidth(); + if ((boxBody.getY() + aimHeight) > boxRoot.getSafeHeight()) { + boxBody.setY(boxRoot.getSafeHeight() - aimHeight); + } + float calX = loc.getX() != -1 ? loc.getX() : baseViewLoc.getX(); + float calY = baseViewLoc.getY() + selectItemYDeviation * animatedValue; + + if (!offScreen) { + if (calX < 0) { + calX = 0; + } + if (calY < 0) { + calY = 0; + } + if ((calX + boxBody.getWidth()) > boxRoot.getUseAreaWidth()) { + calX = boxRoot.getUseAreaWidth() - boxBody.getWidth(); + } + if ((calY + boxBody.getHeight()) > boxRoot.getUseAreaHeight()) { + calY = boxRoot.getUseAreaHeight() - boxBody.getHeight(); + } + } + boxBody.setX(calX); + boxBody.setY(calY); + + boxBody.requestLayout(); + if (boxBody.getVisibility() != VISIBLE) { + boxBody.setVisibility(View.VISIBLE); + } + + if (isUseBlurBackground()) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + } + }); + enterAnim.setInterpolator(new DecelerateInterpolator(2f)); + enterAnim.setDuration(enterAnimDurationTemp); + enterAnim.start(); + } else { + // 无绑定按钮的情况下 + RelativeLayout.LayoutParams rLp = (RelativeLayout.LayoutParams) boxBody.getLayoutParams(); + rLp.addRule(RelativeLayout.CENTER_IN_PARENT); + rLp.width = getWidth() == -1 ? RelativeLayout.LayoutParams.MATCH_PARENT : getWidth(); + rLp.leftMargin = dip2px(50); + rLp.rightMargin = dip2px(50); + boxBody.setLayoutParams(rLp); + boxBody.setAlpha(0f); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !isUseBlurBackground()) { + boxBody.setElevation(dip2px(20)); + } + boxBody.setVisibility(View.VISIBLE); + boxBody.animate().alpha(1f).setDuration(enterAnimDurationTemp); + + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f); + bkgAlpha.setDuration(enterAnimDurationTemp); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + }); + bkgAlpha.start(); + } + } + + @Override + public void doExitAnim(PopMenu dialog, ViewGroup dialogBodyView) { + Animation exitAnim = AnimationUtils.loadAnimation(getOwnActivity() == null ? boxRoot.getContext() : getOwnActivity(), R.anim.anim_dialogx_default_exit); + long exitAnimDuration = getExitAnimationDuration(exitAnim); + exitAnim.setDuration(exitAnimDuration); + boxBody.startAnimation(exitAnim); + + boxRoot.animate() + .alpha(0f) + .setInterpolator(new AccelerateInterpolator()) + .setDuration(exitAnimDuration); + + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1, 0f); + bkgAlpha.setDuration(exitAnimDuration); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (boxRoot != null && baseView() == null) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + } + }); + bkgAlpha.start(); + } + }; + } + return dialogXAnimImpl; + } + + private boolean isUseBlurBackground() { + return style.popMenuSettings() != null && style.popMenuSettings().blurBackgroundSettings() != null && style.popMenuSettings().blurBackgroundSettings().blurBackground(); + } + + public long getExitAnimationDuration(@Nullable Animation defaultExitAnim) { + if (defaultExitAnim == null && boxBody.getAnimation() != null) { + defaultExitAnim = boxBody.getAnimation(); + } + long exitAnimDurationTemp = (defaultExitAnim == null || defaultExitAnim.getDuration() == 0) ? 150 : defaultExitAnim.getDuration(); + if (overrideExitDuration >= 0) { + exitAnimDurationTemp = overrideExitDuration; + } + if (exitAnimDuration != -1) { + exitAnimDurationTemp = exitAnimDuration; + } + return exitAnimDurationTemp; + } + + public long getEnterAnimationDuration(@Nullable Animation defaultEnterAnim) { + if (defaultEnterAnim == null && boxBody.getAnimation() != null) { + defaultEnterAnim = boxBody.getAnimation(); + } + long enterAnimDurationTemp = (defaultEnterAnim == null || defaultEnterAnim.getDuration() == 0) ? 150 : defaultEnterAnim.getDuration(); + if (overrideEnterDuration >= 0) { + enterAnimDurationTemp = overrideEnterDuration; + } + if (enterAnimDuration >= 0) { + enterAnimDurationTemp = enterAnimDuration; + } + return enterAnimDurationTemp; + } + } + + private void removeDrawListener(ViewTreeObserver viewTreeObserver, ViewTreeObserver.OnDrawListener listener) { + if (viewTreeObserver == null || listener == null || !viewTreeObserver.isAlive()) { + return; + } + try { + viewTreeObserver.removeOnDrawListener(listener); + } catch (Exception e) { + } + } + + private int getBodyRealHeight() { + if (getDialogImpl() == null) { + return 0; + } + + int matchParentMeasureSpec = View.MeasureSpec.makeMeasureSpec(((View) getDialogImpl().boxBody.getParent()).getWidth(), View.MeasureSpec.EXACTLY); + int wrapContentMeasureSpec = View.MeasureSpec.makeMeasureSpec(((View) getDialogImpl().boxBody.getParent()).getHeight(), View.MeasureSpec.AT_MOST); + getDialogImpl().boxBody.measure(matchParentMeasureSpec, wrapContentMeasureSpec); + + return getDialogImpl().boxBody.getMeasuredHeight(); + } + + private boolean closing; + + public void dismiss() { + closing = true; + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl == null) { + return; + } + dialogImpl.doDismiss(null); + } + }); + } + + @Override + public void restartDialog() { + if (getDialogView() != null) { + if (baseViewDrawListener != null) { + if (viewTreeObserver != null) { + removeDrawListener(viewTreeObserver, baseViewDrawListener); + } else { + if (baseView() != null) { + removeDrawListener(baseView().getViewTreeObserver(), baseViewDrawListener); + } + } + baseViewDrawListener = null; + } + dismiss(getDialogView()); + isShow = false; + } + if (getDialogImpl().boxCustom != null) { + getDialogImpl().boxCustom.removeAllViews(); + } + show(); + } + + @Override + protected void shutdown() { + + } + + public List getMenuList() { + return menuList; + } + + public PopMenu setMenuList(List menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(menuList); + refreshUI(); + return this; + } + + public PopMenu setMenuList(String[] menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + refreshUI(); + return this; + } + + public PopMenu setMenuList(CharSequence[] menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + refreshUI(); + return this; + } + + public PopMenu setMenus(String... menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + refreshUI(); + return this; + } + + public PopMenu setMenus(CharSequence... menuList) { + this.menuList = new ArrayList<>(); + this.menuList.addAll(Arrays.asList(menuList)); + refreshUI(); + return this; + } + + public void refreshUI() { + if (getDialogImpl() == null) { + return; + } + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl != null) { + dialogImpl.refreshView(); + } + } + }); + } + + @Override + public boolean isCancelable() { + return true; + } + + public DialogImpl getDialogImpl() { + return dialogImpl; + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + public DialogLifecycleCallback getDialogLifecycleCallback() { + return dialogLifecycleCallback == null ? new DialogLifecycleCallback() { + } : dialogLifecycleCallback; + } + + public PopMenu setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) { + dialogLifecycleCallback.onShow(me); + } + return this; + } + + public boolean isOverlayBaseView() { + return overlayBaseView; + } + + public PopMenu setOverlayBaseView(boolean overlayBaseView) { + this.overlayBaseView = overlayBaseView; + refreshUI(); + return this; + } + + public OnMenuItemClickListener getOnMenuItemClickListener() { + return onMenuItemClickListener == null ? new OnMenuItemClickListener() { + @Override + public boolean onClick(PopMenu dialog, CharSequence text, int index) { + return false; + } + } : onMenuItemClickListener; + } + + public PopMenu setOnMenuItemClickListener(OnMenuItemClickListener onMenuItemClickListener) { + this.onMenuItemClickListener = onMenuItemClickListener; + return this; + } + + public OnIconChangeCallBack getOnIconChangeCallBack() { + return onIconChangeCallBack; + } + + public PopMenu setOnIconChangeCallBack(OnIconChangeCallBack onIconChangeCallBack) { + this.onIconChangeCallBack = onIconChangeCallBack; + return this; + } + + public PopMenu setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + refreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) { + return null; + } + return onBindView.getCustomView(); + } + + public PopMenu removeCustomView() { + this.onBindView.clean(); + refreshUI(); + return this; + } + + public int getWidth() { + return width; + } + + /** + * 设置菜单 UI 宽度(单位:像素) + * + * @param width 宽度(像素) + * @return PopMenu实例 + */ + public PopMenu setWidth(int width) { + this.width = width; + refreshUI(); + return this; + } + + public int getHeight() { + return height; + } + + /** + * 设置菜单 UI 高度(单位:像素) + * + * @param height 高度(像素) + * @return PopMenu实例 + */ + public PopMenu setHeight(int height) { + this.height = height; + refreshUI(); + return this; + } + + public int getAlignGravity() { + return alignGravity; + } + + /** + * 判断是否有设置对应的位置关系 + * + * @param gravity 位置关系 + * @return 是否具备位置关系 + */ + public boolean isAlignGravity(int gravity) { + return (alignGravity & gravity) == gravity; + } + + public PopMenu setAlignGravity(int alignGravity) { + this.alignGravity = alignGravity; + refreshMenuLoc(); + return this; + } + + public PopMenu setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public TextInfo getMenuTextInfo() { + if (menuTextInfo == null) { + return DialogX.menuTextInfo; + } + return menuTextInfo; + } + + public PopMenu setMenuTextInfo(TextInfo menuTextInfo) { + this.menuTextInfo = menuTextInfo; + return this; + } + + public boolean isOffScreen() { + return offScreen; + } + + /** + * 是否允许超出屏幕显示 + * PopMenu 默认位置显示在屏幕内,开启后将无视屏幕范围限制。 + * + * @param offScreen 超出屏幕 + * @return PopMenu实例 + */ + public PopMenu setOffScreen(boolean offScreen) { + this.offScreen = offScreen; + return this; + } + + public boolean isBkgInterceptTouch() { + return bkgInterceptTouch; + } + + public PopMenu setBkgInterceptTouch(boolean bkgInterceptTouch) { + this.bkgInterceptTouch = bkgInterceptTouch; + return this; + } + + public OnBackgroundMaskClickListener getOnBackgroundMaskClickListener() { + return onBackgroundMaskClickListener; + } + + public PopMenu setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + refreshUI(); + return this; + } + + public PopMenu setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public PopMenu setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public PopMenu setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public PopMenu setHapticFeedbackEnabled(boolean isHapticFeedbackEnabled) { + this.isHapticFeedbackEnabled = isHapticFeedbackEnabled ? 1 : 0; + return this; + } + + public float getRadius() { + return backgroundRadius; + } + + public void hide() { + isHide = true; + hideWithExitAnim = false; + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + + protected boolean hideWithExitAnim; + + public void hideWithExitAnim() { + hideWithExitAnim = true; + isHide = true; + if (getDialogImpl() != null) { + getDialogImpl().getDialogXAnimImpl().doExitAnim(me, getDialogImpl().boxBody); + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + }, getDialogImpl().getExitAnimationDuration(null)); + } + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public PopMenu setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public OnBackPressedListener getOnBackPressedListener() { + return onBackPressedListener; + } + + public PopMenu setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + return this; + } + + public View getBaseView() { + return baseView(); + } + + public PopMenu setBaseView(View baseView) { + baseView(baseView); + refreshUI(); + return this; + } + + public PopMenu setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public PopMenu setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + public int getPressedIndex() { + return pressedIndex; + } + + // 设置已选择的菜单项(菜单背景会有选中状态的显示) + public PopMenu setPressedIndex(int pressedIndex) { + this.pressedIndex = pressedIndex; + refreshUI(); + return this; + } + + public int getSelectIndex() { + return selectIndex; + } + + public CharSequence getSelectMenuText() { + if (menuList == null) return ""; + return menuList.get(selectIndex); + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new PopMenu() { + * + * @param dialog self + * @Override public void onShow(PopMenu dialog) { + * //... + * } + * } + */ + protected void onShow(PopMenu dialog) { + + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new PopMenu() { + * + * @param dialog self + * @Override public boolean onDismiss(PopMenu dialog) { + * WaitDialog.show("Please Wait..."); + * if (dialog.getButtonSelectResult() == BUTTON_SELECT_RESULT.BUTTON_OK) { + * //点击了OK的情况 + * //... + * } else { + * //其他按钮点击、对话框dismiss的情况 + * //... + * } + * return false; + * } + * } + */ + // 用于使用 new 构建实例时,override 的生命周期事件 + protected void onDismiss(PopMenu dialog) { + + } + + public MenuItemLayoutRefreshCallback getMenuMenuItemLayoutRefreshCallback() { + return menuMenuItemLayoutRefreshCallback; + } + + public PopMenu setMenuMenuItemLayoutRefreshCallback(MenuItemLayoutRefreshCallback menuMenuItemLayoutRefreshCallback) { + this.menuMenuItemLayoutRefreshCallback = menuMenuItemLayoutRefreshCallback; + return this; + } + + protected PopMenu baseView(View view) { + if (view == null && baseViewWeakReference != null) { + baseViewWeakReference.clear(); + baseViewWeakReference = null; + } else { + baseViewWeakReference = new WeakReference<>(view); + } + return this; + } + + protected View baseView() { + return baseViewWeakReference == null ? null : baseViewWeakReference.get(); + } + + public PopMenu setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public PopMenu onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public PopMenu onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public PopMenu setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public List getIconResIds() { + return iconResIds; + } + + public int getIconResIds(int position) { + if (iconResIds != null && position >= 0 && position < iconResIds.size()) { + return iconResIds.get(position); + } + return 0; + } + + public PopMenu setIconResIds(int... resIds) { + if (iconResIds == null) { + iconResIds = new ArrayList<>(); + } + for (int id : resIds) { + iconResIds.add(id); + } + refreshUI(); + return this; + } + + public PopMenu setIconResIds(List iconResIds) { + this.iconResIds = iconResIds; + refreshUI(); + return this; + } + + public boolean isAutoTintIconInLightOrDarkMode() { + return autoTintIconInLightOrDarkMode; + } + + public PopMenu setAutoTintIconInLightOrDarkMode(boolean autoTintIconInLightOrDarkMode) { + this.autoTintIconInLightOrDarkMode = autoTintIconInLightOrDarkMode; + return this; + } + + public PopMenu setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex(" + orderIndex + ") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public PopMenu bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public PopMenu enableMenu(int... menuIndex) { + for (int i : menuIndex) { + menuUsability.put(i, true); + } + return this; + } + + public PopMenu enableMenu(CharSequence... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (CharSequence c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, true); + } + } else { + error("DialogX: " + dialogKey() + " .enableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public PopMenu enableMenu(String... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (String c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, true); + } + } else { + error("DialogX: " + dialogKey() + " .enableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public PopMenu disableMenu(int... menuIndex) { + for (int i : menuIndex) { + menuUsability.put(i, false); + } + return this; + } + + public PopMenu disableMenu(CharSequence... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (CharSequence c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, false); + } + } else { + error("DialogX: " + dialogKey() + " .disableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public PopMenu disableMenu(String... menuText) { + if (menuList != null && !menuList.isEmpty()) { + for (String c : menuText) { + int index = menuList.indexOf(c); + menuUsability.put(index, false); + } + } else { + error("DialogX: " + dialogKey() + " .disableMenu(" + menuText + ")执行失败,请先初始化菜单项 menuList"); + } + return this; + } + + public boolean isMenuItemEnable(int index) { + Boolean enabled = menuUsability.get(index); + if (enabled == null) { + return true; + } + return enabled; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public PopMenu setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + refreshUI(); + return this; + } + + public PopMenu setBackgroundColorRes(@ColorRes int backgroundColorResId) { + this.backgroundColor = getColor(backgroundColorResId); + refreshUI(); + return this; + } + + public PopMenu setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public PopMenu cleanAction(int actionId) { + dialogActionRunnableMap.remove(actionId); + return this; + } + + public PopMenu cleanAllAction() { + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss() { + dismiss(); + } + + public PopMenu bindDismissWithLifecycleOwner(LifecycleOwner owner) { + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } + + public ItemDivider getItemDivider() { + return itemDivider; + } + + public PopMenu setItemDivider(ItemDivider itemDivider) { + this.itemDivider = itemDivider; + refreshUI(); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopNotification.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopNotification.java new file mode 100644 index 0000000..8d067e0 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopNotification.java @@ -0,0 +1,1732 @@ +package com.kongzue.dialogx.dialogs; + +import android.animation.ValueAnimator; +import android.app.Activity; +import android.graphics.Bitmap; +import android.graphics.Outline; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.text.TextUtils; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.DecelerateInterpolator; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.annotation.Nullable; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.BlurViewType; +import com.kongzue.dialogx.interfaces.DialogConvertViewInterface; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.NoTouchInterface; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.OnDialogButtonClickListener; +import com.kongzue.dialogx.interfaces.OnSafeInsetsChangeListener; +import com.kongzue.dialogx.interfaces.PopMoveDisplacementInterceptor; +import com.kongzue.dialogx.util.PopValueAnimator; +import com.kongzue.dialogx.util.TextInfo; +import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/20 11:59 + */ +public class PopNotification extends BaseDialog implements NoTouchInterface { + + public static final int TIME_NO_AUTO_DISMISS_DELAY = -1; + protected static List popNotificationList; + + public static long overrideEnterDuration = -1; + public static long overrideExitDuration = -1; + public static int overrideEnterAnimRes = 0; + public static int overrideExitAnimRes = 0; + + public static int maxShowCount = Integer.MAX_VALUE; + public static PopMoveDisplacementInterceptor moveDisplacementInterceptor; + + protected OnBindView onBindView; + protected DialogLifecycleCallback dialogLifecycleCallback; + protected PopNotification me = this; + protected DialogImpl dialogImpl; + protected int enterAnimResId = 0; + protected int exitAnimResId = 0; + protected DialogXStyle.PopNotificationSettings.ALIGN align; + protected OnDialogButtonClickListener onButtonClickListener; + protected OnDialogButtonClickListener onPopNotificationClickListener; + protected boolean autoTintIconInLightOrDarkMode = true; + protected BOOLEAN tintIcon; + protected float backgroundRadius = DialogX.defaultPopNotificationBackgroundRadius; + protected DialogXAnimInterface dialogXAnimImpl; + + protected int iconResId; + protected Bitmap iconBitmap; + protected Drawable iconDrawable; + protected CharSequence title; + protected CharSequence message; + protected CharSequence buttonText; + protected int iconSize; + protected boolean slideToClose = true; + + protected TextInfo titleTextInfo; + protected TextInfo messageTextInfo; + protected TextInfo buttonTextInfo = new TextInfo().setBold(true); + protected int[] bodyMargin = new int[]{-1, -1, -1, -1}; + + protected PopNotification() { + super(); + } + + @Override + public boolean isCancelable() { + return false; + } + + public static PopNotification build() { + return new PopNotification(); + } + + public static PopNotification build(DialogXStyle style) { + return new PopNotification().setStyle(style); + } + + public static PopNotification build(OnBindView onBindView) { + return new PopNotification().setCustomView(onBindView); + } + + public PopNotification(OnBindView onBindView) { + this.onBindView = onBindView; + } + + public PopNotification(CharSequence title) { + this.title = title; + } + + public PopNotification(CharSequence title, CharSequence message) { + this.title = title; + this.message = message; + } + + public PopNotification(int titleResId) { + this.title = getString(titleResId); + } + + public PopNotification(int titleResId, int messageResId) { + this.title = getString(titleResId); + this.message = getString(messageResId); + } + + public PopNotification(int iconResId, CharSequence title) { + this.iconResId = iconResId; + this.title = title; + } + + public PopNotification(int iconResId, CharSequence title, CharSequence message) { + this.iconResId = iconResId; + this.title = title; + this.message = message; + } + + public PopNotification(int iconResId, int titleResId, int messageResId) { + this.iconResId = iconResId; + this.title = getString(titleResId); + this.message = getString(messageResId); + } + + public PopNotification(CharSequence title, OnBindView onBindView) { + this.title = title; + this.onBindView = onBindView; + } + + public PopNotification(CharSequence title, CharSequence message, OnBindView onBindView) { + this.title = title; + this.message = message; + this.onBindView = onBindView; + } + + public PopNotification(int titleResId, OnBindView onBindView) { + this.title = getString(titleResId); + this.onBindView = onBindView; + } + + public PopNotification(int titleResId, int messageResId, OnBindView onBindView) { + this.title = getString(titleResId); + this.message = getString(messageResId); + this.onBindView = onBindView; + } + + public PopNotification(int iconResId, CharSequence title, OnBindView onBindView) { + this.iconResId = iconResId; + this.title = title; + this.onBindView = onBindView; + } + + public PopNotification(int iconResId, CharSequence title, CharSequence message, OnBindView onBindView) { + this.iconResId = iconResId; + this.title = title; + this.message = message; + this.onBindView = onBindView; + } + + public PopNotification(int iconResId, int titleResId, int messageResId, OnBindView onBindView) { + this.iconResId = iconResId; + this.title = getString(titleResId); + this.message = getString(messageResId); + this.onBindView = onBindView; + } + + public PopNotification(int iconResId, int titleResId, int messageResId, int buttonTextResId) { + this.iconResId = iconResId; + this.title = getString(titleResId); + this.message = getString(messageResId); + this.buttonText = getString(buttonTextResId); + } + + public PopNotification(int iconResId, int titleResId, int messageResId, int buttonTextResId, OnBindView onBindView) { + this.iconResId = iconResId; + this.title = getString(titleResId); + this.message = getString(messageResId); + this.buttonText = getString(buttonTextResId); + this.onBindView = onBindView; + } + + public PopNotification(int iconResId, CharSequence title, CharSequence message, CharSequence buttonText) { + this.iconResId = iconResId; + this.title = title; + this.message = message; + this.buttonText = buttonText; + } + + public PopNotification(int iconResId, CharSequence title, CharSequence message, CharSequence buttonText, OnBindView onBindView) { + this.iconResId = iconResId; + this.title = title; + this.message = message; + this.buttonText = buttonText; + this.onBindView = onBindView; + } + + public static PopNotification show(OnBindView onBindView) { + PopNotification popNotification = new PopNotification(onBindView); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(CharSequence title) { + PopNotification popNotification = new PopNotification(title); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(CharSequence title, CharSequence message) { + PopNotification popNotification = new PopNotification(title, message); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int titleResId) { + PopNotification popNotification = new PopNotification(titleResId); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int titleResId, int messageResId) { + PopNotification popNotification = new PopNotification(titleResId, messageResId); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(CharSequence title, OnBindView onBindView) { + PopNotification popNotification = new PopNotification(title, onBindView); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(CharSequence title, CharSequence message, OnBindView onBindView) { + PopNotification popNotification = new PopNotification(title, message, onBindView); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int titleResId, OnBindView onBindView) { + PopNotification popNotification = new PopNotification(titleResId, onBindView); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int titleResId, int messageResId, OnBindView onBindView) { + PopNotification popNotification = new PopNotification(titleResId, messageResId, onBindView); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int iconResId, CharSequence title, OnBindView onBindView) { + PopNotification popNotification = new PopNotification(iconResId, title, onBindView); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int iconResId, CharSequence title, CharSequence message, OnBindView onBindView) { + PopNotification popNotification = new PopNotification(iconResId, title, message, onBindView); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int iconResId, CharSequence title) { + PopNotification popNotification = new PopNotification(iconResId, title); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int iconResId, CharSequence title, CharSequence message) { + PopNotification popNotification = new PopNotification(iconResId, title, message); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int iconResId, int titleResId, int messageResId) { + PopNotification popNotification = new PopNotification(iconResId, titleResId, messageResId); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int iconResId, int titleResId, int messageResId, OnBindView onBindView) { + PopNotification popNotification = new PopNotification(iconResId, titleResId, messageResId, onBindView); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int iconResId, int titleResId, int messageResId, int buttonTextResId) { + PopNotification popNotification = new PopNotification(iconResId, titleResId, messageResId, buttonTextResId); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int iconResId, int titleResId, int messageResId, int buttonTextResId, OnBindView onBindView) { + PopNotification popNotification = new PopNotification(iconResId, titleResId, messageResId, buttonTextResId, onBindView); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int iconResId, CharSequence title, CharSequence message, CharSequence buttonText) { + PopNotification popNotification = new PopNotification(iconResId, title, message, buttonText); + popNotification.show(); + return popNotification; + } + + public static PopNotification show(int iconResId, CharSequence title, CharSequence message, CharSequence buttonText, OnBindView onBindView) { + PopNotification popNotification = new PopNotification(iconResId, title, message, buttonText, onBindView); + popNotification.show(); + return popNotification; + } + + public PopNotification show() { + if (isHide && getDialogView() != null) { + getDialogView().setVisibility(View.VISIBLE); + return this; + } + super.beforeShow(); + if (getDialogView() == null) { + if (DialogX.onlyOnePopNotification) { + if (popNotificationList != null && !popNotificationList.isEmpty()) { + popNotificationList.get(popNotificationList.size() - 1).dismiss(); + } + } else { + if (popNotificationList != null) { + CopyOnWriteArrayList popNotificationCopyOnWriteArrayList = new CopyOnWriteArrayList<>(popNotificationList); + for (int i = 0; i < popNotificationCopyOnWriteArrayList.size(); i++) { + PopNotification popInstance = popNotificationCopyOnWriteArrayList.get(i); + if (popNotificationCopyOnWriteArrayList.size() >= maxShowCount) { + if (i <= popNotificationCopyOnWriteArrayList.size() - maxShowCount) { + popInstance.dismiss(); + popNotificationList.remove(popInstance); + } + } + } + } + } + if (popNotificationList == null) popNotificationList = new ArrayList<>(); + popNotificationList.add(PopNotification.this); + int layoutResId = isLightTheme() ? R.layout.layout_dialogx_popnotification_material : R.layout.layout_dialogx_popnotification_material_dark; + if (style.popNotificationSettings() != null) { + if (style.popNotificationSettings().layout(isLightTheme()) != 0) { + layoutResId = style.popNotificationSettings().layout(isLightTheme()); + } + align = style.popNotificationSettings().align(); + if (align == null) align = DialogXStyle.PopNotificationSettings.ALIGN.TOP; + int styleEnterAnimResId = style.popNotificationSettings().enterAnimResId(isLightTheme()); + int styleExitAnimResId = style.popNotificationSettings().exitAnimResId(isLightTheme()); + enterAnimResId = enterAnimResId == 0 ? ( + overrideEnterAnimRes == 0 ? (styleEnterAnimResId != 0 ? styleEnterAnimResId : R.anim.anim_dialogx_notification_enter) : overrideEnterAnimRes + ) : enterAnimResId; + exitAnimResId = exitAnimResId == 0 ? ( + overrideExitAnimRes == 0 ? (styleExitAnimResId != 0 ? styleExitAnimResId : R.anim.anim_dialogx_notification_exit) : overrideExitAnimRes + ) : exitAnimResId; + enterAnimDuration = enterAnimDuration == -1 ? ( + overrideEnterDuration + ) : enterAnimDuration; + exitAnimDuration = exitAnimDuration == -1 ? ( + overrideExitDuration + ) : exitAnimDuration; + } + View dialogView = createView(layoutResId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } else { + show(getDialogView()); + } + return this; + } + + public PopNotification show(Activity activity) { + super.beforeShow(); + if (getDialogView() == null) { + if (DialogX.onlyOnePopNotification) { + PopNotification oldInstance = null; + if (popNotificationList != null && !popNotificationList.isEmpty()) { + oldInstance = popNotificationList.get(popNotificationList.size() - 1); + } + if (oldInstance != null) { + oldInstance.dismiss(); + } + } + if (popNotificationList == null) popNotificationList = new ArrayList<>(); + popNotificationList.add(PopNotification.this); + int layoutResId = isLightTheme() ? R.layout.layout_dialogx_popnotification_material : R.layout.layout_dialogx_popnotification_material_dark; + if (style.popNotificationSettings() != null) { + if (style.popNotificationSettings().layout(isLightTheme()) != 0) { + layoutResId = style.popNotificationSettings().layout(isLightTheme()); + } + align = style.popNotificationSettings().align(); + if (align == null) align = DialogXStyle.PopNotificationSettings.ALIGN.TOP; + int styleEnterAnimResId = style.popNotificationSettings().enterAnimResId(isLightTheme()); + int styleExitAnimResId = style.popNotificationSettings().exitAnimResId(isLightTheme()); + enterAnimResId = enterAnimResId == 0 ? ( + overrideEnterAnimRes == 0 ? (styleEnterAnimResId != 0 ? styleEnterAnimResId : R.anim.anim_dialogx_notification_enter) : overrideEnterAnimRes + ) : enterAnimResId; + exitAnimResId = exitAnimResId == 0 ? ( + overrideExitAnimRes == 0 ? (styleExitAnimResId != 0 ? styleExitAnimResId : R.anim.anim_dialogx_notification_exit) : overrideExitAnimRes + ) : exitAnimResId; + enterAnimDuration = enterAnimDuration == -1 ? ( + overrideEnterDuration + ) : enterAnimDuration; + exitAnimDuration = exitAnimDuration == -1 ? ( + overrideExitDuration + ) : exitAnimDuration; + } + View dialogView = createView(layoutResId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(activity, dialogView); + } else { + show(activity, getDialogView()); + } + return this; + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + protected Timer autoDismissTimer; + protected long autoDismissDelay = Long.MIN_VALUE; + + public PopNotification autoDismiss(long delay) { + autoDismissDelay = delay; + if (autoDismissTimer != null) { + autoDismissTimer.cancel(); + } + if (delay < 0) return this; + autoDismissTimer = new Timer(); + autoDismissTimer.schedule(new TimerTask() { + @Override + public void run() { + dismiss(); + } + }, delay); + return this; + } + + public void resetAutoDismissTimer() { + autoDismiss(autoDismissDelay); + } + + private boolean isNoSetCustomDelay() { + return autoDismissDelay == Long.MIN_VALUE; + } + + public PopNotification showShort() { + if (isNoSetCustomDelay()) autoDismiss(2000); + if (!preShow && !isShow) { + show(); + } + return this; + } + + public PopNotification showLong() { + autoDismiss(3500); + if (!preShow && !isShow) { + show(); + } + return this; + } + + public PopNotification showAlways() { + return noAutoDismiss(); + } + + public PopNotification noAutoDismiss() { + autoDismiss(TIME_NO_AUTO_DISMISS_DELAY); + return this; + } + + private float defaultTop; + + public class DialogImpl implements DialogConvertViewInterface { + + public DialogXBaseRelativeLayout boxRoot; + public ViewGroup boxBody; + public ImageView imgDialogxPopIcon; + public TextView txtDialogxPopTitle; + public TextView txtDialogxPopMessage; + public TextView txtDialogxButton; + public RelativeLayout boxCustom; + + private List blurViews; + + public DialogImpl(View convertView) { + if (convertView == null) return; + setDialogView(convertView); + boxRoot = convertView.findViewById(R.id.box_root); + boxBody = convertView.findViewById(R.id.box_body); + imgDialogxPopIcon = convertView.findViewById(R.id.img_dialogx_pop_icon); + txtDialogxPopTitle = convertView.findViewById(R.id.txt_dialogx_pop_title); + txtDialogxPopMessage = convertView.findViewById(R.id.txt_dialogx_pop_message); + txtDialogxButton = convertView.findViewById(R.id.txt_dialogx_button); + boxCustom = convertView.findViewById(R.id.box_custom); + + blurViews = findAllBlurView(convertView); + + init(); + dialogImpl = this; + refreshView(); + } + + @Override + public void init() { + if (titleTextInfo == null) titleTextInfo = DialogX.titleTextInfo; + if (messageTextInfo == null) messageTextInfo = DialogX.messageTextInfo; + if (buttonTextInfo == null) buttonTextInfo = DialogX.buttonTextInfo; + if (backgroundColor == null) backgroundColor = DialogX.backgroundColor; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(getThisOrderIndex()); + } + + if (autoDismissTimer == null) { + showShort(); + } + + boxRoot.setClickable(false); + boxRoot.setFocusable(false); + boxRoot.setParentDialog(me); + boxRoot.setAutoUnsafePlacePadding(false); + boxRoot.setOnLifecycleCallBack(new DialogXBaseRelativeLayout.OnLifecycleCallBack() { + @Override + public void onShow() { + isShow = true; + preShow = false; + + setLifecycleState(Lifecycle.State.CREATED); + boxRoot.setAlpha(0f); + + onDialogShow(); + getDialogLifecycleCallback().onShow(me); + PopNotification.this.onShow(me); + } + + @Override + public void onDismiss() { + if (popNotificationList != null) { + popNotificationList.remove(PopNotification.this); + if (popNotificationList.isEmpty()) { + popNotificationList = null; + } + } + if (autoDismissTimer != null) { + autoDismissTimer.cancel(); + } + isShow = false; + getDialogLifecycleCallback().onDismiss(me); + PopNotification.this.onDismiss(me); + setLifecycleState(Lifecycle.State.DESTROYED); + dialogImpl = null; + System.gc(); + } + }); + + RelativeLayout.LayoutParams rlp; + rlp = ((RelativeLayout.LayoutParams) boxBody.getLayoutParams()); + if (align == null) align = DialogXStyle.PopNotificationSettings.ALIGN.TOP; + switch (align) { + case TOP: + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_TOP); + break; + case BOTTOM: + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + boxRoot.setAutoUnsafePlacePadding(true); + break; + case CENTER: + rlp.removeRule(RelativeLayout.ALIGN_PARENT_TOP); + rlp.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + rlp.addRule(RelativeLayout.CENTER_IN_PARENT); + break; + } + boxBody.setLayoutParams(rlp); + + boxRoot.setOnSafeInsetsChangeListener(new OnSafeInsetsChangeListener() { + @Override + public void onChange(Rect unsafeRect) { + if (align == DialogXStyle.PopNotificationSettings.ALIGN.TOP) { + boxBody.setY(defaultTop <= 0 ? defaultTop = (unsafeRect.top + bodyMargin[1]) : defaultTop); + } else if (align == DialogXStyle.PopNotificationSettings.ALIGN.TOP_INSIDE) { + boxBody.setPadding(0, unsafeRect.top, 0, 0); + } + } + }); + + boxRoot.setOnBackPressedListener(new DialogXBaseRelativeLayout.PrivateBackPressedListener() { + @Override + public boolean onBackPressed() { + return false; + } + }); + + boxRoot.post(new Runnable() { + @Override + public void run() { + getDialogXAnimImpl().doShowAnim(me, boxBody); + + if (!DialogX.onlyOnePopNotification) { + if (popNotificationList != null) { + for (int i = 0; i < popNotificationList.size() - 1; i++) { + PopNotification popInstance = popNotificationList.get(i); + popInstance.moveBack(boxBody.getHeight()); + } + } + } + + Integer blurFrontColor = null; + Float popNotificationRadius = null; + if (getStyle().popNotificationSettings() != null && getStyle().popNotificationSettings().blurBackgroundSettings() != null && + getStyle().popNotificationSettings().blurBackgroundSettings().blurBackground()) { + blurFrontColor = backgroundColor == null ? + getColorNullable(getIntStyleAttr(getStyle().popNotificationSettings().blurBackgroundSettings().blurForwardColorRes(isLightTheme()))) : + backgroundColor; + popNotificationRadius = getFloatStyleAttr((float) getStyle().popNotificationSettings().blurBackgroundSettings().blurBackgroundRoundRadiusPx()); + } + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setOverlayColor(backgroundColor == null ? blurFrontColor : backgroundColor); + ((BlurViewType) blurView).setRadiusPx(popNotificationRadius); + } + } + setLifecycleState(Lifecycle.State.RESUMED); + } + }); + + boxBody.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (dismissAnimFlag) { + log("skip click @ A"); + return; + } + haptic(v); + if (onPopNotificationClickListener != null) { + if (!onPopNotificationClickListener.onClick(me, v)) { + dismiss(); + } + } else { + dismiss(); + } + } + }); + + txtDialogxButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (dismissAnimFlag) { + return; + } + haptic(v); + if (onButtonClickListener != null) { + if (!onButtonClickListener.onClick(me, v)) { + doDismiss(v); + } + } else { + doDismiss(v); + } + } + }); + onDialogInit(); + } + + @Override + public void refreshView() { + if (boxRoot == null) { + return; + } + boxRoot.setRootPadding(screenPaddings[0], screenPaddings[1], screenPaddings[2], screenPaddings[3]); + if (backgroundColor != null) { + tintColor(boxBody, backgroundColor); + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setOverlayColor(backgroundColor); + } + } + } + + if (onBindView != null && onBindView.getCustomView() != null) { + onBindView.bindParent(boxCustom, me); + boxCustom.setVisibility(View.VISIBLE); + } else { + boxCustom.setVisibility(View.GONE); + } + + if (backgroundRadius > -1) { + if (boxBody.getBackground() instanceof GradientDrawable) { + GradientDrawable gradientDrawable = (GradientDrawable) boxBody.getBackground(); + if (gradientDrawable != null) + gradientDrawable.setCornerRadius(backgroundRadius); + } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + boxBody.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), backgroundRadius); + } + }); + boxBody.setClipToOutline(true); + } + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setRadiusPx(backgroundRadius); + } + } + } + + showText(txtDialogxPopTitle, title); + showText(txtDialogxPopMessage, message); + showText(txtDialogxButton, buttonText); + + useTextInfo(txtDialogxPopTitle, titleTextInfo); + useTextInfo(txtDialogxPopMessage, messageTextInfo); + useTextInfo(txtDialogxButton, buttonTextInfo); + + if (iconBitmap != null && !iconBitmap.isRecycled()) { + imgDialogxPopIcon.setVisibility(View.VISIBLE); + imgDialogxPopIcon.setImageBitmap(iconBitmap); + } else { + if (iconDrawable != null) { + imgDialogxPopIcon.setVisibility(View.VISIBLE); + imgDialogxPopIcon.setImageDrawable(iconDrawable); + } else { + if (iconResId != 0) { + imgDialogxPopIcon.setVisibility(View.VISIBLE); + imgDialogxPopIcon.setImageResource(iconResId); + } else { + imgDialogxPopIcon.setVisibility(View.GONE); + } + } + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && tintIcon == BOOLEAN.TRUE) { + if (autoTintIconInLightOrDarkMode) { + imgDialogxPopIcon.setImageTintList(txtDialogxPopTitle.getTextColors()); + } else { + imgDialogxPopIcon.setImageTintList(null); + } + } else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + imgDialogxPopIcon.setImageTintList(null); + } + } + if (iconSize > 0) { + ViewGroup.LayoutParams iLp = imgDialogxPopIcon.getLayoutParams(); + iLp.width = iconSize; + iLp.height = iconSize; + imgDialogxPopIcon.setLayoutParams(iLp); + } + + if (slideToClose) { + boxBody.setOnTouchListener(new View.OnTouchListener() { + + boolean touchDown; + float touchY; + + @Override + public boolean onTouch(View v, MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + touchDown = true; + touchY = event.getY(); + break; + case MotionEvent.ACTION_MOVE: + if (touchDown) { + float deltaY = event.getY() - touchY; + if (boxBody.getY() + deltaY < defaultTop) { + boxBody.setY(boxBody.getY() + deltaY); + } else { + boxBody.setY(defaultTop); + } + } + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + if (boxBody.getY() < defaultTop - DialogX.touchSlideTriggerThreshold) { + doDismiss(v); + } else { + PopValueAnimator valueAnimator = PopValueAnimator.ofFloat(boxBody.getY(), defaultTop); + boxBody.setTag(valueAnimator); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (getDialogImpl() == null || !isShow) { + animation.cancel(); + return; + } + View bodyView = getDialogImpl().boxBody; + if (bodyView != null && bodyView.isAttachedToWindow()) { + bodyView.setY((Float) animation.getAnimatedValue()); + } + } + }); + valueAnimator.setDuration(enterAnimDuration == -1 ? 300 : enterAnimDuration).setInterpolator(new DecelerateInterpolator(2f)); + valueAnimator.start(); + + if (Math.abs(boxBody.getY() - defaultTop) <= 1 && event.getY() <= touchY) { + boxBody.callOnClick(); + } + } + break; + } + return true; + } + }); + } else { + boxBody.setOnTouchListener(null); + } + + RelativeLayout.LayoutParams rlp = ((RelativeLayout.LayoutParams) boxBody.getLayoutParams()); + if (bodyMargin[0] != -1) rlp.leftMargin = bodyMargin[0]; + if (bodyMargin[1] != -1) rlp.topMargin = bodyMargin[1]; + if (bodyMargin[2] != -1) rlp.rightMargin = bodyMargin[2]; + if (bodyMargin[3] != -1) rlp.bottomMargin = bodyMargin[3]; + boxBody.setLayoutParams(rlp); + onDialogRefreshUI(); + } + + @Override + public void doDismiss(final View v) { + if (PopNotification.this.preDismiss(PopNotification.this)) { + return; + } + if (v != null) v.setEnabled(false); + + if (!dismissAnimFlag && boxRoot != null) { + dismissAnimFlag = true; + boxCustom.setVisibility(View.GONE); + boxBody.setFocusable(false); + boxBody.setClickable(false); + boxBody.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return false; + } + }); + txtDialogxButton.setFocusable(false); + txtDialogxButton.setClickable(false); + boxRoot.post(new Runnable() { + @Override + public void run() { + getDialogXAnimImpl().doExitAnim(me, boxBody); + + preRecycle = true; + runOnMainDelay(new Runnable() { + @Override + public void run() { + waitForDismiss(); + } + }, getExitAnimationDuration(null)); + + if (popNotificationList != null) { + //使位于自己之前的PopTip moveDown + int index = popNotificationList.indexOf(me); + for (int i = 0; i < index; i++) { + PopNotification popNotification = popNotificationList.get(i); + popNotification.moveFront(boxBody.getHeight()); + } + } + } + }); + } + } + + protected DialogXAnimInterface getDialogXAnimImpl() { + if (dialogXAnimImpl == null) { + dialogXAnimImpl = new DialogXAnimInterface() { + @Override + public void doShowAnim(PopNotification dialog, ViewGroup dialogBodyView) { + Animation enterAnim = AnimationUtils.loadAnimation(getApplicationContext(), enterAnimResId == 0 ? R.anim.anim_dialogx_notification_enter : enterAnimResId); + long enterAnimDuration = getEnterAnimationDuration(enterAnim); + enterAnim.setInterpolator(new DecelerateInterpolator(2f)); + enterAnim.setDuration(enterAnimDuration); + enterAnim.setFillAfter(true); + boxBody.startAnimation(enterAnim); + + boxRoot.animate() + .setDuration(enterAnimDuration) + .alpha(1f) + .setInterpolator(new DecelerateInterpolator()) + .setListener(null); + } + + @Override + public void doExitAnim(PopNotification dialog, ViewGroup dialogBodyView) { + Animation exitAnim = AnimationUtils.loadAnimation(getApplicationContext() == null ? boxRoot.getContext() : getApplicationContext(), exitAnimResId == 0 ? R.anim.anim_dialogx_notification_exit : exitAnimResId); + long exitAnimDuration = getExitAnimationDuration(exitAnim); + exitAnim.setDuration(exitAnimDuration); + exitAnim.setFillAfter(true); + boxBody.startAnimation(exitAnim); + + boxRoot.animate() + .alpha(0f) + .setInterpolator(new AccelerateInterpolator()) + .setDuration(exitAnimDuration); + } + }; + } + return dialogXAnimImpl; + } + + public long getExitAnimationDuration(@Nullable Animation defaultExitAnim) { + if (defaultExitAnim == null && boxBody.getAnimation() != null) { + defaultExitAnim = boxBody.getAnimation(); + } + long exitAnimDurationTemp = (defaultExitAnim == null || defaultExitAnim.getDuration() == 0) ? 300 : defaultExitAnim.getDuration(); + if (overrideExitDuration >= 0) { + exitAnimDurationTemp = overrideExitDuration; + } + if (exitAnimDuration != -1) { + exitAnimDurationTemp = exitAnimDuration; + } + return exitAnimDurationTemp; + } + + public long getEnterAnimationDuration(@Nullable Animation defaultEnterAnim) { + if (defaultEnterAnim == null && boxBody.getAnimation() != null) { + defaultEnterAnim = boxBody.getAnimation(); + } + long enterAnimDurationTemp = (defaultEnterAnim == null || defaultEnterAnim.getDuration() == 0) ? 300 : defaultEnterAnim.getDuration(); + if (overrideEnterDuration >= 0) { + enterAnimDurationTemp = overrideEnterDuration; + } + if (enterAnimDuration >= 0) { + enterAnimDurationTemp = enterAnimDuration; + } + return enterAnimDurationTemp; + } + } + + protected boolean preRecycle = false; + + /** + * 之所以这样处理,在较为频繁的启停 popNotification 时可能存在 popNotification 关闭动画位置错误无法计算的问题 + * 使用 preRecycle 标记记录是否需要回收,而不是立即销毁 + * 等待所有 popNotification 处于待回收状态时一并回收可以避免此问题 + */ + private void waitForDismiss() { + if (popNotificationList == null || popNotificationList.isEmpty()) { + return; + } + preRecycle = true; + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + CopyOnWriteArrayList copyPopNotificationList = new CopyOnWriteArrayList<>(popNotificationList); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + copyPopNotificationList.removeIf(Objects::isNull); + } else { + Iterator iterator = copyPopNotificationList.iterator(); + while (iterator.hasNext()) { + if (iterator.next() == null) { + iterator.remove(); + } + } + } +// boolean allPreRecycled = true; +// for (PopNotification popTip : copyPopNotificationList) { +// if (!popTip.preRecycle) { +// allPreRecycled = false; +// break; +// } +// } +// if (allPreRecycled) { +// for (PopNotification popTip : copyPopNotificationList) { +// dismiss(popTip.getDialogView()); +// } +// } + dismiss(getDialogView()); + } + + private void moveBack(int newDialogHeight) { + if (getDialogImpl() != null && getDialogImpl().boxBody != null) { + View bodyView = getDialogImpl().boxBody; + if (getDialogImpl() == null || bodyView == null) return; + if (style.popNotificationSettings() != null) + align = style.popNotificationSettings().align(); + if (align == null) align = DialogXStyle.PopNotificationSettings.ALIGN.TOP; + float moveAimTop = 0; + float y = bodyView.getY(); + if (bodyView.getTag() instanceof PopValueAnimator) { + ((PopValueAnimator) bodyView.getTag()).end(); + y = ((PopValueAnimator) bodyView.getTag()).getEndValue(); + } + switch (align) { + case TOP: + moveAimTop = y + newDialogHeight * 1.1f; + break; + case TOP_INSIDE: + moveAimTop = y + newDialogHeight - bodyView.getPaddingTop(); + break; + case CENTER: + case BOTTOM: + case BOTTOM_INSIDE: + moveAimTop = y - newDialogHeight * 1.1f; + break; + } + if (moveDisplacementInterceptor != null) { + moveAimTop = moveDisplacementInterceptor.resetAnimY(popNotificationList == null ? 0 : popNotificationList.indexOf(me), me, bodyView.getY(), moveAimTop, (int) (bodyView.getHeight() / bodyView.getScaleY()), popNotificationList == null ? 1 : popNotificationList.size(), true); + } + final float fromY = bodyView.getY(); + float toY = moveAimTop; + PopValueAnimator valueAnimator = PopValueAnimator.ofFloat(bodyView.getY(), moveAimTop); + bodyView.setTag(valueAnimator); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (getDialogImpl() == null || !isShow) { + animation.cancel(); + return; + } + View bodyView = getDialogImpl().boxBody; + float value = (Float) animation.getAnimatedValue(); + float totalDistance = toY - fromY; + if (moveDisplacementInterceptor != null && moveDisplacementInterceptor.animUpdater(popNotificationList == null ? 0 : popNotificationList.indexOf(me), me, bodyView, fromY, toY, Math.max(0f, Math.min(1f, (totalDistance == 0f ? 1f : (value - fromY) / totalDistance))), animation, popNotificationList == null ? 1 : countDisplayPopNotificationNum(), true)) { + return; + } + if (bodyView != null && bodyView.isAttachedToWindow()) { + bodyView.setY(defaultTop = value); + } + } + }); + valueAnimator.setDuration(enterAnimDuration == -1 ? 300 : enterAnimDuration).setInterpolator(new DecelerateInterpolator(2f)); + valueAnimator.start(); + } + } + + private void moveFront(int newDialogHeight) { + if (getDialogImpl() != null && getDialogImpl().boxBody != null) { + View bodyView = getDialogImpl().boxBody; + if (getDialogImpl() == null || bodyView == null) return; + if (style.popNotificationSettings() != null) + align = style.popNotificationSettings().align(); + if (align == null) align = DialogXStyle.PopNotificationSettings.ALIGN.TOP; + float moveAimTop = 0; + float y = bodyView.getY(); + if (bodyView.getTag() instanceof PopValueAnimator) { + ((PopValueAnimator) bodyView.getTag()).end(); + y = ((PopValueAnimator) bodyView.getTag()).getEndValue(); + } + switch (align) { + case TOP: + moveAimTop = y - newDialogHeight * 1.1f; + break; + case TOP_INSIDE: + moveAimTop = y - newDialogHeight + bodyView.getPaddingTop(); + break; + case CENTER: + case BOTTOM: + case BOTTOM_INSIDE: + moveAimTop = y + newDialogHeight * 1.1f; + break; + } + if (moveDisplacementInterceptor != null) { + moveAimTop = moveDisplacementInterceptor.resetAnimY(popNotificationList == null ? 0 : popNotificationList.indexOf(me), me, bodyView.getY(), moveAimTop, (int) (bodyView.getHeight() / bodyView.getScaleY()), popNotificationList == null ? 1 : popNotificationList.size(), false); + } + final float fromY = bodyView.getY(); + float toY = moveAimTop; + PopValueAnimator valueAnimator = PopValueAnimator.ofFloat(fromY, toY); + bodyView.setTag(valueAnimator); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (getDialogImpl() == null || !isShow) { + animation.cancel(); + return; + } + View bodyView = getDialogImpl().boxBody; + float value = (Float) animation.getAnimatedValue(); + float totalDistance = toY - fromY; + if (moveDisplacementInterceptor != null && moveDisplacementInterceptor.animUpdater(popNotificationList == null ? 0 : popNotificationList.indexOf(me), me, bodyView, fromY, toY, Math.max(0f, Math.min(1f, (totalDistance == 0f ? 1f : (value - fromY) / totalDistance))), animation, popNotificationList == null ? 1 : countDisplayPopNotificationNum(), false)) { + return; + } + if (bodyView != null && bodyView.isAttachedToWindow()) { + bodyView.setY(defaultTop = value); + } + } + }); + valueAnimator.setDuration(exitAnimDuration == -1 ? 300 : exitAnimDuration).setInterpolator(new DecelerateInterpolator(2f)); + valueAnimator.start(); + } + } + + private int countDisplayPopNotificationNum() { + if (popNotificationList == null) return 0; + int count = 0; + for (int i = 0; i < popNotificationList.size(); i++) { + PopNotification tips = popNotificationList.get(i); + if (tips != null && !tips.preRecycle) { + count++; + } + } + return count; + } + + public void refreshUI() { + if (getDialogImpl() == null) return; + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl != null) dialogImpl.refreshView(); + } + }); + } + + public void dismiss() { + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl == null) return; + dialogImpl.doDismiss(null); + } + }); + } + + public DialogLifecycleCallback getDialogLifecycleCallback() { + return dialogLifecycleCallback == null ? new DialogLifecycleCallback() { + } : dialogLifecycleCallback; + } + + public PopNotification setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me); + return this; + } + + public PopNotification setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public PopNotification setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public PopNotification.DialogImpl getDialogImpl() { + return dialogImpl; + } + + public PopNotification setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + refreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) return null; + return onBindView.getCustomView(); + } + + public PopNotification removeCustomView() { + this.onBindView.clean(); + refreshUI(); + return this; + } + + public DialogXStyle.PopNotificationSettings.ALIGN getAlign() { + return align; + } + + public PopNotification setAlign(DialogXStyle.PopNotificationSettings.ALIGN align) { + this.align = align; + return this; + } + + public int getIconResId() { + return iconResId; + } + + public PopNotification setIconResId(int iconResId) { + this.iconResId = iconResId; + refreshUI(); + return this; + } + + public PopNotification setIcon(Bitmap bitmap) { + this.iconBitmap = bitmap; + refreshUI(); + return this; + } + + public PopNotification setIcon(Drawable iconDrawable) { + this.iconDrawable = iconDrawable; + return this; + } + + public int getIconSize() { + return iconSize; + } + + public PopNotification setIconSize(int iconSize) { + this.iconSize = iconSize; + refreshUI(); + return this; + } + + public CharSequence getMessage() { + return message; + } + + public PopNotification setMessage(CharSequence message) { + this.message = message; + refreshUI(); + return this; + } + + public PopNotification setMessage(int messageResId) { + this.message = getString(messageResId); + refreshUI(); + return this; + } + + public CharSequence getButtonText() { + return buttonText; + } + + public PopNotification setButton(CharSequence buttonText) { + this.buttonText = buttonText; + refreshUI(); + return this; + } + + public PopNotification setButton(int buttonTextResId) { + this.buttonText = getString(buttonTextResId); + refreshUI(); + return this; + } + + public PopNotification setButton(CharSequence buttonText, OnDialogButtonClickListener onButtonClickListener) { + this.buttonText = buttonText; + this.onButtonClickListener = onButtonClickListener; + refreshUI(); + return this; + } + + public PopNotification setButton(int buttonTextResId, OnDialogButtonClickListener onButtonClickListener) { + this.buttonText = getString(buttonTextResId); + this.onButtonClickListener = onButtonClickListener; + refreshUI(); + return this; + } + + public PopNotification setButton(OnDialogButtonClickListener onButtonClickListener) { + this.onButtonClickListener = onButtonClickListener; + return this; + } + + public TextInfo getMessageTextInfo() { + return messageTextInfo; + } + + public PopNotification setMessageTextInfo(TextInfo messageTextInfo) { + this.messageTextInfo = messageTextInfo; + refreshUI(); + return this; + } + + public TextInfo getButtonTextInfo() { + return buttonTextInfo; + } + + public PopNotification setButtonTextInfo(TextInfo buttonTextInfo) { + this.buttonTextInfo = buttonTextInfo; + refreshUI(); + return this; + } + + public OnDialogButtonClickListener getOnButtonClickListener() { + return onButtonClickListener; + } + + public PopNotification setOnButtonClickListener(OnDialogButtonClickListener onButtonClickListener) { + this.onButtonClickListener = onButtonClickListener; + return this; + } + + public boolean isAutoTintIconInLightOrDarkMode() { + return autoTintIconInLightOrDarkMode; + } + + public PopNotification setAutoTintIconInLightOrDarkMode(boolean autoTintIconInLightOrDarkMode) { + this.autoTintIconInLightOrDarkMode = autoTintIconInLightOrDarkMode; + refreshUI(); + return this; + } + + public OnDialogButtonClickListener getOnPopNotificationClickListener() { + return onPopNotificationClickListener; + } + + public PopNotification setOnPopNotificationClickListener(OnDialogButtonClickListener onPopNotificationClickListener) { + this.onPopNotificationClickListener = onPopNotificationClickListener; + refreshUI(); + return this; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public PopNotification setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + refreshUI(); + return this; + } + + public PopNotification setBackgroundColorRes(@ColorRes int backgroundColorResId) { + this.backgroundColor = getColor(backgroundColorResId); + refreshUI(); + return this; + } + + public long getEnterAnimDuration() { + return enterAnimDuration; + } + + public PopNotification setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public long getExitAnimDuration() { + return exitAnimDuration; + } + + public PopNotification setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + @Override + public void restartDialog() { + if (getDialogView() != null) { + dismiss(getDialogView()); + isShow = false; + } + if (getDialogImpl().boxCustom != null) { + getDialogImpl().boxCustom.removeAllViews(); + } + + if (DialogX.onlyOnePopNotification) { + PopNotification oldInstance = null; + if (popNotificationList != null && !popNotificationList.isEmpty()) { + oldInstance = popNotificationList.get(popNotificationList.size() - 1); + } + if (oldInstance != null) { + oldInstance.dismiss(); + } + } + if (popNotificationList == null) popNotificationList = new ArrayList<>(); + popNotificationList.add(PopNotification.this); + + int layoutResId = isLightTheme() ? R.layout.layout_dialogx_popnotification_material : R.layout.layout_dialogx_popnotification_material_dark; + if (style.popNotificationSettings() != null) { + if (style.popNotificationSettings().layout(isLightTheme()) != 0) { + layoutResId = style.popNotificationSettings().layout(isLightTheme()); + } + align = style.popNotificationSettings().align(); + if (align == null) align = DialogXStyle.PopNotificationSettings.ALIGN.TOP; + int styleEnterAnimResId = style.popNotificationSettings().enterAnimResId(isLightTheme()); + int styleExitAnimResId = style.popNotificationSettings().exitAnimResId(isLightTheme()); + enterAnimResId = enterAnimResId == 0 ? ( + overrideEnterAnimRes == 0 ? (styleEnterAnimResId != 0 ? styleEnterAnimResId : R.anim.anim_dialogx_notification_enter) : overrideEnterAnimRes + ) : enterAnimResId; + exitAnimResId = exitAnimResId == 0 ? ( + overrideExitAnimRes == 0 ? (styleExitAnimResId != 0 ? styleExitAnimResId : R.anim.anim_dialogx_notification_exit) : overrideExitAnimRes + ) : exitAnimResId; + enterAnimDuration = enterAnimDuration == -1 ? ( + overrideEnterDuration + ) : enterAnimDuration; + exitAnimDuration = exitAnimDuration == -1 ? ( + overrideExitDuration + ) : exitAnimDuration; + } + enterAnimDuration = 0; + View dialogView = createView(layoutResId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } + + public void hide() { + isHide = true; + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + + public PopNotification setAnimResId(int enterResId, int exitResId) { + this.enterAnimResId = enterResId; + this.exitAnimResId = exitResId; + return this; + } + + public PopNotification setEnterAnimResId(int enterResId) { + this.enterAnimResId = enterResId; + return this; + } + + public PopNotification setExitAnimResId(int exitResId) { + this.exitAnimResId = exitResId; + return this; + } + + @Override + protected void shutdown() { + dismiss(); + } + + public PopNotification setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public PopNotification setMargin(int left, int top, int right, int bottom) { + bodyMargin[0] = left; + bodyMargin[1] = top; + bodyMargin[2] = right; + bodyMargin[3] = bottom; + refreshUI(); + return this; + } + + public PopNotification setMarginLeft(int left) { + bodyMargin[0] = left; + refreshUI(); + return this; + } + + public PopNotification setMarginTop(int top) { + bodyMargin[1] = top; + refreshUI(); + return this; + } + + public PopNotification setMarginRight(int right) { + bodyMargin[2] = right; + refreshUI(); + return this; + } + + public PopNotification setMarginBottom(int bottom) { + bodyMargin[3] = bottom; + refreshUI(); + return this; + } + + public int getMarginLeft() { + return bodyMargin[0]; + } + + public int getMarginTop() { + return bodyMargin[1]; + } + + public int getMarginRight() { + return bodyMargin[2]; + } + + public int getMarginBottom() { + return bodyMargin[3]; + } + + public PopNotification iconSuccess() { + setTintIcon(false); + int resId = R.mipmap.ico_dialogx_success; + if (getStyle().popNotificationSettings() != null && getStyle().popNotificationSettings().defaultIconSuccess() != 0) { + resId = getStyle().popNotificationSettings().defaultIconSuccess(); + } + setIconSize(dip2px(26)); + setIconResId(resId); + return this; + } + + public PopNotification iconWarning() { + setTintIcon(false); + int resId = R.mipmap.ico_dialogx_warning; + if (getStyle().popNotificationSettings() != null && getStyle().popNotificationSettings().defaultIconWarning() != 0) { + resId = getStyle().popNotificationSettings().defaultIconWarning(); + } + setIconSize(dip2px(26)); + setIconResId(resId); + return this; + } + + public PopNotification iconError() { + setTintIcon(false); + int resId = R.mipmap.ico_dialogx_error; + if (getStyle().popNotificationSettings() != null && getStyle().popNotificationSettings().defaultIconError() != 0) { + resId = getStyle().popNotificationSettings().defaultIconError(); + } + setIconSize(dip2px(26)); + setIconResId(resId); + return this; + } + + public boolean getTintIcon() { + return tintIcon == BOOLEAN.TRUE; + } + + public PopNotification setTintIcon(boolean tintIcon) { + this.tintIcon = tintIcon ? BOOLEAN.TRUE : BOOLEAN.FALSE; + refreshUI(); + return this; + } + + public Drawable getIconDrawable() { + return iconDrawable; + } + + public Bitmap getIconBitmap() { + return iconBitmap; + } + + public CharSequence getTitle() { + return title; + } + + public PopNotification setTitle(CharSequence title) { + this.title = title; + refreshUI(); + return this; + } + + public TextInfo getTitleTextInfo() { + return titleTextInfo; + } + + public PopNotification setTitleTextInfo(TextInfo titleTextInfo) { + this.titleTextInfo = titleTextInfo; + refreshUI(); + return this; + } + + public PopNotification setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public float getRadius() { + return backgroundRadius; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public PopNotification setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public PopNotification setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public PopNotification setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + public PopNotification setHapticFeedbackEnabled(boolean isHapticFeedbackEnabled) { + this.isHapticFeedbackEnabled = isHapticFeedbackEnabled ? 1 : 0; + return this; + } + + public boolean isSlideToClose() { + return slideToClose; + } + + public PopNotification setSlideToClose(boolean slideToClose) { + this.slideToClose = slideToClose; + refreshUI(); + return this; + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new PopNotification() { + * + * @param dialog self + * @Override public void onShow(PopNotification dialog) { + * //... + * } + * } + */ + protected void onShow(PopNotification dialog) { + + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new PopNotification() { + * + * @param dialog self + * @Override public boolean onDismiss(PopNotification dialog) { + * WaitDialog.show("Please Wait..."); + * if (dialog.getButtonSelectResult() == BUTTON_SELECT_RESULT.BUTTON_OK) { + * //点击了OK的情况 + * //... + * } else { + * //其他按钮点击、对话框dismiss的情况 + * //... + * } + * return false; + * } + * } + */ + //用于使用 new 构建实例时,override 的生命周期事件 + protected void onDismiss(PopNotification dialog) { + + } + + public PopNotification setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public PopNotification onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public PopNotification onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public PopNotification appendMessage(CharSequence message) { + this.message = TextUtils.concat(this.message, message); + refreshUI(); + return this; + } + + public PopNotification setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex(" + orderIndex + ") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public PopNotification bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public PopNotification setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public PopNotification cleanAction(int actionId) { + dialogActionRunnableMap.remove(actionId); + return this; + } + + public PopNotification cleanAllAction() { + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss() { + dismiss(); + } + + public PopNotification bindDismissWithLifecycleOwner(LifecycleOwner owner) { + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopTip.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopTip.java new file mode 100644 index 0000000..500edc3 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopTip.java @@ -0,0 +1,1566 @@ +package com.kongzue.dialogx.dialogs; + +import android.animation.ValueAnimator; +import android.app.Activity; +import android.graphics.Outline; +import android.graphics.Rect; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.DecelerateInterpolator; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.annotation.Nullable; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.BlurViewType; +import com.kongzue.dialogx.interfaces.DialogConvertViewInterface; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.NoTouchInterface; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.OnDialogButtonClickListener; +import com.kongzue.dialogx.interfaces.OnSafeInsetsChangeListener; +import com.kongzue.dialogx.interfaces.PopMoveDisplacementInterceptor; +import com.kongzue.dialogx.util.TextInfo; +import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/20 11:59 + */ +public class PopTip extends BaseDialog implements NoTouchInterface { + + public static final int TIME_NO_AUTO_DISMISS_DELAY = -1; + protected static List popTipList; + + public static long overrideEnterDuration = -1; + public static long overrideExitDuration = -1; + public static int overrideEnterAnimRes = 0; + public static int overrideExitAnimRes = 0; + public static int maxShowCount = Integer.MAX_VALUE; + public static PopMoveDisplacementInterceptor moveDisplacementInterceptor; + + protected OnBindView onBindView; + protected DialogLifecycleCallback dialogLifecycleCallback; + protected PopTip me = this; + protected DialogImpl dialogImpl; + protected int enterAnimResId = 0; + protected int exitAnimResId = 0; + protected DialogXStyle.PopTipSettings.ALIGN align; + protected OnDialogButtonClickListener onButtonClickListener; + protected OnDialogButtonClickListener onPopTipClickListener; + protected BOOLEAN tintIcon; + protected float backgroundRadius = DialogX.defaultPopTipBackgroundRadius; + protected DialogXAnimInterface dialogXAnimImpl; + + protected int iconResId; + protected CharSequence message; + protected CharSequence buttonText; + + protected TextInfo messageTextInfo; + protected TextInfo buttonTextInfo = new TextInfo().setBold(true); + protected int[] bodyMargin = new int[]{-1, -1, -1, -1}; + + protected PopTip() { + super(); + } + + @Override + public boolean isCancelable() { + return false; + } + + public static PopTip build() { + return new PopTip(); + } + + public static PopTip build(DialogXStyle style) { + return new PopTip().setStyle(style); + } + + public static PopTip build(OnBindView onBindView) { + return new PopTip().setCustomView(onBindView); + } + + public PopTip(OnBindView onBindView) { + this.onBindView = onBindView; + } + + public PopTip(CharSequence message) { + this.message = message; + } + + public PopTip(int messageResId) { + this.message = getString(messageResId); + } + + public static PopTip tip(String message) { + return PopTip.show(message); + } + + public static PopTip tip(int messageResId) { + return PopTip.show(messageResId); + } + + public static PopTip tip(String message, String buttonText) { + return PopTip.show(message, buttonText); + } + + public static PopTip tip(int messageResId, int buttonTextResId) { + return PopTip.show(messageResId, buttonTextResId); + } + + public static PopTip tip(int iconResId, String message) { + return PopTip.show(iconResId, message); + } + + public static PopTip tip(int iconResId, String message, String buttonText) { + return PopTip.show(iconResId, message, buttonText); + } + + public PopTip(int iconResId, CharSequence message) { + this.iconResId = iconResId; + this.message = message; + } + + public PopTip(int iconResId, CharSequence message, CharSequence buttonText) { + this.iconResId = iconResId; + this.message = message; + this.buttonText = buttonText; + } + + public PopTip(int iconResId, int messageResId, int buttonTextResId) { + this.iconResId = iconResId; + this.message = getString(messageResId); + this.buttonText = getString(buttonTextResId); + } + + public PopTip(CharSequence message, CharSequence buttonText) { + this.message = message; + this.buttonText = buttonText; + } + + public PopTip(int messageResId, int buttonTextResId) { + this.message = getString(messageResId); + this.buttonText = getString(buttonTextResId); + } + + public PopTip(CharSequence message, OnBindView onBindView) { + this.message = message; + this.onBindView = onBindView; + } + + public PopTip(int messageResId, OnBindView onBindView) { + this.message = getString(messageResId); + this.onBindView = onBindView; + } + + public PopTip(int iconResId, CharSequence message, OnBindView onBindView) { + this.iconResId = iconResId; + this.message = message; + this.onBindView = onBindView; + } + + public PopTip(int iconResId, CharSequence message, CharSequence buttonText, OnBindView onBindView) { + this.iconResId = iconResId; + this.message = message; + this.buttonText = buttonText; + this.onBindView = onBindView; + } + + public PopTip(int iconResId, int messageResId, int buttonTextResId, OnBindView onBindView) { + this.iconResId = iconResId; + this.message = getString(messageResId); + this.buttonText = getString(buttonTextResId); + this.onBindView = onBindView; + } + + public PopTip(CharSequence message, CharSequence buttonText, OnBindView onBindView) { + this.message = message; + this.buttonText = buttonText; + this.onBindView = onBindView; + } + + public PopTip(int messageResId, int buttonTextResId, OnBindView onBindView) { + this.message = getString(messageResId); + this.buttonText = getString(buttonTextResId); + this.onBindView = onBindView; + } + + public static PopTip show(OnBindView onBindView) { + PopTip popTip = new PopTip(onBindView); + popTip.show(); + return popTip; + } + + public static PopTip show(CharSequence message) { + PopTip popTip = new PopTip(message); + popTip.show(); + return popTip; + } + + public static PopTip show(int messageResId) { + PopTip popTip = new PopTip(messageResId); + popTip.show(); + return popTip; + } + + public static PopTip show(CharSequence message, OnBindView onBindView) { + PopTip popTip = new PopTip(message, onBindView); + popTip.show(); + return popTip; + } + + public static PopTip show(int messageResId, OnBindView onBindView) { + PopTip popTip = new PopTip(messageResId, onBindView); + popTip.show(); + return popTip; + } + + public static PopTip show(CharSequence message, CharSequence buttonText) { + PopTip popTip = new PopTip(message, buttonText); + popTip.show(); + return popTip; + } + + public static PopTip show(int messageResId, int buttonTextResId) { + PopTip popTip = new PopTip(messageResId, buttonTextResId); + popTip.show(); + return popTip; + } + + public static PopTip show(int iconResId, CharSequence message, OnBindView onBindView) { + PopTip popTip = new PopTip(iconResId, message, onBindView); + popTip.show(); + return popTip; + } + + public static PopTip show(int iconResId, CharSequence message) { + PopTip popTip = new PopTip(iconResId, message); + popTip.show(); + return popTip; + } + + public static PopTip show(int iconResId, CharSequence message, CharSequence buttonText) { + PopTip popTip = new PopTip(iconResId, message, buttonText); + popTip.show(); + return popTip; + } + + public static PopTip show(int iconResId, CharSequence message, CharSequence buttonText, OnBindView onBindView) { + PopTip popTip = new PopTip(iconResId, message, buttonText, onBindView); + popTip.show(); + return popTip; + } + + public static PopTip show(int iconResId, int messageResId, int buttonTextResId, OnBindView onBindView) { + PopTip popTip = new PopTip(iconResId, messageResId, buttonTextResId, onBindView); + popTip.show(); + return popTip; + } + + public static PopTip show(CharSequence message, CharSequence buttonText, OnBindView onBindView) { + PopTip popTip = new PopTip(message, buttonText, onBindView); + popTip.show(); + return popTip; + } + + public static PopTip show(int messageResId, int buttonTextResId, OnBindView onBindView) { + PopTip popTip = new PopTip(messageResId, buttonTextResId, onBindView); + popTip.show(); + return popTip; + } + + public PopTip show() { + if (isHide && getDialogView() != null) { + getDialogView().setVisibility(View.VISIBLE); + return this; + } + super.beforeShow(); + if (getDialogView() == null) { + if (DialogX.onlyOnePopTip) { + PopTip oldInstance = null; + if (popTipList != null && !popTipList.isEmpty()) { + oldInstance = popTipList.get(popTipList.size() - 1); + } + if (oldInstance != null) { + oldInstance.dismiss(); + } + } else { + if (popTipList != null) { + CopyOnWriteArrayList copyPopTipList = new CopyOnWriteArrayList<>(popTipList); + for (int i = 0; i < copyPopTipList.size(); i++) { + PopTip popInstance = copyPopTipList.get(i); + if (copyPopTipList.size() < maxShowCount) { + popInstance.moveBack(); + } else { + if (i <= copyPopTipList.size() - maxShowCount) { + popInstance.dismiss(); + popTipList.remove(popInstance); + } else { + popInstance.moveBack(); + } + } + } + } + } + if (popTipList == null) popTipList = new ArrayList<>(); + popTipList.add(PopTip.this); + int layoutResId = isLightTheme() ? R.layout.layout_dialogx_poptip_material : R.layout.layout_dialogx_poptip_material_dark; + if (style.popTipSettings() != null) { + if (style.popTipSettings().layout(isLightTheme()) != 0) { + layoutResId = style.popTipSettings().layout(isLightTheme()); + } + if (align == null) { + if (style.popTipSettings().align() == null) { + align = DialogXStyle.PopTipSettings.ALIGN.BOTTOM; + } else { + align = style.popTipSettings().align(); + } + } + int styleEnterAnimResId = style.popTipSettings().enterAnimResId(isLightTheme()); + int styleExitAnimResId = style.popTipSettings().exitAnimResId(isLightTheme()); + enterAnimResId = enterAnimResId == 0 ? ( + overrideEnterAnimRes == 0 ? (styleEnterAnimResId != 0 ? styleEnterAnimResId : R.anim.anim_dialogx_default_enter) : overrideEnterAnimRes + ) : enterAnimResId; + exitAnimResId = exitAnimResId == 0 ? ( + overrideExitAnimRes == 0 ? (styleExitAnimResId != 0 ? styleExitAnimResId : R.anim.anim_dialogx_default_exit) : overrideExitAnimRes + ) : exitAnimResId; + enterAnimDuration = enterAnimDuration == -1 ? ( + overrideEnterDuration + ) : enterAnimDuration; + exitAnimDuration = exitAnimDuration == -1 ? ( + overrideExitDuration + ) : exitAnimDuration; + } + View dialogView = createView(layoutResId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } else { + show(getDialogView()); + } + return this; + } + + public PopTip show(Activity activity) { + super.beforeShow(); + if (getDialogView() == null) { + if (DialogX.onlyOnePopTip) { + PopTip oldInstance = null; + if (popTipList != null && !popTipList.isEmpty()) { + oldInstance = popTipList.get(popTipList.size() - 1); + } + if (oldInstance != null) { + oldInstance.dismiss(); + } + } else { + if (popTipList != null) { + CopyOnWriteArrayList copyPopTipList = new CopyOnWriteArrayList<>(popTipList); + for (int i = 0; i < copyPopTipList.size(); i++) { + PopTip popInstance = copyPopTipList.get(i); + if (copyPopTipList.size() < maxShowCount) { + popInstance.moveBack(); + } else { + if (i <= copyPopTipList.size() - maxShowCount) { + popInstance.dismiss(); + popTipList.remove(popInstance); + } else { + popInstance.moveBack(); + } + } + } + } + } + if (popTipList == null) popTipList = new ArrayList<>(); + popTipList.add(PopTip.this); + int layoutResId = isLightTheme() ? R.layout.layout_dialogx_poptip_material : R.layout.layout_dialogx_poptip_material_dark; + if (style.popTipSettings() != null) { + if (style.popTipSettings().layout(isLightTheme()) != 0) { + layoutResId = style.popTipSettings().layout(isLightTheme()); + } + + if (align == null) { + if (style.popTipSettings().align() == null) { + align = DialogXStyle.PopTipSettings.ALIGN.BOTTOM; + } else { + align = style.popTipSettings().align(); + } + } + int styleEnterAnimResId = style.popTipSettings().enterAnimResId(isLightTheme()); + int styleExitAnimResId = style.popTipSettings().exitAnimResId(isLightTheme()); + enterAnimResId = enterAnimResId == 0 ? ( + overrideEnterAnimRes == 0 ? (styleEnterAnimResId != 0 ? styleEnterAnimResId : R.anim.anim_dialogx_default_enter) : overrideEnterAnimRes + ) : enterAnimResId; + exitAnimResId = exitAnimResId == 0 ? ( + overrideExitAnimRes == 0 ? (styleExitAnimResId != 0 ? styleExitAnimResId : R.anim.anim_dialogx_default_exit) : overrideExitAnimRes + ) : exitAnimResId; + enterAnimDuration = enterAnimDuration == -1 ? ( + overrideEnterDuration + ) : enterAnimDuration; + exitAnimDuration = exitAnimDuration == -1 ? ( + overrideExitDuration + ) : exitAnimDuration; + } + View dialogView = createView(layoutResId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(activity, dialogView); + } else { + show(activity, getDialogView()); + } + return this; + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + protected Timer autoDismissTimer; + protected long autoDismissDelay; + + public PopTip autoDismiss(long delay) { + autoDismissDelay = delay; + if (autoDismissTimer != null) { + autoDismissTimer.cancel(); + } + if (delay < 0) return this; + autoDismissTimer = new Timer(); + autoDismissTimer.schedule(new TimerTask() { + @Override + public void run() { + dismiss(); + } + }, delay); + return this; + } + + public void resetAutoDismissTimer() { + autoDismiss(autoDismissDelay); + } + + public PopTip showShort() { + autoDismiss(2000); + if (!preShow && !isShow) { + show(); + } + return this; + } + + public PopTip showLong() { + autoDismiss(3500); + if (!preShow && !isShow) { + show(); + } + return this; + } + + public PopTip showAlways() { + return noAutoDismiss(); + } + + public PopTip noAutoDismiss() { + autoDismiss(TIME_NO_AUTO_DISMISS_DELAY); + return this; + } + + public class DialogImpl implements DialogConvertViewInterface { + + public DialogXBaseRelativeLayout boxRoot; + public LinearLayout boxBody; + public ImageView imgDialogxPopIcon; + public TextView txtDialogxPopText; + public RelativeLayout boxCustom; + public TextView txtDialogxButton; + + private List blurViews; + + public DialogImpl(View convertView) { + if (convertView == null) return; + setDialogView(convertView); + boxRoot = convertView.findViewById(R.id.box_root); + boxBody = convertView.findViewById(R.id.box_body); + imgDialogxPopIcon = convertView.findViewById(R.id.img_dialogx_pop_icon); + txtDialogxPopText = convertView.findViewById(R.id.txt_dialogx_pop_text); + boxCustom = convertView.findViewById(R.id.box_custom); + txtDialogxButton = convertView.findViewById(R.id.txt_dialogx_button); + + blurViews = findAllBlurView(convertView); + + init(); + dialogImpl = this; + refreshView(); + } + + @Override + public void init() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(getThisOrderIndex()); + } + + if (messageTextInfo == null) messageTextInfo = DialogX.popTextInfo; + if (buttonTextInfo == null) buttonTextInfo = DialogX.buttonTextInfo; + if (backgroundColor == null) backgroundColor = DialogX.backgroundColor; + + if (autoDismissTimer == null) { + showShort(); + } + + boxRoot.setParentDialog(me); + boxRoot.setAutoUnsafePlacePadding(true); + boxRoot.setOnLifecycleCallBack(new DialogXBaseRelativeLayout.OnLifecycleCallBack() { + @Override + public void onShow() { + isShow = true; + preShow = false; + setLifecycleState(Lifecycle.State.CREATED); + boxRoot.setAlpha(0f); + onDialogShow(); + getDialogLifecycleCallback().onShow(me); + PopTip.this.onShow(me); + } + + @Override + public void onDismiss() { + if (popTipList != null) { + popTipList.remove(PopTip.this); + if (popTipList.isEmpty()) { + popTipList = null; + } + } + isShow = false; + + if (autoDismissTimer != null) { + autoDismissTimer.cancel(); + } + getDialogLifecycleCallback().onDismiss(me); + PopTip.this.onDismiss(me); + setLifecycleState(Lifecycle.State.DESTROYED); + dialogImpl = null; + System.gc(); + } + }); + + applyPopTipAlign(); + + boxRoot.setOnSafeInsetsChangeListener(new OnSafeInsetsChangeListener() { + @Override + public void onChange(Rect unsafeRect) { + if (align == DialogXStyle.PopTipSettings.ALIGN.TOP_INSIDE) { + boxBody.setPadding(0, unsafeRect.top, 0, 0); + } + } + }); + + boxRoot.setOnBackPressedListener(new DialogXBaseRelativeLayout.PrivateBackPressedListener() { + @Override + public boolean onBackPressed() { + return false; + } + }); + + boxRoot.post(new Runnable() { + @Override + public void run() { + getDialogXAnimImpl().doShowAnim(me, boxBody); + setLifecycleState(Lifecycle.State.RESUMED); + } + }); + + txtDialogxButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + haptic(v); + if (onButtonClickListener != null) { + if (!onButtonClickListener.onClick(me, v)) { + doDismiss(v); + } + } else { + doDismiss(v); + } + } + }); + onDialogInit(); + } + + private void applyPopTipAlign() { + RelativeLayout.LayoutParams rlp; + rlp = ((RelativeLayout.LayoutParams) boxBody.getLayoutParams()); + if (align == null) align = DialogXStyle.PopTipSettings.ALIGN.BOTTOM; + switch (align) { + case TOP: + rlp.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_TOP); + break; + case BOTTOM: + rlp.removeRule(RelativeLayout.ALIGN_PARENT_TOP); + rlp.removeRule(RelativeLayout.CENTER_IN_PARENT); + rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + break; + case CENTER: + rlp.removeRule(RelativeLayout.ALIGN_PARENT_TOP); + rlp.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + rlp.addRule(RelativeLayout.CENTER_IN_PARENT); + break; + } + boxBody.setLayoutParams(rlp); + } + + @Override + public void refreshView() { + if (boxRoot == null || getOwnActivity() == null) { + return; + } + boxRoot.setRootPadding(screenPaddings[0], screenPaddings[1], screenPaddings[2], screenPaddings[3]); + if (backgroundColor != null) { + tintColor(boxBody, backgroundColor); + tintColor(txtDialogxButton, backgroundColor); + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setOverlayColor(backgroundColor); + } + } + } + + if (onBindView != null && onBindView.getCustomView() != null) { + onBindView.bindParent(boxCustom, me); + boxCustom.setVisibility(View.VISIBLE); + } else { + boxCustom.setVisibility(View.GONE); + } + + showText(txtDialogxPopText, message); + showText(txtDialogxButton, buttonText); + + useTextInfo(txtDialogxPopText, messageTextInfo); + useTextInfo(txtDialogxButton, buttonTextInfo); + + if (iconResId != 0) { + imgDialogxPopIcon.setVisibility(View.VISIBLE); + imgDialogxPopIcon.setImageResource(iconResId); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (isTintIcon()) { + imgDialogxPopIcon.setImageTintList(txtDialogxPopText.getTextColors()); + } else { + imgDialogxPopIcon.setImageTintList(null); + } + } + } else { + imgDialogxPopIcon.setVisibility(View.GONE); + } + + if (backgroundRadius > -1) { + if (boxBody.getBackground() instanceof GradientDrawable) { + GradientDrawable gradientDrawable = (GradientDrawable) boxBody.getBackground(); + if (gradientDrawable != null) + gradientDrawable.setCornerRadius(backgroundRadius); + } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + boxBody.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), backgroundRadius); + } + }); + boxBody.setClipToOutline(true); + } + + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setRadiusPx(backgroundRadius); + } + } + } + + if (onPopTipClickListener != null) { + boxBody.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (!onPopTipClickListener.onClick(me, v)) { + haptic(v); + dismiss(); + } + } + }); + } else { + boxBody.setOnClickListener(null); + boxBody.setClickable(false); + } + + RelativeLayout.LayoutParams rlp = ((RelativeLayout.LayoutParams) boxBody.getLayoutParams()); + if (bodyMargin[0] != -1) rlp.leftMargin = bodyMargin[0]; + if (bodyMargin[1] != -1) rlp.topMargin = bodyMargin[1]; + if (bodyMargin[2] != -1) rlp.rightMargin = bodyMargin[2]; + if (bodyMargin[3] != -1) rlp.bottomMargin = bodyMargin[3]; + boxBody.setLayoutParams(rlp); + onDialogRefreshUI(); + } + + @Override + public void doDismiss(final View v) { + if (PopTip.this.preDismiss(PopTip.this)) { + return; + } + if (v != null) v.setEnabled(false); + + if (!dismissAnimFlag && boxRoot != null) { + dismissAnimFlag = true; + boxRoot.post(new Runnable() { + @Override + public void run() { + getDialogXAnimImpl().doExitAnim(me, boxBody); + + preRecycle = true; + runOnMainDelay(new Runnable() { + @Override + public void run() { + waitForDismiss(); + } + }, getExitAnimationDuration(null)); + + if (popTipList != null) { + //使位于自己之前的PopTip moveDown + int index = popTipList.indexOf(me); + for (int i = 0; i < index; i++) { + PopTip popTip = popTipList.get(i); + popTip.moveFront(); + } + } + } + }); + } + } + + protected DialogXAnimInterface getDialogXAnimImpl() { + if (dialogXAnimImpl == null) { + dialogXAnimImpl = new DialogXAnimInterface() { + @Override + public void doShowAnim(PopTip dialog, ViewGroup dialogBodyView) { + Animation enterAnim = AnimationUtils.loadAnimation(getOwnActivity(), enterAnimResId == 0 ? R.anim.anim_dialogx_default_enter : enterAnimResId); + long enterAnimDuration = getEnterAnimationDuration(enterAnim); + enterAnim.setInterpolator(new DecelerateInterpolator(2f)); + enterAnim.setDuration(enterAnimDuration); + enterAnim.setFillAfter(true); + boxBody.startAnimation(enterAnim); + + boxRoot.animate() + .setDuration(enterAnimDuration) + .alpha(1f) + .setInterpolator(new DecelerateInterpolator()) + .setListener(null); + } + + @Override + public void doExitAnim(PopTip dialog, ViewGroup dialogBodyView) { + Animation exitAnim = AnimationUtils.loadAnimation(getOwnActivity() == null ? boxRoot.getContext() : getOwnActivity(), exitAnimResId == 0 ? R.anim.anim_dialogx_default_exit : exitAnimResId); + long exitAnimDuration = getExitAnimationDuration(exitAnim); + exitAnim.setDuration(exitAnimDuration); + exitAnim.setFillAfter(true); + boxBody.startAnimation(exitAnim); + + boxRoot.animate() + .alpha(0f) + .setInterpolator(new AccelerateInterpolator()) + .setDuration(exitAnimDuration); + } + }; + } + return dialogXAnimImpl; + } + + public long getExitAnimationDuration(@Nullable Animation defaultExitAnim) { + if (defaultExitAnim == null && boxBody.getAnimation() != null) { + defaultExitAnim = boxBody.getAnimation(); + } + long exitAnimDurationTemp = (defaultExitAnim == null || defaultExitAnim.getDuration() == 0) ? 300 : defaultExitAnim.getDuration(); + if (overrideExitDuration >= 0) { + exitAnimDurationTemp = overrideExitDuration; + } + if (exitAnimDuration != -1) { + exitAnimDurationTemp = exitAnimDuration; + } + return exitAnimDurationTemp; + } + + public long getEnterAnimationDuration(@Nullable Animation defaultEnterAnim) { + if (defaultEnterAnim == null && boxBody.getAnimation() != null) { + defaultEnterAnim = boxBody.getAnimation(); + } + long enterAnimDurationTemp = (defaultEnterAnim == null || defaultEnterAnim.getDuration() == 0) ? 300 : defaultEnterAnim.getDuration(); + if (overrideEnterDuration >= 0) { + enterAnimDurationTemp = overrideEnterDuration; + } + if (enterAnimDuration >= 0) { + enterAnimDurationTemp = enterAnimDuration; + } + return enterAnimDurationTemp; + } + } + + protected boolean preRecycle = false; + + /** + * 之所以这样处理,在较为频繁的启停 PopTip 时可能存在 PopTip 关闭动画位置错误无法计算的问题 + * 使用 preRecycle 标记记录是否需要回收,而不是立即销毁 + * 等待所有 PopTip 处于待回收状态时一并回收可以避免此问题 + */ + private void waitForDismiss() { + if (popTipList == null || popTipList.isEmpty()) { + return; + } + preRecycle = true; + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + CopyOnWriteArrayList copyPopTipList = new CopyOnWriteArrayList<>(popTipList); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + copyPopTipList.removeIf(Objects::isNull); + } else { + Iterator iterator = copyPopTipList.iterator(); + while (iterator.hasNext()) { + if (iterator.next() == null) { + iterator.remove(); + } + } + } + dismiss(getDialogView()); + } + + private void moveBack() { + if (getDialogImpl() != null && getDialogImpl().boxBody != null) { + if (getDialogImpl() == null || getDialogImpl().boxBody == null) return; + View bodyView = getDialogImpl().boxBody; + bodyView.post(new Runnable() { + @Override + public void run() { + if (getDialogImpl() == null) { + return; + } + if (align == null && style.popTipSettings() != null) { + align = style.popTipSettings().align(); + } + if (align == null) align = DialogXStyle.PopTipSettings.ALIGN.TOP; + float moveAimTop = 0; + switch (align) { + case TOP: + moveAimTop = bodyView.getY() + bodyView.getHeight() * 1.3f; + break; + case TOP_INSIDE: + moveAimTop = bodyView.getY() + bodyView.getHeight() - bodyView.getPaddingTop(); + break; + case CENTER: + case BOTTOM: + case BOTTOM_INSIDE: + moveAimTop = bodyView.getY() - bodyView.getHeight() * 1.3f; + break; + } + if (moveDisplacementInterceptor != null) { + moveAimTop = moveDisplacementInterceptor.resetAnimY(popTipList == null ? 0 : popTipList.indexOf(me), me, bodyView.getY(), moveAimTop, (int) (bodyView.getHeight() / bodyView.getScaleY()), popTipList == null ? 1 : popTipList.size(), true); + } + if (bodyView.getTag() instanceof ValueAnimator) { + ((ValueAnimator) bodyView.getTag()).end(); + } + //log("#Animation from:" + bodyView.getY() + " to:" + moveAimTop); + final float fromY = bodyView.getY(); + float toY = moveAimTop; + ValueAnimator valueAnimator = ValueAnimator.ofFloat(fromY, toY); + bodyView.setTag(valueAnimator); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (getDialogImpl() == null || !isShow) { + animation.cancel(); + return; + } + View bodyView = getDialogImpl().boxBody; + float value = (Float) animation.getAnimatedValue(); + float totalDistance = toY - fromY; + if (moveDisplacementInterceptor != null && moveDisplacementInterceptor.animUpdater(popTipList == null ? 0 : popTipList.indexOf(me), me, bodyView, fromY, toY, Math.max(0f, Math.min(1f, (totalDistance == 0f ? 1f : (value - fromY) / totalDistance))), animation, popTipList == null ? 1 : countDisplayPopTipsNum(), true)) { + return; + } + if (bodyView != null && bodyView.isAttachedToWindow()) { + bodyView.setY(value); + } + } + }); + valueAnimator.setDuration(enterAnimDuration == -1 ? 300 : enterAnimDuration).setInterpolator(new DecelerateInterpolator(2f)); + valueAnimator.start(); + } + }); + } + } + + private void moveFront() { + if (getDialogImpl() != null && getDialogImpl().boxBody != null) { + if (getDialogImpl() == null || getDialogImpl().boxBody == null) return; + View bodyView = getDialogImpl().boxBody; + bodyView.post(new Runnable() { + @Override + public void run() { + if (getDialogImpl() == null) { + return; + } + if (align == null && style.popTipSettings() != null) { + align = style.popTipSettings().align(); + } + if (align == null) align = DialogXStyle.PopTipSettings.ALIGN.TOP; + float moveAimTop = 0; + switch (align) { + case TOP: + moveAimTop = bodyView.getY() - bodyView.getHeight() * 1.3f; + break; + case TOP_INSIDE: + moveAimTop = bodyView.getY() - bodyView.getHeight() + bodyView.getPaddingTop(); + break; + case CENTER: + case BOTTOM: + case BOTTOM_INSIDE: + moveAimTop = bodyView.getY() + bodyView.getHeight() * 1.3f; + break; + } + if (moveDisplacementInterceptor != null) { + moveAimTop = moveDisplacementInterceptor.resetAnimY(popTipList == null ? 0 : popTipList.indexOf(me), me, bodyView.getY(), moveAimTop, (int) (bodyView.getHeight() / bodyView.getScaleY()), popTipList == null ? 1 : popTipList.size(), false); + } + if (bodyView.getTag() instanceof ValueAnimator) { + ((ValueAnimator) bodyView.getTag()).end(); + } + //log("#Animation from:" + bodyView.getY() + " to:" + moveAimTop); + final float fromY = bodyView.getY(); + float toY = moveAimTop; + ValueAnimator valueAnimator = ValueAnimator.ofFloat(fromY, toY); + bodyView.setTag(valueAnimator); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (getDialogImpl() == null || !isShow) { + animation.cancel(); + return; + } + View bodyView = getDialogImpl().boxBody; + float value = (Float) animation.getAnimatedValue(); + float totalDistance = toY - fromY; + if (moveDisplacementInterceptor != null && moveDisplacementInterceptor.animUpdater(popTipList == null ? 0 : popTipList.indexOf(me), me, bodyView, fromY, toY, Math.max(0f, Math.min(1f, (totalDistance == 0f ? 1f : (value - fromY) / totalDistance))), animation, popTipList == null ? 1 : countDisplayPopTipsNum(), false)) { + return; + } + if (bodyView != null && bodyView.isAttachedToWindow()) { + bodyView.setY(value); + } + } + }); + valueAnimator.setDuration(exitAnimDuration == -1 ? 300 : exitAnimDuration).setInterpolator(new AccelerateInterpolator(2f)); + valueAnimator.start(); + } + }); + } + } + + private int countDisplayPopTipsNum() { + if (popTipList == null) return 0; + int count = 0; + for (int i = 0; i < popTipList.size(); i++) { + PopTip tips = popTipList.get(i); + if (tips != null && !tips.preRecycle) { + count++; + } + } + return count; + } + + public void refreshUI() { + if (getDialogImpl() == null) return; + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl != null) dialogImpl.refreshView(); + } + }); + } + + public void dismiss() { + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogImpl == null) return; + dialogImpl.doDismiss(null); + } + }); + } + + public DialogLifecycleCallback getDialogLifecycleCallback() { + return dialogLifecycleCallback == null ? new DialogLifecycleCallback() { + } : dialogLifecycleCallback; + } + + public PopTip setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me); + return this; + } + + public PopTip setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public PopTip setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public PopTip.DialogImpl getDialogImpl() { + return dialogImpl; + } + + public PopTip setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + refreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) return null; + return onBindView.getCustomView(); + } + + public PopTip removeCustomView() { + this.onBindView.clean(); + refreshUI(); + return this; + } + + public DialogXStyle.PopTipSettings.ALIGN getAlign() { + return align; + } + + public PopTip setAlign(DialogXStyle.PopTipSettings.ALIGN align) { + this.align = align; + if (getDialogImpl() != null) { + getDialogImpl().applyPopTipAlign(); + } + return this; + } + + public int getIconResId() { + return iconResId; + } + + public PopTip setIconResId(int iconResId) { + this.iconResId = iconResId; + refreshUI(); + return this; + } + + public CharSequence getMessage() { + return message; + } + + public PopTip setMessage(CharSequence message) { + this.message = message; + refreshUI(); + return this; + } + + public PopTip setMessage(int messageResId) { + this.message = getString(messageResId); + refreshUI(); + return this; + } + + public CharSequence getButtonText() { + return buttonText; + } + + public PopTip setButton(CharSequence buttonText) { + this.buttonText = buttonText; + refreshUI(); + return this; + } + + public PopTip setButton(int buttonTextResId) { + this.buttonText = getString(buttonTextResId); + refreshUI(); + return this; + } + + public PopTip setButton(CharSequence buttonText, OnDialogButtonClickListener onButtonClickListener) { + this.buttonText = buttonText; + this.onButtonClickListener = onButtonClickListener; + refreshUI(); + return this; + } + + public PopTip setButton(int buttonTextResId, OnDialogButtonClickListener onButtonClickListener) { + this.buttonText = getString(buttonTextResId); + this.onButtonClickListener = onButtonClickListener; + refreshUI(); + return this; + } + + public PopTip setButton(OnDialogButtonClickListener onButtonClickListener) { + this.onButtonClickListener = onButtonClickListener; + return this; + } + + public TextInfo getMessageTextInfo() { + return messageTextInfo; + } + + public PopTip setMessageTextInfo(TextInfo messageTextInfo) { + this.messageTextInfo = messageTextInfo; + refreshUI(); + return this; + } + + public TextInfo getButtonTextInfo() { + return buttonTextInfo; + } + + public PopTip setButtonTextInfo(TextInfo buttonTextInfo) { + this.buttonTextInfo = buttonTextInfo; + refreshUI(); + return this; + } + + public OnDialogButtonClickListener getOnButtonClickListener() { + return onButtonClickListener; + } + + public PopTip setOnButtonClickListener(OnDialogButtonClickListener onButtonClickListener) { + this.onButtonClickListener = onButtonClickListener; + return this; + } + + @Deprecated + public boolean isAutoTintIconInLightOrDarkMode() { + return isTintIcon(); + } + + @Deprecated + public PopTip setAutoTintIconInLightOrDarkMode(boolean autoTintIconInLightOrDarkMode) { + setTintIcon(autoTintIconInLightOrDarkMode); + return this; + } + + public OnDialogButtonClickListener getOnPopTipClickListener() { + return onPopTipClickListener; + } + + public PopTip setOnPopTipClickListener(OnDialogButtonClickListener onPopTipClickListener) { + this.onPopTipClickListener = onPopTipClickListener; + refreshUI(); + return this; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public PopTip setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + refreshUI(); + return this; + } + + public PopTip setBackgroundColorRes(@ColorRes int backgroundColorResId) { + this.backgroundColor = getColor(backgroundColorResId); + refreshUI(); + return this; + } + + public long getEnterAnimDuration() { + return enterAnimDuration; + } + + public PopTip setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public long getExitAnimDuration() { + return exitAnimDuration; + } + + public PopTip setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + @Override + public void restartDialog() { + if (getDialogView() != null) { + dismiss(getDialogView()); + isShow = false; + } + if (getDialogImpl().boxCustom != null) { + getDialogImpl().boxCustom.removeAllViews(); + } + + if (DialogX.onlyOnePopTip) { + PopTip oldInstance = null; + if (popTipList != null && !popTipList.isEmpty()) { + oldInstance = popTipList.get(popTipList.size() - 1); + } + if (oldInstance != null) { + oldInstance.dismiss(); + } + } else { + if (popTipList != null) { + CopyOnWriteArrayList copyPopTipList = new CopyOnWriteArrayList<>(popTipList); + for (int i = 0; i < copyPopTipList.size(); i++) { + PopTip popInstance = copyPopTipList.get(i); + if (copyPopTipList.size() < maxShowCount) { + popInstance.moveBack(); + } else { + if (i <= copyPopTipList.size() - maxShowCount) { + popInstance.dismiss(); + popTipList.remove(popInstance); + } else { + popInstance.moveBack(); + } + } + } + } + } + if (popTipList == null) popTipList = new ArrayList<>(); + popTipList.add(PopTip.this); + + int layoutResId = isLightTheme() ? R.layout.layout_dialogx_poptip_material : R.layout.layout_dialogx_poptip_material_dark; + if (style.popTipSettings() != null) { + if (style.popTipSettings().layout(isLightTheme()) != 0) { + layoutResId = style.popTipSettings().layout(isLightTheme()); + } + if (align == null) { + if (style.popTipSettings().align() == null) { + align = DialogXStyle.PopTipSettings.ALIGN.BOTTOM; + } else { + align = style.popTipSettings().align(); + } + } + int styleEnterAnimResId = style.popTipSettings().enterAnimResId(isLightTheme()); + int styleExitAnimResId = style.popTipSettings().exitAnimResId(isLightTheme()); + enterAnimResId = enterAnimResId == 0 ? ( + overrideEnterAnimRes == 0 ? (styleEnterAnimResId != 0 ? styleEnterAnimResId : R.anim.anim_dialogx_default_enter) : overrideEnterAnimRes + ) : enterAnimResId; + exitAnimResId = exitAnimResId == 0 ? ( + overrideExitAnimRes == 0 ? (styleExitAnimResId != 0 ? styleExitAnimResId : R.anim.anim_dialogx_default_exit) : overrideExitAnimRes + ) : exitAnimResId; + enterAnimDuration = enterAnimDuration == -1 ? ( + overrideEnterDuration + ) : enterAnimDuration; + exitAnimDuration = exitAnimDuration == -1 ? ( + overrideExitDuration + ) : exitAnimDuration; + } + enterAnimDuration = 0; + View dialogView = createView(layoutResId); + dialogImpl = new DialogImpl(dialogView); + if (dialogView != null) dialogView.setTag(me); + show(dialogView); + } + + public void hide() { + isHide = true; + if (getDialogView() != null) { + getDialogView().setVisibility(View.GONE); + } + } + + public PopTip setAnimResId(int enterResId, int exitResId) { + this.enterAnimResId = enterResId; + this.exitAnimResId = exitResId; + return this; + } + + public PopTip setEnterAnimResId(int enterResId) { + this.enterAnimResId = enterResId; + return this; + } + + public PopTip setExitAnimResId(int exitResId) { + this.exitAnimResId = exitResId; + return this; + } + + public PopTip setHapticFeedbackEnabled(boolean isHapticFeedbackEnabled) { + this.isHapticFeedbackEnabled = isHapticFeedbackEnabled ? 1 : 0; + return this; + } + + @Override + protected void shutdown() { + dismiss(); + } + + public PopTip setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public PopTip setMargin(int left, int top, int right, int bottom) { + bodyMargin[0] = left; + bodyMargin[1] = top; + bodyMargin[2] = right; + bodyMargin[3] = bottom; + refreshUI(); + return this; + } + + public PopTip setMarginLeft(int left) { + bodyMargin[0] = left; + refreshUI(); + return this; + } + + public PopTip setMarginTop(int top) { + bodyMargin[1] = top; + refreshUI(); + return this; + } + + public PopTip setMarginRight(int right) { + bodyMargin[2] = right; + refreshUI(); + return this; + } + + public PopTip setMarginBottom(int bottom) { + bodyMargin[3] = bottom; + refreshUI(); + return this; + } + + public int getMarginLeft() { + return bodyMargin[0]; + } + + public int getMarginTop() { + return bodyMargin[1]; + } + + public int getMarginRight() { + return bodyMargin[2]; + } + + public int getMarginBottom() { + return bodyMargin[3]; + } + + public PopTip iconSuccess() { + setTintIcon(false); + int resId = R.mipmap.ico_dialogx_success; + if (getStyle().popTipSettings() != null && getStyle().popTipSettings().defaultIconSuccess() != 0) { + resId = getStyle().popTipSettings().defaultIconSuccess(); + } + setIconResId(resId); + return this; + } + + public PopTip iconWarning() { + setTintIcon(false); + int resId = R.mipmap.ico_dialogx_warning; + if (getStyle().popTipSettings() != null && getStyle().popTipSettings().defaultIconWarning() != 0) { + resId = getStyle().popTipSettings().defaultIconWarning(); + } + setIconResId(resId); + return this; + } + + public PopTip iconError() { + setTintIcon(false); + int resId = R.mipmap.ico_dialogx_error; + if (getStyle().popTipSettings() != null && getStyle().popTipSettings().defaultIconError() != 0) { + resId = getStyle().popTipSettings().defaultIconError(); + } + setIconResId(resId); + return this; + } + + public boolean isTintIcon() { + if (tintIcon == null && getStyle().popTipSettings() != null) { + return getStyle().popTipSettings().tintIcon(); + } + return tintIcon == BOOLEAN.TRUE; + } + + public PopTip setTintIcon(boolean tintIcon) { + this.tintIcon = tintIcon ? BOOLEAN.TRUE : BOOLEAN.FALSE; + refreshUI(); + return this; + } + + public PopTip setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public float getRadius() { + return backgroundRadius; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public PopTip setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public PopTip setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public PopTip setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new PopTip() { + * + * @param dialog self + * @Override public void onShow(PopTip dialog) { + * //... + * } + * } + */ + protected void onShow(PopTip dialog) { + + } + + /** + * 用于使用 new 构建实例时,override 的生命周期事件 + * 例如: + * new PopTip() { + * + * @param dialog self + * @Override public boolean onDismiss(PopTip dialog) { + * WaitDialog.show("Please Wait..."); + * if (dialog.getButtonSelectResult() == BUTTON_SELECT_RESULT.BUTTON_OK) { + * //点击了OK的情况 + * //... + * } else { + * //其他按钮点击、对话框dismiss的情况 + * //... + * } + * return false; + * } + * } + */ + //用于使用 new 构建实例时,override 的生命周期事件 + protected void onDismiss(PopTip dialog) { + + } + + @Override + protected void cleanActivityContext() { + super.cleanActivityContext(); + dismiss(getDialogView()); + } + + public PopTip setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public PopTip onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public PopTip onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public PopTip appendMessage(CharSequence message) { + this.message = TextUtils.concat(this.message, message); + refreshUI(); + return this; + } + + public PopTip setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex(" + orderIndex + ") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public PopTip bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public PopTip setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public PopTip cleanAction(int actionId) { + dialogActionRunnableMap.remove(actionId); + return this; + } + + public PopTip cleanAllAction() { + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss() { + dismiss(); + } + + public PopTip bindDismissWithLifecycleOwner(LifecycleOwner owner) { + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/TipDialog.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/TipDialog.java new file mode 100644 index 0000000..e1e5f06 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/TipDialog.java @@ -0,0 +1,313 @@ +package com.kongzue.dialogx.dialogs; + +import android.app.Activity; +import android.os.Build; +import android.text.TextUtils; + +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; + +import java.util.HashMap; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/28 23:53 + */ +public class TipDialog extends WaitDialog { + + /** + * 参数 duration 使用此值,或小于 0 的任意整数时, + * TipDialog 将不自动关闭 + */ + public static final int NO_AUTO_DISMISS = -1; + + protected TipDialog() { + super(); + } + + public static WaitDialog showTipWithDefaultText(TYPE tip) { + return WaitDialog.showTipWithDefaultText(tip); + } + + public static WaitDialog show(int messageResId) { + return show((Activity) null, messageResId); + } + + public static WaitDialog show(Activity activity, int messageResId) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(messageResId, TYPE.WARNING); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(CharSequence message) { + return show(null, message); + } + + public static WaitDialog show(Activity activity, CharSequence message) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(message, TYPE.WARNING); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(int messageResId, TYPE tip) { + return show(null, messageResId, tip); + } + + public static WaitDialog show(Activity activity, int messageResId, TYPE tip) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(messageResId, tip); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(CharSequence message, TYPE tip) { + return show(null, message, tip); + } + + public static WaitDialog show(Activity activity, CharSequence message, TYPE tip) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(message, tip); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(int messageResId, TYPE tip, long duration) { + return show(null, messageResId, tip, duration); + } + + public static WaitDialog show(Activity activity, int messageResId, TYPE tip, long duration) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(messageResId, tip); + dialog.setTipShowDuration(duration); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(CharSequence message, TYPE tip, long duration) { + return show(null, message, tip, duration); + } + + public static WaitDialog show(Activity activity, CharSequence message, TYPE tip, long duration) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(message, tip); + dialog.setTipShowDuration(duration); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + public TipDialog setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + refreshUI(); + return this; + } + + public TipDialog setMaxHeight(int maxHeight) { + this.maxHeight = maxHeight; + refreshUI(); + return this; + } + + public TipDialog setMinHeight(int minHeight) { + this.minHeight = minHeight; + refreshUI(); + return this; + } + + public TipDialog setMinWidth(int minWidth) { + this.minWidth = minWidth; + refreshUI(); + return this; + } + + public TipDialog setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public boolean isBkgInterceptTouch() { + return bkgInterceptTouch; + } + + public TipDialog setBkgInterceptTouch(boolean bkgInterceptTouch) { + this.bkgInterceptTouch = bkgInterceptTouch; + return this; + } + + public OnBackgroundMaskClickListener getOnBackgroundMaskClickListener() { + return onBackgroundMaskClickListener; + } + + public TipDialog setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + return this; + } + + public TipDialog setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public float getRadius() { + return backgroundRadius; + } + + public TipDialog setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public TipDialog setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + refreshUI(); + return this; + } + + public TipDialog setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public TipDialog setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + public TipDialog setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public TipDialog onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public TipDialog onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public TipDialog setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public TipDialog appendMessage(CharSequence message) { + this.message = TextUtils.concat(this.message, message); + refreshUI(); + return this; + } + + public TipDialog setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex(" + orderIndex + ") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public TipDialog bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public TipDialog setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public TipDialog cleanAction(int actionId){ + dialogActionRunnableMap.remove(actionId); + return this; + } + + public TipDialog cleanAllAction(){ + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss(){ + dismiss(); + } + + public TipDialog bindDismissWithLifecycleOwner(LifecycleOwner owner){ + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/dialogs/WaitDialog.java b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/WaitDialog.java new file mode 100644 index 0000000..791a6fe --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/dialogs/WaitDialog.java @@ -0,0 +1,1347 @@ +package com.kongzue.dialogx.dialogs; + +import android.animation.ValueAnimator; +import android.app.Activity; +import android.content.Context; +import android.graphics.Outline; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.DecelerateInterpolator; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.annotation.Nullable; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.BlurViewType; +import com.kongzue.dialogx.interfaces.DialogConvertViewInterface; +import com.kongzue.dialogx.interfaces.DialogLifecycleCallback; +import com.kongzue.dialogx.interfaces.DialogXAnimInterface; +import com.kongzue.dialogx.interfaces.DialogXRunnable; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.OnBackPressedListener; +import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener; +import com.kongzue.dialogx.interfaces.OnBindView; +import com.kongzue.dialogx.interfaces.ProgressViewInterface; +import com.kongzue.dialogx.util.TextInfo; +import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout; +import com.kongzue.dialogx.util.views.MaxRelativeLayout; +import com.kongzue.dialogx.util.views.ProgressView; + +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/27 14:50 + */ +public class WaitDialog extends BaseDialog { + + public static int overrideEnterDuration = -1; + public static int overrideExitDuration = -1; + public static int overrideEnterAnimRes = 0; + public static int overrideExitAnimRes = 0; + public static BOOLEAN overrideCancelable; + protected boolean bkgInterceptTouch = true; + protected OnBindView onBindView; + protected int customEnterAnimResId; + protected int customExitAnimResId; + protected float backgroundRadius = DialogX.defaultWaitAndTipDialogBackgroundRadius; + protected DialogXAnimInterface dialogXAnimImpl; + protected OnBackPressedListener onBackPressedListener; + + public enum TYPE { + /** + * @deprecated NONE 和 PROGRESSING 不建议使用,禁止使用此方法。 + * 此类型等同于直接使用 WaitDialog,因此请勿使用 TipDialog 并指定使用 TYPE.NONE, + * 如有需要,请直接使用: WaitDialog.show(...) + *

+ * 要是用进度,请直接使用 WaitDialog.show(float) + */ + @Deprecated NONE, SUCCESS, WARNING, ERROR, @Deprecated PROGRESSING + } + + protected CharSequence message; + protected long tipShowDuration = 1500; + protected float waitProgress = -1; + protected int showType = -1; //-1:WaitDialog 状态标示符,其余为 TipDialog 状态标示 + protected TextInfo messageTextInfo; + protected Integer maskColor = null; + protected BOOLEAN privateCancelable; + + protected DialogLifecycleCallback dialogLifecycleCallback; + protected OnBackgroundMaskClickListener onBackgroundMaskClickListener; + + protected WaitDialog() { + super(); + ownActivity = new WeakReference<>(getTopActivity()); + cancelable = DialogX.cancelableTipDialog; + } + + protected static WaitDialog instanceBuild() { + return new WaitDialog(); + } + + public static WaitDialog build() { + return new WaitDialog(); + } + + public static WaitDialog showWaitWithDefaultText() { + WaitDialog dialog = getInstance(); + if (dialog != null) { + dialog.setTip(null, TYPE.NONE); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + protected static WaitDialog showTipWithDefaultText(TYPE tip) { + WaitDialog dialog = getInstance(); + if (dialog != null) { + dialog.setTip(null, tip); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(CharSequence message) { + WaitDialog dialog = getInstance(); + if (dialog != null) { + dialog.setTip(message, TYPE.NONE); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(Activity activity, CharSequence message) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(message, TYPE.NONE); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(int messageResId) { + WaitDialog dialog = getInstance(); + if (dialog != null) { + dialog.setTip(messageResId, TYPE.NONE); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(Activity activity, int messageResId) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(messageResId, TYPE.NONE); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(CharSequence message, float progress) { + WaitDialog dialog = getInstance(); + if (dialog != null) { + dialog.setTip(message, TYPE.PROGRESSING); + dialog.setProgress(progress); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(Activity activity, CharSequence message, float progress) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(message, TYPE.PROGRESSING); + dialog.setProgress(progress); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(int messageResId, float progress) { + WaitDialog dialog = getInstance(); + if (dialog != null) { + dialog.setTip(messageResId, TYPE.PROGRESSING); + dialog.setProgress(progress); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(Activity activity, int messageResId, float progress) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(messageResId, TYPE.PROGRESSING); + dialog.setProgress(progress); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(Activity activity, float progress) { + WaitDialog dialog = getInstance(activity); + if (dialog != null) { + dialog.setTip(TYPE.PROGRESSING); + dialog.setProgress(progress); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public static WaitDialog show(float progress) { + WaitDialog dialog = getInstance(); + if (dialog != null) { + dialog.setTip(TYPE.PROGRESSING); + dialog.setProgress(progress); + if (dialog.getDialogImpl() == null) { + dialog.show(); + } else { + dialog.cancelDelayDismissTimer(); + } + return dialog; + } else { + return instanceBuild(); + } + } + + public float getProgress() { + return waitProgress; + } + + public WaitDialog setProgress(float waitProgress) { + this.waitProgress = waitProgress; + refreshUI(); + return this; + } + + private WeakReference dialogView; + + protected View getWaitDialogView() { + if (dialogView == null) return null; + return dialogView.get(); + } + + protected void setWaitDialogView(View v) { + dialogView = new WeakReference<>(v); + setDialogView(v); + } + + public WaitDialog show() { + super.beforeShow(); + runOnMain(new Runnable() { + @Override + public void run() { + int layoutResId = R.layout.layout_dialogx_wait; + if (style.overrideWaitTipRes() != null && style.overrideWaitTipRes().overrideWaitLayout(isLightTheme()) != 0) { + layoutResId = style.overrideWaitTipRes().overrideWaitLayout(isLightTheme()); + } + dialogImpl = new WeakReference<>(new DialogImpl(layoutResId)); + if (getDialogImpl() != null) { + getDialogImpl().lazyCreate(); + if (getWaitDialogView() != null) { + getWaitDialogView().setTag(WaitDialog.this); + show(getWaitDialogView()); + } + } + } + }); + return this; + } + + public WaitDialog show(final Activity activity) { + super.beforeShow(); + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + int layoutResId = R.layout.layout_dialogx_wait; + if (style.overrideWaitTipRes() != null && style.overrideWaitTipRes().overrideWaitLayout(isLightTheme()) != 0) { + layoutResId = style.overrideWaitTipRes().overrideWaitLayout(isLightTheme()); + } + dialogImpl = new WeakReference<>(new DialogImpl(layoutResId)); + if (getDialogImpl() != null) { + getDialogImpl().lazyCreate(); + if (getWaitDialogView() != null) { + getWaitDialogView().setTag(WaitDialog.this); + show(activity, getWaitDialogView()); + } + } + } + }); + return this; + } + + protected WeakReference dialogImpl; + + public class DialogImpl implements DialogConvertViewInterface { + + private List blurViews; + + public DialogXBaseRelativeLayout boxRoot; + public MaxRelativeLayout bkg; + public RelativeLayout boxProgress; + public ProgressViewInterface progressView; + public RelativeLayout boxCustomView; + public TextView txtInfo; + + private int layoutResId; + + public DialogImpl(int layoutResId) { + this.layoutResId = layoutResId; + } + + public void lazyCreate() { + View dialogView = createView(layoutResId); + if (dialogView == null) return; + setWaitDialogView(dialogView); + boxRoot = dialogView.findViewById(R.id.box_root); + bkg = dialogView.findViewById(R.id.bkg); + boxProgress = dialogView.findViewById(R.id.box_progress); + View progressViewCache = (View) style.overrideWaitTipRes().overrideWaitView(getOwnActivity(), isLightTheme()); + if (progressViewCache == null) { + progressViewCache = new ProgressView(getOwnActivity()); + } + progressView = (ProgressViewInterface) progressViewCache; + boxProgress.addView(progressViewCache, new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + boxCustomView = dialogView.findViewById(R.id.box_customView); + txtInfo = dialogView.findViewById(R.id.txt_info); + + blurViews = findAllBlurView(dialogView); + + init(); + setDialogImpl(this); + refreshView(); + } + + public DialogImpl(View convertView) { + if (convertView == null) return; + setDialogView(convertView); + boxRoot = convertView.findViewById(R.id.box_root); + bkg = convertView.findViewById(R.id.bkg); + boxProgress = convertView.findViewById(R.id.box_progress); + View progressViewCache = (View) style.overrideWaitTipRes().overrideWaitView(getOwnActivity(), isLightTheme()); + if (progressViewCache == null) { + progressViewCache = new ProgressView(getOwnActivity()); + } + progressView = (ProgressViewInterface) progressViewCache; + boxProgress.addView(progressViewCache, new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + boxCustomView = convertView.findViewById(R.id.box_customView); + txtInfo = convertView.findViewById(R.id.txt_info); + init(); + setDialogImpl(this); + refreshView(); + } + + public void init() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && getDialogView() != null) { + getDialogView().setTranslationZ(getThisOrderIndex()); + } + + if (messageTextInfo == null) messageTextInfo = DialogX.tipTextInfo; + if (backgroundColor == null) backgroundColor = DialogX.tipBackgroundColor; + + blurViews = findAllBlurView(dialogView.get()); + + Integer blurFrontColor = getColor(isLightTheme() ? R.color.dialogxWaitBkgDark : R.color.dialogxWaitBkgLight); + Float dialogXRadius = (float) dip2px(15); + if (style.overrideWaitTipRes() != null) { + dialogXRadius = getFloatStyleAttr((float) style.overrideWaitTipRes().overrideRadiusPx(), dialogXRadius); + blurFrontColor = getColorNullable(getIntStyleAttr(style.overrideWaitTipRes().overrideBackgroundColorRes(isLightTheme()), isLightTheme() ? R.color.dialogxWaitBkgDark : R.color.dialogxWaitBkgLight), blurFrontColor); + } + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setOverlayColor(backgroundColor == null ? blurFrontColor : backgroundColor); + ((BlurViewType) blurView).setRadiusPx(dialogXRadius); + } + } else { + GradientDrawable gradientDrawable = (GradientDrawable) getResources().getDrawable(R.drawable.rect_dialogx_material_wait_bkg); + gradientDrawable.setColor(blurFrontColor); + gradientDrawable.setCornerRadius(dialogXRadius); + bkg.setBackground(gradientDrawable); + } + boxRoot.setClickable(true); + + boxRoot.setParentDialog(WaitDialog.this); + boxRoot.setOnLifecycleCallBack(new DialogXBaseRelativeLayout.OnLifecycleCallBack() { + @Override + public void onShow() { + isShow = true; + preShow = false; + setLifecycleState(Lifecycle.State.CREATED); + boxRoot.setAlpha(0f); + bkg.post(new Runnable() { + @Override + public void run() { + if (getOwnActivity() == null) return; + + getDialogXAnimImpl().doShowAnim(WaitDialog.this, bkg); + + onDialogShow(); + getDialogLifecycleCallback().onShow(WaitDialog.this); + setLifecycleState(Lifecycle.State.RESUMED); + } + }); + } + + @Override + public void onDismiss() { + cleanInstance(); + } + }); + + if (readyTipType != null && readyTipType != TYPE.NONE) { + progressView.noLoading(); + ((View) progressView).postDelayed(new Runnable() { + @Override + public void run() { + showTip(readyTipType); + } + }, 100); + } + + boxRoot.setOnBackPressedListener(new DialogXBaseRelativeLayout.PrivateBackPressedListener() { + @Override + public boolean onBackPressed() { + if (onBackPressedListener != null) { + if (onBackPressedListener.onBackPressed(WaitDialog.this)) { + dismiss(); + } + } else { + if (isCancelable()) { + dismiss(); + } + } + return true; + } + }); + onDialogInit(); + } + + private float oldProgress = -1; + + public void refreshView() { + if (boxRoot == null || getOwnActivity() == null) { + return; + } + boxRoot.setAutoUnsafePlacePadding(isEnableImmersiveMode()); + boxRoot.setRootPadding(screenPaddings[0], screenPaddings[1], screenPaddings[2], screenPaddings[3]); + + bkg.setMaxWidth(getMaxWidth()); + bkg.setMaxHeight(getMaxHeight()); + bkg.setMinWidth(getMinWidth()); + bkg.setMinHeight(getMinHeight()); + + if (backgroundColor != null) { + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setOverlayColor(backgroundColor); + } + } else { + GradientDrawable gradientDrawable = (GradientDrawable) getResources().getDrawable(R.drawable.rect_dialogx_material_wait_bkg); + gradientDrawable.setColor(getBackgroundColor()); + gradientDrawable.setCornerRadius(getRadius()); + bkg.setBackground(gradientDrawable); + } + } + if (style.overrideWaitTipRes() != null) { + int overrideTextColorRes = getIntStyleAttr(style.overrideWaitTipRes().overrideTextColorRes(isLightTheme()), isLightTheme() ? R.color.white : R.color.black); + txtInfo.setTextColor(getResources().getColor(overrideTextColorRes)); + progressView.setColor(getResources().getColor(overrideTextColorRes)); + } else { + int overrideTextColorRes = isLightTheme() ? R.color.white : R.color.black; + txtInfo.setTextColor(getResources().getColor(overrideTextColorRes)); + progressView.setColor(getResources().getColor(overrideTextColorRes)); + } + if (DialogX.tipProgressColor != null) progressView.setColor(DialogX.tipProgressColor); + + if (waitProgress >= 0 && waitProgress <= 1 && oldProgress != waitProgress) { + progressView.progress(waitProgress); + oldProgress = waitProgress; + } + if (backgroundRadius > -1) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + bkg.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), backgroundRadius); + } + }); + bkg.setClipToOutline(true); + } + if (blurViews != null) { + for (View blurView : blurViews) { + ((BlurViewType) blurView).setRadiusPx(backgroundRadius); + } + } + } + + showText(txtInfo, message == null ? getDefaultTipText(readyTipType) : message); + useTextInfo(txtInfo, messageTextInfo); + + if (maskColor != null) { + boxRoot.setBackgroundColor(maskColor); + } + + if (onBindView != null && onBindView.getCustomView() != null) { + onBindView.bindParent(boxCustomView, WaitDialog.this); + boxCustomView.setVisibility(View.VISIBLE); + boxProgress.setVisibility(View.GONE); + } else { + boxCustomView.setVisibility(View.GONE); + boxProgress.setVisibility(View.VISIBLE); + } + + if (bkgInterceptTouch) { + if (isCancelable()) { + boxRoot.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onBackgroundMaskClickListener == null || !onBackgroundMaskClickListener.onClick(WaitDialog.this, v)) { + doDismiss(v); + } + } + }); + } else { + boxRoot.setOnClickListener(null); + } + } else { + boxRoot.setClickable(false); + } + onDialogRefreshUI(); + } + + private CharSequence getDefaultTipText(TYPE readyTipType) { + switch (readyTipType) { + case WARNING: + return DialogX.defaultTipDialogWarningText; + case SUCCESS: + return DialogX.defaultTipDialogSuccessText; + case ERROR: + return DialogX.defaultTipDialogErrorText; + case NONE: + return DialogX.defaultWaitDialogWaitingText; + default: + return null; + } + } + + public void doDismiss(final View v) { + if (WaitDialog.this.preDismiss(WaitDialog.this)) { + return; + } + if (boxRoot == null) return; + if (getOwnActivity() == null) return; + + if (!dismissAnimFlag && boxRoot != null) { + dismissAnimFlag = true; + boxRoot.post(new Runnable() { + @Override + public void run() { + if (v != null) v.setEnabled(false); + getDialogXAnimImpl().doExitAnim(WaitDialog.this, bkg); + + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (boxRoot != null) { + boxRoot.setVisibility(View.GONE); + } + dismiss(getWaitDialogView()); + } + }, getExitAnimationDuration(null)); + } + }); + } + } + + protected DialogXAnimInterface getDialogXAnimImpl() { + if (dialogXAnimImpl == null) { + dialogXAnimImpl = new DialogXAnimInterface() { + @Override + public void doShowAnim(WaitDialog dialog, ViewGroup dialogBodyView) { + int enterAnimResId = R.anim.anim_dialogx_default_enter; + if (overrideEnterAnimRes != 0) { + enterAnimResId = overrideEnterAnimRes; + } + if (customEnterAnimResId != 0) { + enterAnimResId = customEnterAnimResId; + } + Animation enterAnim = AnimationUtils.loadAnimation(getOwnActivity(), enterAnimResId); + long enterAnimDurationTemp = getEnterAnimationDuration(enterAnim); + enterAnim.setInterpolator(new DecelerateInterpolator()); + enterAnim.setDuration(enterAnimDurationTemp); + bkg.startAnimation(enterAnim); + + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f); + bkgAlpha.setDuration(enterAnimDurationTemp); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + }); + bkgAlpha.start(); + + boxRoot.animate().setDuration(enterAnimDurationTemp).alpha(1f).setInterpolator(new DecelerateInterpolator()).setListener(null); + } + + @Override + public void doExitAnim(WaitDialog dialog, ViewGroup dialogBodyView) { + Context context = getOwnActivity(); + if (context == null) context = boxRoot.getContext(); + if (context == null) return; + + int exitAnimResId = R.anim.anim_dialogx_default_exit; + if (overrideExitAnimRes != 0) { + exitAnimResId = overrideExitAnimRes; + } + if (customExitAnimResId != 0) { + exitAnimResId = customExitAnimResId; + } + Animation exitAnim = AnimationUtils.loadAnimation(context, exitAnimResId); + long exitAnimDurationTemp = getExitAnimationDuration(exitAnim); + exitAnim.setDuration(exitAnimDurationTemp); + exitAnim.setInterpolator(new AccelerateInterpolator()); + bkg.startAnimation(exitAnim); + + boxRoot.animate().alpha(0f).setInterpolator(new AccelerateInterpolator()).setDuration(exitAnimDurationTemp); + + ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f); + bkgAlpha.setDuration(exitAnimDurationTemp); + bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (boxRoot != null) { + boxRoot.setBkgAlpha((Float) animation.getAnimatedValue()); + } + } + }); + bkgAlpha.start(); + } + }; + } + return dialogXAnimImpl; + } + + public long getExitAnimationDuration(@Nullable Animation defaultExitAnim) { + if (defaultExitAnim == null && bkg.getAnimation() != null) { + defaultExitAnim = bkg.getAnimation(); + } + long exitAnimDurationTemp = (defaultExitAnim == null || defaultExitAnim.getDuration() == 0) ? 300 : defaultExitAnim.getDuration(); + if (overrideExitDuration >= 0) { + exitAnimDurationTemp = overrideExitDuration; + } + if (exitAnimDuration != -1) { + exitAnimDurationTemp = exitAnimDuration; + } + return exitAnimDurationTemp; + } + + public long getEnterAnimationDuration(@Nullable Animation defaultEnterAnim) { + if (defaultEnterAnim == null && bkg.getAnimation() != null) { + defaultEnterAnim = bkg.getAnimation(); + } + long enterAnimDurationTemp = (defaultEnterAnim == null || defaultEnterAnim.getDuration() == 0) ? 300 : defaultEnterAnim.getDuration(); + if (overrideEnterDuration >= 0) { + enterAnimDurationTemp = overrideEnterDuration; + } + if (enterAnimDuration >= 0) { + enterAnimDurationTemp = enterAnimDuration; + } + return enterAnimDurationTemp; + } + + public void showTip(final TYPE tip) { + runOnMain(new Runnable() { + @Override + public void run() { + showType = tip.ordinal(); + if (progressView == null) return; + switch (tip) { + case NONE: + progressView.loading(); + return; + case SUCCESS: + progressView.success(); + break; + case WARNING: + progressView.warning(); + break; + case ERROR: + progressView.error(); + break; + } + + if (boxProgress != null && boxProgress.getVisibility() == View.VISIBLE) { + //此事件是在完成衔接动画绘制后执行的逻辑 + progressView.whenShowTick(new Runnable() { + @Override + public void run() { + getDialogLifecycleCallback().onShow(WaitDialog.this); + refreshView(); + if (tipShowDuration > 0) { + ((View) progressView).postDelayed(new Runnable() { + @Override + public void run() { + if (showType > -1) { + doDismiss(null); + } + } + }, tipShowDuration); + } + } + }); + } else { + getDialogLifecycleCallback().onShow(WaitDialog.this); + refreshView(); + if (tipShowDuration > 0) { + runOnMainDelay(new Runnable() { + @Override + public void run() { + if (showType > -1) { + doDismiss(null); + } + } + }, tipShowDuration); + } + } + } + }); + } + } + + public void cleanInstance() { + isShow = false; + getDialogLifecycleCallback().onDismiss(WaitDialog.this); + setLifecycleState(Lifecycle.State.DESTROYED); + if (dialogImpl != null) dialogImpl.clear(); + dialogImpl = null; + if (dialogView != null) dialogView.clear(); + dialogView = null; + dialogLifecycleCallback = null; + if (ownActivity != null) ownActivity.clear(); + System.gc(); + } + + private void setDialogImpl(DialogImpl d) { + if (dialogImpl != null && dialogImpl.get() != d) dialogImpl = new WeakReference<>(d); + } + + @Override + public String dialogKey() { + return getClass().getSimpleName() + "(" + Integer.toHexString(hashCode()) + ")"; + } + + @Override + public boolean isLightTheme() { + if (DialogX.tipTheme == null) { + return super.isLightTheme(); + } else { + return DialogX.tipTheme == DialogX.THEME.LIGHT; + } + } + + public void refreshUI() { + if (getDialogImpl() == null) return; + runOnMain(new Runnable() { + @Override + public void run() { + if (getDialogImpl() != null) getDialogImpl().refreshView(); + } + }); + } + + public void doDismiss() { + isShow = false; + runOnMain(new Runnable() { + @Override + public void run() { + if (getDialogImpl() != null) { + getDialogImpl().doDismiss(null); + } + } + }); + } + + public static void dismiss() { + WaitDialog instance = getInstance(); + if (instance != null) { + instance.doDismiss(); + } + } + + public static void dismiss(Activity activity) { + WaitDialog instance = getInstance(activity); + if (instance != null) instance.doDismiss(); + } + + protected Timer delayDismissTimer; + + protected void cancelDelayDismissTimer() { + if (delayDismissTimer != null) { + delayDismissTimer.cancel(); + } + } + + public static void dismiss(long delayTime) { + WaitDialog instance = getInstance(); + if (instance != null) { + instance.doDismiss(delayTime); + } + } + + public void doDismiss(long delayTime) { + cancelDelayDismissTimer(); + delayDismissTimer = new Timer(); + delayDismissTimer.schedule(new TimerTask() { + @Override + public void run() { + doDismiss(); + } + }, delayTime); + } + + protected static WaitDialog me() { + WaitDialog instance = getInstance(); + return instance == null ? instanceBuild() : instance; + } + + protected TYPE readyTipType; + + protected void showTip(CharSequence message, TYPE type) { + showType = type.ordinal(); + this.message = message; + readyTipType = type; + show(); + } + + protected void showTip(Activity activity, CharSequence message, TYPE type) { + showType = type.ordinal(); + this.message = message; + readyTipType = type; + show(activity); + } + + protected void showTip(int messageResId, TYPE type) { + showType = type.ordinal(); + this.message = getString(messageResId); + readyTipType = type; + show(); + } + + protected void showTip(Activity activity, int messageResId, TYPE type) { + showType = type.ordinal(); + this.message = getString(messageResId); + readyTipType = type; + show(activity); + } + + protected void showTip(TYPE type) { + if (readyTipType == type) { + return; + } + showType = type.ordinal(); + readyTipType = type; + if (getDialogImpl() != null) { + getDialogImpl().showTip(type); + } + } + + protected void setTip(TYPE type) { + showTip(type); + } + + protected void setTip(CharSequence message, TYPE type) { + this.message = message; + showTip(type); + refreshUI(); + } + + protected void setTip(int messageResId, TYPE type) { + this.message = getString(messageResId); + showTip(type); + refreshUI(); + } + + protected void setTipShowDuration(long tipShowDuration) { + this.tipShowDuration = tipShowDuration; + showTip(readyTipType); + } + + public static CharSequence getMessage() { + return me().message; + } + + public static WaitDialog setMessage(CharSequence message) { + WaitDialog instance = getInstance(); + if (instance != null) { + instance.preMessage(message); + instance.refreshUI(); + return instance; + } + return instanceBuild(); + } + + public static WaitDialog setMessage(int messageResId) { + WaitDialog instance = getInstance(); + if (instance != null) { + instance.preMessage(messageResId); + instance.refreshUI(); + return instance; + } + return instanceBuild(); + } + + public boolean isCancelable() { + if (privateCancelable != null) { + return privateCancelable == BOOLEAN.TRUE; + } + if (overrideCancelable != null) { + return overrideCancelable == BOOLEAN.TRUE; + } + return DialogX.cancelableTipDialog; + } + + public WaitDialog setCancelable(boolean cancelable) { + privateCancelable = cancelable ? BOOLEAN.TRUE : BOOLEAN.FALSE; + refreshUI(); + return this; + } + + /** + * 用于从 WaitDialog 到 TipDialog 的消息设置 + * 此方法不会立即执行,而是等到动画衔接完成后由事件设置 + * + * @param message 消息 + * @return me + */ + protected WaitDialog preMessage(CharSequence message) { + this.message = message; + return this; + } + + protected WaitDialog preMessage(int messageResId) { + this.message = getString(messageResId); + return this; + } + + public DialogLifecycleCallback getDialogLifecycleCallback() { + return dialogLifecycleCallback == null ? new DialogLifecycleCallback() { + } : dialogLifecycleCallback; + } + + public WaitDialog setDialogLifecycleCallback(DialogLifecycleCallback dialogLifecycleCallback) { + this.dialogLifecycleCallback = dialogLifecycleCallback; + if (isShow) dialogLifecycleCallback.onShow(me()); + return this; + } + + public DialogImpl getDialogImpl() { + if (dialogImpl == null) return null; + return dialogImpl.get(); + } + + public WaitDialog setCustomView(OnBindView onBindView) { + this.onBindView = onBindView; + refreshUI(); + return this; + } + + public View getCustomView() { + if (onBindView == null) return null; + return onBindView.getCustomView(); + } + + public WaitDialog removeCustomView() { + this.onBindView.clean(); + refreshUI(); + return this; + } + + public OnBackPressedListener getOnBackPressedListener() { + return (OnBackPressedListener) onBackPressedListener; + } + + public WaitDialog setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + refreshUI(); + return this; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + public WaitDialog setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + refreshUI(); + return this; + } + + public WaitDialog setBackgroundColorRes(@ColorRes int backgroundColorResId) { + this.backgroundColor = getColor(backgroundColorResId); + refreshUI(); + return this; + } + + public WaitDialog setMaskColor(@ColorInt int maskColor) { + this.maskColor = maskColor; + refreshUI(); + return this; + } + + public WaitDialog setEnterAnimDuration(long enterAnimDuration) { + this.enterAnimDuration = enterAnimDuration; + return this; + } + + public long getExitAnimDuration() { + return exitAnimDuration; + } + + public WaitDialog setExitAnimDuration(long exitAnimDuration) { + this.exitAnimDuration = exitAnimDuration; + return this; + } + + @Override + public void restartDialog() { + refreshUI(); + } + + /** + * 获取当前 WaitDialog 显示状态 + *

+ * 值的含义: + * -1: WaitDialog 等待状态 + * 0: TipDialog 无状态(与 WaitDialog 等待状态相同) + * 1: TipDialog 完成状态 + * 2: TipDialog 警告状态 + * 3: TipDialog 错误状态 + * + * @return showType + */ + public static int getType() { + return me().showType; + } + + public WaitDialog setAnimResId(int enterResId, int exitResId) { + customEnterAnimResId = enterResId; + customExitAnimResId = exitResId; + return this; + } + + public WaitDialog setEnterAnimResId(int enterResId) { + customEnterAnimResId = enterResId; + return this; + } + + public WaitDialog setExitAnimResId(int exitResId) { + customExitAnimResId = exitResId; + return this; + } + + public static WaitDialog getInstance() { + return getInstance(null); + } + + public static WaitDialog getInstance(Activity activity) { + if (activity == null) { + activity = getTopActivity(); + } + if (isActivityDestroyed(activity)) { + return null; + } + for (BaseDialog baseDialog : getRunningDialogList()) { + if (baseDialog instanceof WaitDialog) { + if (baseDialog.isShow() && baseDialog.getOwnActivity() == activity) { + return (WaitDialog) baseDialog; + } + } + } + return new WaitDialog(); + } + + @Override + protected void shutdown() { + dismiss(); + } + + public WaitDialog setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + refreshUI(); + return this; + } + + public WaitDialog setMaxHeight(int maxHeight) { + this.maxHeight = maxHeight; + refreshUI(); + return this; + } + + public WaitDialog setMinHeight(int minHeight) { + this.minHeight = minHeight; + refreshUI(); + return this; + } + + public WaitDialog setMinWidth(int minWidth) { + this.minWidth = minWidth; + refreshUI(); + return this; + } + + public WaitDialog setDialogImplMode(DialogX.IMPL_MODE dialogImplMode) { + this.dialogImplMode = dialogImplMode; + return this; + } + + public boolean isBkgInterceptTouch() { + return bkgInterceptTouch; + } + + public WaitDialog setBkgInterceptTouch(boolean bkgInterceptTouch) { + this.bkgInterceptTouch = bkgInterceptTouch; + return this; + } + + public OnBackgroundMaskClickListener getOnBackgroundMaskClickListener() { + return onBackgroundMaskClickListener; + } + + public WaitDialog setOnBackgroundMaskClickListener(OnBackgroundMaskClickListener onBackgroundMaskClickListener) { + this.onBackgroundMaskClickListener = onBackgroundMaskClickListener; + return this; + } + + public WaitDialog setStyle(DialogXStyle style) { + this.style = style; + return this; + } + + public WaitDialog setTheme(DialogX.THEME theme) { + this.theme = theme; + return this; + } + + public TextInfo getMessageTextInfo() { + return messageTextInfo; + } + + public WaitDialog setMessageTextInfo(TextInfo messageTextInfo) { + this.messageTextInfo = messageTextInfo; + refreshUI(); + return this; + } + + public WaitDialog setMessageContent(CharSequence message) { + this.message = message; + refreshUI(); + return this; + } + + public WaitDialog setMessageContent(int messageResId) { + this.message = getString(messageResId); + refreshUI(); + return this; + } + + public CharSequence getMessageContent() { + return message; + } + + public WaitDialog setTipType(TYPE type) { + showTip(type); + return this; + } + + public WaitDialog setRadius(float radiusPx) { + backgroundRadius = radiusPx; + refreshUI(); + return this; + } + + public float getRadius() { + return backgroundRadius < 0 ? dip2px(15) : backgroundRadius; + } + + public DialogXAnimInterface getDialogXAnimImpl() { + return dialogXAnimImpl; + } + + public WaitDialog setDialogXAnimImpl(DialogXAnimInterface dialogXAnimImpl) { + this.dialogXAnimImpl = dialogXAnimImpl; + return this; + } + + public WaitDialog setRootPadding(int padding) { + this.screenPaddings = new int[]{padding, padding, padding, padding}; + refreshUI(); + return this; + } + + public WaitDialog setRootPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { + this.screenPaddings = new int[]{paddingLeft, paddingTop, paddingRight, paddingBottom}; + refreshUI(); + return this; + } + + public WaitDialog onShow(DialogXRunnable dialogXRunnable) { + onShowRunnable = dialogXRunnable; + if (isShow() && onShowRunnable != null) { + onShowRunnable.run(this); + } + return this; + } + + public WaitDialog onDismiss(DialogXRunnable dialogXRunnable) { + onDismissRunnable = dialogXRunnable; + return this; + } + + public WaitDialog setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public WaitDialog setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + public WaitDialog appendMessage(CharSequence message) { + this.message = TextUtils.concat(this.message, message); + refreshUI(); + return this; + } + + public WaitDialog setThisOrderIndex(int orderIndex) { + this.thisOrderIndex = orderIndex; + if (getDialogView() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getDialogView().setTranslationZ(orderIndex); + } else { + error("DialogX: " + dialogKey() + " 执行 .setThisOrderIndex(" + orderIndex + ") 失败:系统不支持此方法,SDK-API 版本必须大于 21(LOLLIPOP)"); + } + } + return this; + } + + public WaitDialog bringToFront() { + setThisOrderIndex(getHighestOrderIndex()); + return this; + } + + public WaitDialog setActionRunnable(int actionId, DialogXRunnable runnable) { + dialogActionRunnableMap.put(actionId, runnable); + return this; + } + + public WaitDialog cleanAction(int actionId){ + dialogActionRunnableMap.remove(actionId); + return this; + } + + public WaitDialog cleanAllAction(){ + dialogActionRunnableMap.clear(); + return this; + } + + // for BaseDialog use + public void callDialogDismiss(){ + dismiss(); + } + + public WaitDialog bindDismissWithLifecycleOwner(LifecycleOwner owner){ + super.bindDismissWithLifecycleOwnerPrivate(owner); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/impl/ActivityLifecycleImpl.java b/DialogX/src/main/java/com/kongzue/dialogx/impl/ActivityLifecycleImpl.java new file mode 100644 index 0000000..17a08fd --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/impl/ActivityLifecycleImpl.java @@ -0,0 +1,229 @@ +package com.kongzue.dialogx.impl; + +import android.app.Activity; +import android.app.Application; +import android.content.Context; +import android.os.Build; +import android.os.Bundle; +import android.util.ArrayMap; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.util.DialogXFloatingWindowActivity; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import static com.kongzue.dialogx.DialogX.error; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/22 11:31 + */ +public class ActivityLifecycleImpl implements Application.ActivityLifecycleCallbacks { + + private onActivityResumeCallBack onActivityResumeCallBack; + private static ActivityLifecycleImpl activityLifecycle; + private static Application application; + + public ActivityLifecycleImpl(ActivityLifecycleImpl.onActivityResumeCallBack onActivityResumeCallBack) { + this.onActivityResumeCallBack = onActivityResumeCallBack; + } + + public static void init(Context context, ActivityLifecycleImpl.onActivityResumeCallBack onActivityResumeCallBack) { + if (context != null) { + Application application = getApplicationContext(context); + if (application == null) { + error("DialogX 未初始化(E1)。\n请检查是否在启动对话框前进行初始化操作,使用以下代码进行初始化:\nDialogX.init(context);\n\n另外建议您前往查看 DialogX 的文档进行使用:https://github.com/kongzue/DialogX"); + return; + } + + ActivityLifecycleImpl.application = application; + if (activityLifecycle != null) { + application.unregisterActivityLifecycleCallbacks(activityLifecycle); + } + application.registerActivityLifecycleCallbacks(activityLifecycle = new ActivityLifecycleImpl(onActivityResumeCallBack)); + } else { + if (ActivityLifecycleImpl.application != null) { + init(ActivityLifecycleImpl.application, onActivityResumeCallBack); + } + } + } + + public static Application getApplicationContext(Context context) { + if (context != null) { + return (Application) context.getApplicationContext(); + } + try { + Application application = (Application) Class.forName("android.app.ActivityThread").getMethod("currentApplication").invoke(null, (Object[]) null); + return application; + } catch (Exception e) { + } + try { + Class activityThreadClass = Class.forName("android.app.ActivityThread"); + Object activityThread = activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null); + Method getApplicationMethod = activityThreadClass.getDeclaredMethod("getApplication"); + Application application = (Application) getApplicationMethod.invoke(activityThread); + return application; + } catch (Exception e) { + } + try { + Application application = (Application) Class.forName("android.app.AppGlobals").getMethod("getInitialApplication").invoke(null, (Object[]) null); + return application; + } catch (Exception e) { + } + error("DialogX.init: 初始化异常,请确保init方法内传入的Context是有效的。"); + return null; + } + + public static Application getApplicationContext() { + if (application != null) { + return application; + } + try { + Application application = (Application) Class.forName("android.app.ActivityThread").getMethod("currentApplication").invoke(null, (Object[]) null); + return application; + } catch (Exception e) { + } + try { + Class activityThreadClass = Class.forName("android.app.ActivityThread"); + Object activityThread = activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null); + Method getApplicationMethod = activityThreadClass.getDeclaredMethod("getApplication"); + Application application = (Application) getApplicationMethod.invoke(activityThread); + return application; + } catch (Exception e) { + } + try { + Application application = (Application) Class.forName("android.app.AppGlobals").getMethod("getInitialApplication").invoke(null, (Object[]) null); + return application; + } catch (Exception e) { + } + return null; + } + + public static Activity getTopActivity() { + try { + Class activityThreadClass = Class.forName("android.app.ActivityThread"); + Object activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null); + Field activitiesField = activityThreadClass.getDeclaredField("mActivities"); + activitiesField.setAccessible(true); + Map activities; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + activities = (HashMap) activitiesField.get(activityThread); + } else { + activities = (ArrayMap) activitiesField.get(activityThread); + } + if (activities.size() < 1) { + return null; + } + for (Object activityRecord : activities.values()) { + Class activityRecordClass = activityRecord.getClass(); + Field pausedField = activityRecordClass.getDeclaredField("paused"); + pausedField.setAccessible(true); + if (!pausedField.getBoolean(activityRecord)) { + Field activityField = activityRecordClass.getDeclaredField("activity"); + activityField.setAccessible(true); + Activity activity = (Activity) activityField.get(activityRecord); + return activity; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + + public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) { + if (onActivityResumeCallBack != null) { + if (activity instanceof DialogXFloatingWindowActivity) { + return; + } + onActivityResumeCallBack.getActivity(activity); + } + } + + + public void onActivityStarted(@NonNull Activity activity) { + if (application == null) { + BaseDialog.init(activity); + } + } + + + public void onActivityPreResumed(@NonNull Activity activity) { +// Application.ActivityLifecycleCallbacks.super.onActivityPreResumed(activity); // # Android 4.4 bug,此代码将导致在 Android 4.4 上闪退,错误原因:java.lang.VerifyError + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + callOnResume(activity); + } + } + + + public void onActivityResumed(@NonNull Activity activity) { + if (activity.isDestroyed() || activity.isFinishing() || activity instanceof DialogXFloatingWindowActivity) { + return; + } + if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + callOnResume(activity); + } + BaseDialog.onActivityResume(activity); + } + + private void callOnResume(Activity activity) { + if (activity.isDestroyed() || activity.isFinishing() || activity instanceof DialogXFloatingWindowActivity) { + return; + } + if (onActivityResumeCallBack != null) { + onActivityResumeCallBack.getActivity(activity); + } + } + + + public void onActivityPaused(@NonNull Activity activity) { + } + + + public void onActivityStopped(@NonNull Activity activity) { + } + + + public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) { + + } + + public void onActivityDestroyed(@NonNull Activity activity) { + if (BaseDialog.getTopActivity() == activity) { + BaseDialog.cleanContext(); + } + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.Q) { + BaseDialog.recycleDialog(activity); + } + } + + public void onActivityPreDestroyed(@NonNull final Activity activity) { + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + BaseDialog.recycleDialog(activity); + } + } + + public interface onActivityResumeCallBack { + void getActivity(Activity activity); + } + + public static boolean isExemptActivities(Activity activity) { + if (activity == null) return true; + for (String packageName : DialogX.unsupportedActivitiesPackageNames) { + if (activity.getClass().getName().contains(packageName)) { + return true; + } + } + return false; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/impl/AnimatorListenerEndCallBack.java b/DialogX/src/main/java/com/kongzue/dialogx/impl/AnimatorListenerEndCallBack.java new file mode 100644 index 0000000..c0dc335 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/impl/AnimatorListenerEndCallBack.java @@ -0,0 +1,30 @@ +package com.kongzue.dialogx.impl; + +import android.animation.Animator; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/22 14:37 + */ +public abstract class AnimatorListenerEndCallBack implements Animator.AnimatorListener { + @Override + public void onAnimationStart(Animator animation) { + + } + + @Override + public abstract void onAnimationEnd(Animator animation); + + @Override + public void onAnimationCancel(Animator animation) { + + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/impl/DialogFragmentImpl.java b/DialogX/src/main/java/com/kongzue/dialogx/impl/DialogFragmentImpl.java new file mode 100644 index 0000000..3f22d90 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/impl/DialogFragmentImpl.java @@ -0,0 +1,137 @@ +package com.kongzue.dialogx.impl; + +import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; +import static com.kongzue.dialogx.DialogX.error; + +import android.app.Activity; +import android.graphics.Color; +import android.graphics.PixelFormat; +import android.graphics.drawable.ColorDrawable; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; + +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.NoTouchInterface; + +import java.lang.ref.WeakReference; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/7/28 15:45 + */ +public class DialogFragmentImpl extends DialogFragment { + + public DialogFragmentImpl() { + } + + private View dialogView; + private BaseDialog baseDialog; + + public DialogFragmentImpl(BaseDialog baseDialog, View dialogView) { + this.dialogView = dialogView; + this.baseDialog = baseDialog; + activityWeakReference = new WeakReference<>(baseDialog.getOwnActivity()); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return dialogView; + } + + WeakReference activityWeakReference = null; + + @Override + public void onStart() { + super.onStart(); + final Activity activity = activityWeakReference.get(); + + if (getDialog() == null) return; + Window dialogWindow = getDialog().getWindow(); + if (dialogWindow == null) return; + dialogWindow.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); + + dialogWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + + WindowManager.LayoutParams lp = dialogWindow.getAttributes(); + lp.width = WindowManager.LayoutParams.MATCH_PARENT; + lp.height = WindowManager.LayoutParams.MATCH_PARENT; + lp.dimAmount = 0f; + lp.format = PixelFormat.TRANSPARENT; + dialogView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + for (BaseDialog dialog : BaseDialog.getRunningDialogList()) { + if (dialog.getOwnActivity() == activity && dialog != baseDialog && dialog.getDialogImplMode() == baseDialog.getDialogImplMode()) { + if (!(dialog instanceof NoTouchInterface) && dialog.getDialogView() != null) { + Log.e(">>>", "onTouch: dialog=" + dialog + " baseDialog="+baseDialog ); + dialog.getDialogView().dispatchTouchEvent(event); + return true; + } + } + } + if (baseDialog instanceof NoTouchInterface) { + return activity.dispatchTouchEvent(event); + } else { + return false; + } + } + }); + dialogWindow.setAttributes(lp); + dialogWindow.addFlags(FLAG_TRANSLUCENT_STATUS); + dialogWindow.getDecorView().setPadding(0, 0, 0, 0); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + } + dialogWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + int visibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; + if (activity != null) { + if ((activity.getWindow().getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) == View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) { + visibility = visibility | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + } + } + dialogWindow.getDecorView().setSystemUiVisibility(visibility); + dialogWindow.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); + dialogWindow.setStatusBarColor(Color.TRANSPARENT); + dialogWindow.setNavigationBarColor(Color.TRANSPARENT); + } else { + dialogWindow.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + } + } + + @Override + public void show(FragmentManager manager, @Nullable String tag) { + if (manager == null) { + error("DialogX.DialogFragment 模式无法支持非 AppCompatActivity 启动。"); + return; + } + //super.show(manager, tag); + + FragmentTransaction ft = manager.beginTransaction(); + ft.add(this, tag); + ft.commitAllowingStateLoss(); + } + + @Override + public void dismiss() { + dismissAllowingStateLoss(); + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BaseDialog.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BaseDialog.java new file mode 100644 index 0000000..f22a8f5 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BaseDialog.java @@ -0,0 +1,1173 @@ +package com.kongzue.dialogx.interfaces; + +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static com.kongzue.dialogx.DialogX.DEBUGMODE; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.res.ColorStateList; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Color; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.text.TextUtils; +import android.util.Log; +import android.view.HapticFeedbackConstants; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowInsets; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.FrameLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.FragmentManager; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleEventObserver; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LifecycleRegistry; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.dialogs.WaitDialog; +import com.kongzue.dialogx.impl.ActivityLifecycleImpl; +import com.kongzue.dialogx.impl.DialogFragmentImpl; +import com.kongzue.dialogx.util.ActivityRunnable; +import com.kongzue.dialogx.util.DialogListBuilder; +import com.kongzue.dialogx.util.DialogXFloatingWindowActivity; +import com.kongzue.dialogx.util.TextInfo; +import com.kongzue.dialogx.util.WindowUtil; +import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/22 14:10 + */ +public abstract class BaseDialog implements LifecycleOwner { + + private static Thread uiThread; + private static WeakReference activityWeakReference; + protected WeakReference ownActivity; + private WeakReference rootFrameLayout; + private static List runningDialogList; + protected int isHapticFeedbackEnabled = -1; + private WeakReference dialogView; + protected WeakReference ownDialogFragmentImpl; + protected DialogX.IMPL_MODE dialogImplMode = DialogX.implIMPLMode; + protected WeakReference floatingWindowActivity; + @Nullable + private DialogListBuilder dialogListBuilder; + protected LifecycleRegistry lifecycle = new LifecycleRegistry(this); + protected Map data; + protected DialogXRunnable onShowRunnable; + protected DialogXRunnable onDismissRunnable; + protected boolean enableImmersiveMode = true; // 沉浸式适配 + protected int thisOrderIndex = 0; + protected Map dialogActionRunnableMap = new HashMap(); + + public enum BUTTON_SELECT_RESULT { + NONE, // 未做出选择 + BUTTON_OK, // 选择了确定按钮 + BUTTON_CANCEL, // 选择了取消按钮 + BUTTON_OTHER // 选择了其他按钮 + } + + public static void init(Context context) { + if (context == null) { + context = ActivityLifecycleImpl.getTopActivity(); + } + if (context instanceof Activity) { + initActivityContext((Activity) context); + } + ActivityLifecycleImpl.init(context, new ActivityLifecycleImpl.onActivityResumeCallBack() { + @Override + public void getActivity(Activity activity) { + initActivityContext(activity); + } + }); + } + + private static void initActivityContext(Activity activity) { + if (ActivityLifecycleImpl.isExemptActivities(activity)) { + return; + } + try { + uiThread = Looper.getMainLooper().getThread(); + activityWeakReference = new WeakReference<>(activity); + } catch (Exception e) { + e.printStackTrace(); + error("DialogX.init: 初始化异常,找不到Activity的根布局"); + } + } + + protected static void log(Object o) { + if (DEBUGMODE) { + Log.i(">>>", o.toString()); + } + } + + protected static void error(Object o) { + if (DEBUGMODE) { + Log.e(">>>", o.toString()); + } + } + + public static void onActivityResume(Activity activity) { + if (runningDialogList != null) { + CopyOnWriteArrayList copyOnWriteList = new CopyOnWriteArrayList<>(runningDialogList); + for (int i = copyOnWriteList.size() - 1; i >= 0; i--) { + BaseDialog baseDialog = copyOnWriteList.get(i); + if (baseDialog.getOwnActivity() == activity && baseDialog.isShow && baseDialog.getDialogView() != null) { + View boxRoot = baseDialog.getDialogView().findViewById(R.id.box_root); + if (boxRoot instanceof DialogXBaseRelativeLayout) { + if (((DialogXBaseRelativeLayout) boxRoot).isBaseFocusable()) { + ((DialogXBaseRelativeLayout) boxRoot).requestFocusOnResume(); + return; + } + } + } + } + } + } + + private static void requestDialogFocus() { + if (runningDialogList != null) { + CopyOnWriteArrayList copyOnWriteList = new CopyOnWriteArrayList<>(runningDialogList); + for (int i = copyOnWriteList.size() - 1; i >= 0; i--) { + BaseDialog baseDialog = copyOnWriteList.get(i); + if (baseDialog.getOwnActivity() == getTopActivity() && baseDialog.isShow && baseDialog.getDialogView() != null) { + View boxRoot = baseDialog.getDialogView().findViewById(R.id.box_root); + if (boxRoot instanceof DialogXBaseRelativeLayout) { + if (((DialogXBaseRelativeLayout) boxRoot).isBaseFocusable()) { + boxRoot.requestFocus(); + return; + } + } + } + } + } + } + + public abstract void restartDialog(); + + protected static void show(final View view) { + if (view == null) { + return; + } + final BaseDialog dialog = (BaseDialog) view.getTag(); + if (dialog != null) { + if (dialog.isShow) { + if (dialog.getDialogView() != null) { + dialog.getDialogView().setVisibility(View.VISIBLE); + return; + } + error(((BaseDialog) view.getTag()).dialogKey() + "已处于显示状态,请勿重复执行 show() 指令。"); + return; + } + if (dialog.preShow(dialog)) { + return; + } + dialog.dialogView = new WeakReference<>(view); + + log(dialog.dialogKey() + ".show on " + (dialog.isActivityImplMode() ? dialog.getOwnActivity() : "window")); + + addDialogToRunningList(dialog); + switch (dialog.dialogImplMode) { + case WINDOW: + WindowUtil.show(dialog.getOwnActivity(), view, !(dialog instanceof NoTouchInterface)); + break; + case DIALOG_FRAGMENT: + DialogFragmentImpl dialogFragment = new DialogFragmentImpl(dialog, view); + dialogFragment.show(getSupportFragmentManager(dialog.getOwnActivity()), "DialogX"); + dialog.ownDialogFragmentImpl = new WeakReference<>(dialogFragment); + break; + case FLOATING_ACTIVITY: + if (waitRunDialogX == null) { + waitRunDialogX = new HashMap<>(); + } + waitRunDialogX.put(dialog.dialogKey(), new ActivityRunnable() { + @Override + public void run(Activity activity) { + dialog.floatingWindowActivity = new WeakReference<>((DialogXFloatingWindowActivity) activity); + dialog.floatingWindowActivity.get().setFromActivity(dialog.getOwnActivity()); + final FrameLayout activityRootView = getDecorView(activity); + if (activityRootView == null) { + return; + } + runOnMain(new Runnable() { + @Override + public void run() { + if (view.getParent() == dialog.getRootFrameLayout()) { + error(((BaseDialog) view.getTag()).dialogKey() + "已处于显示状态,请勿重复执行 show() 指令。"); + return; + } + if (view.getParent() != null) { + ((ViewGroup) view.getParent()).removeView(view); + } + activityRootView.addView(view); + } + }); + } + }); + DialogXFloatingWindowActivity dialogXFloatingWindowActivity = DialogXFloatingWindowActivity.getDialogXFloatingWindowActivity(); + if (dialogXFloatingWindowActivity != null && dialogXFloatingWindowActivity.isSameFrom(dialog.getOwnActivity().hashCode())) { + dialogXFloatingWindowActivity.showDialogX(dialog.dialogKey()); + return; + } + Intent intent = new Intent(getPrivateContext(), DialogXFloatingWindowActivity.class); + if (dialog.getOwnActivity() == null) { + intent.addFlags(FLAG_ACTIVITY_NEW_TASK); + } + intent.putExtra("dialogXKey", dialog.dialogKey()); + intent.putExtra("fromActivityUiStatus", dialog.getOwnActivity() == null ? 0 : (getDecorView(dialog.getOwnActivity()) == null ? 0 : getDecorView(dialog.getOwnActivity()).getSystemUiVisibility())); + intent.putExtra("from", getPrivateContext().hashCode()); + getPrivateContext().startActivity(intent); + int version = Integer.valueOf(Build.VERSION.SDK_INT); + if (version > 5 && dialog.getOwnActivity() != null) { + dialog.getOwnActivity().overridePendingTransition(0, 0); + } + break; + default: + if (dialog.getRootFrameLayout() == null) { + return; + } + runOnMain(new Runnable() { + @Override + public void run() { + if (view.getParent() == dialog.getRootFrameLayout()) { + error(((BaseDialog) view.getTag()).dialogKey() + "已处于显示状态,请勿重复执行 show() 指令。"); + return; + } + if (view.getParent() != null) { + ((ViewGroup) view.getParent()).removeView(view); + } + dialog.getRootFrameLayout().addView(view); + } + }); + break; + } + } + } + + private static FragmentManager getSupportFragmentManager(Activity activity) { + return (activity instanceof AppCompatActivity) ? ((AppCompatActivity) activity).getSupportFragmentManager() : null; + } + + private static Map waitRunDialogX; + + public static ActivityRunnable getActivityRunnable(String dialogXKey) { + if (dialogXKey == null) { + return null; + } + return waitRunDialogX.get(dialogXKey); + } + + protected static void show(final Activity activity, final View view) { + if (activity == null || view == null) { + return; + } + if (activityWeakReference == null || activityWeakReference.get() == null || ActivityLifecycleImpl.getApplicationContext() == null) { + init(activity.getApplicationContext()); + } + final BaseDialog baseDialog = (BaseDialog) view.getTag(); + if (baseDialog != null) { + baseDialog.setOwnActivity(activity); + if (baseDialog.getDialogView() != null) { + baseDialog.getDialogView().setVisibility(View.VISIBLE); + } + if (baseDialog.isShow) { + error(((BaseDialog) view.getTag()).dialogKey() + "已处于显示状态,请勿重复执行 show() 指令。"); + return; + } + if (activity.isDestroyed()) { + error(((BaseDialog) view.getTag()).dialogKey() + ".show ERROR: activity is Destroyed."); + return; + } + if (baseDialog.preShow(baseDialog)) { + return; + } + baseDialog.dialogView = new WeakReference<>(view); + + log(baseDialog + ".show on " + activity); + addDialogToRunningList(baseDialog); + + switch (baseDialog.dialogImplMode) { + case WINDOW: + WindowUtil.show(activity, view, !(baseDialog instanceof NoTouchInterface)); + break; + case DIALOG_FRAGMENT: + DialogFragmentImpl dialogFragment = new DialogFragmentImpl(baseDialog, view); + dialogFragment.show(getSupportFragmentManager(activity), "DialogX"); + baseDialog.ownDialogFragmentImpl = new WeakReference<>(dialogFragment); + break; + case FLOATING_ACTIVITY: + if (waitRunDialogX == null) { + waitRunDialogX = new HashMap<>(); + } + waitRunDialogX.put(baseDialog.dialogKey(), new ActivityRunnable() { + @Override + public void run(Activity activity) { + baseDialog.floatingWindowActivity = new WeakReference<>((DialogXFloatingWindowActivity) activity); + baseDialog.floatingWindowActivity.get().setFromActivity(baseDialog.getOwnActivity()); + final FrameLayout activityRootView = getDecorView(activity); + if (activityRootView == null) { + return; + } + runOnMain(new Runnable() { + @Override + public void run() { + if (view.getParent() == baseDialog.getRootFrameLayout()) { + error(((BaseDialog) view.getTag()).dialogKey() + "已处于显示状态,请勿重复执行 show() 指令。"); + return; + } + if (view.getParent() != null) { + ((ViewGroup) view.getParent()).removeView(view); + } + activityRootView.addView(view); + } + }); + } + }); + DialogXFloatingWindowActivity dialogXFloatingWindowActivity = DialogXFloatingWindowActivity.getDialogXFloatingWindowActivity(); + if (dialogXFloatingWindowActivity != null && dialogXFloatingWindowActivity.isSameFrom(activity.hashCode())) { + dialogXFloatingWindowActivity.showDialogX(baseDialog.dialogKey()); + return; + } + Intent intent = new Intent(activity, DialogXFloatingWindowActivity.class); + intent.putExtra("dialogXKey", baseDialog.dialogKey()); + intent.putExtra("from", activity.hashCode()); + intent.putExtra("fromActivityUiStatus", getDecorView(activity) == null ? 0 : getDecorView(activity).getSystemUiVisibility()); + activity.startActivity(intent); + int version = Integer.valueOf(Build.VERSION.SDK_INT); + if (version > 5) { + activity.overridePendingTransition(0, 0); + } + break; + default: + final FrameLayout activityRootView = getDecorView(activity); + if (activityRootView == null) { + return; + } + runOnMain(new Runnable() { + @Override + public void run() { + if (view.getParent() == baseDialog.getRootFrameLayout()) { + error(((BaseDialog) view.getTag()).dialogKey() + "已处于显示状态,请勿重复执行 show() 指令。"); + return; + } + if (view.getParent() != null) { + ((ViewGroup) view.getParent()).removeView(view); + } + activityRootView.addView(view); + } + }); + break; + } + } + } + + private void setOwnActivity(Activity activity) { + ownActivity = new WeakReference<>(activity); + } + + protected static void dismiss(final View dialogView) { + if (dialogView == null) { + return; + } + final BaseDialog baseDialog = (BaseDialog) dialogView.getTag(); + log(baseDialog.dialogKey() + ".dismiss"); + removeDialogToRunningList(baseDialog); + if (baseDialog.dialogView != null) { + baseDialog.dialogView.clear(); + } + baseDialog.onDialogDismiss(); + switch (baseDialog.dialogImplMode) { + case WINDOW: + WindowUtil.dismiss(dialogView); + break; + case DIALOG_FRAGMENT: + if (baseDialog.ownDialogFragmentImpl != null && baseDialog.ownDialogFragmentImpl.get() != null) { + baseDialog.ownDialogFragmentImpl.get().dismiss(); + } + break; + case FLOATING_ACTIVITY: + if (baseDialog.floatingWindowActivity != null && baseDialog.floatingWindowActivity.get() != null) { + FrameLayout rootView = getDecorView(baseDialog.floatingWindowActivity.get()); + if (rootView != null) { + rootView.removeView(dialogView); + } + baseDialog.floatingWindowActivity.get().finish(baseDialog.dialogKey()); + requestDialogFocus(); + } + break; + default: + runOnMain(new Runnable() { + @Override + public void run() { + if (dialogView.getParent() == null || !(dialogView.getParent() instanceof ViewGroup)) { + if (baseDialog.getRootFrameLayout() == null) { + return; + } + baseDialog.getRootFrameLayout().removeView(dialogView); + } else { + ((ViewGroup) dialogView.getParent()).removeView(dialogView); + } + requestDialogFocus(); + } + }, true); + break; + } + if (baseDialog.getDialogListBuilder() != null) { + if (baseDialog.getDialogListBuilder().isEmpty()) { + baseDialog.cleanDialogList(); + } else { + baseDialog.getDialogListBuilder().showNext(); + } + } + } + + private static void addDialogToRunningList(BaseDialog baseDialog) { + if (runningDialogList == null) { + runningDialogList = new CopyOnWriteArrayList<>(); + } + runningDialogList.add(baseDialog); + } + + private static void removeDialogToRunningList(BaseDialog baseDialog) { + if (runningDialogList != null) { + runningDialogList.remove(baseDialog); + } + } + + public static Activity getTopActivity() { + if (activityWeakReference == null || activityWeakReference.get() == null) { + // 尝试反射初始化 + init(null); + if (activityWeakReference == null || activityWeakReference.get() == null) { + // 若还为空,无奈,尝试直接反射拿顶层 activity + Activity topActivity = ActivityLifecycleImpl.getTopActivity(); + init(topActivity); + return topActivity; + } + return activityWeakReference.get(); + } + return activityWeakReference.get(); + } + + /** + * @return 获取上下文 + * @Deprecated 已废弃,将在未来版本删除此方法,建议使用 {@link #getOwnActivity()} 替代此方法 + */ + @Deprecated + public static Context getContext() { + return getPrivateContext(); + } + + private static Context getPrivateContext() { + Activity activity = getTopActivity(); + if (activity == null) { + Context applicationContext = getApplicationContext(); + if (applicationContext == null) { + error("DialogX 未初始化(E2)。\n请检查是否在启动对话框前进行初始化操作,使用以下代码进行初始化:\nDialogX.init(context);\n\n另外建议您前往查看 DialogX 的文档进行使用:https://github.com/kongzue/DialogX"); + return null; + } + return applicationContext; + } + return activity; + } + + public static Context getApplicationContext() { + return ActivityLifecycleImpl.getApplicationContext(); + } + + /** + * 自动执行,不建议自行调用此方法 + * + * @hide + */ + public static void cleanContext() { + if (activityWeakReference != null) { + activityWeakReference.clear(); + } + activityWeakReference = null; + System.gc(); + } + + protected abstract void shutdown(); + + protected boolean cancelable = true; + protected boolean isShow; + protected DialogXStyle style; + protected DialogX.THEME theme; + protected boolean autoShowInputKeyboard; + protected Integer backgroundColor = null; + protected long enterAnimDuration = -1; + protected long exitAnimDuration = -1; + protected int maxWidth; + protected int maxHeight; + protected int minWidth; + protected int minHeight; + protected int[] screenPaddings = new int[4]; + + public BaseDialog() { + cancelable = DialogX.cancelable; + style = DialogX.globalStyle; + theme = DialogX.globalTheme; + enterAnimDuration = DialogX.enterAnimDuration; + exitAnimDuration = DialogX.exitAnimDuration; + autoShowInputKeyboard = DialogX.autoShowInputKeyboard; + enableImmersiveMode = DialogX.enableImmersiveMode; + } + + public abstract boolean isCancelable(); + + public View createView(int layoutId) { + if (isActivityImplMode()) { + if (getOwnActivity() == null) { + error("DialogX 未初始化(E3)。\n请检查是否在启动对话框前进行初始化操作,使用以下代码进行初始化:\nDialogX.init(context);\n\n另外建议您前往查看 DialogX 的文档进行使用:https://github.com/kongzue/DialogX"); + return null; + } + return LayoutInflater.from(getOwnActivity()).inflate(layoutId, null); + } else { + return LayoutInflater.from(getApplicationContext()).inflate(layoutId, null); + } + } + + public boolean isShow() { + return isShow; + } + + public DialogXStyle getStyle() { + return style; + } + + public DialogX.THEME getTheme() { + return theme; + } + + public static void useTextInfo(TextView textView, TextInfo textInfo) { + if (textInfo == null) { + return; + } + if (textView == null) { + return; + } + if (textInfo.getFontSize() > 0) { + textView.setTextSize(textInfo.getFontSizeComplexUnit(), textInfo.getFontSize()); + } + if (textInfo.getFontColor() != 1) { + textView.setTextColor(textInfo.getFontColor()); + } + if (textInfo.getGravity() != -1) { + textView.setGravity(textInfo.getGravity()); + } + if (textInfo.isShowEllipsis()) { + textView.setEllipsize(TextUtils.TruncateAt.END); + } else { + textView.setEllipsize(null); + } + if (textInfo.getMaxLines() != -1) { + textView.setMaxLines(textInfo.getMaxLines()); + } else { + textView.setMaxLines(Integer.MAX_VALUE); + } + if (textInfo.getTypeface() != null) { + textView.setTypeface(textInfo.getTypeface()); + } + + textView.getPaint().setFakeBoldText(textInfo.isBold()); + } + + protected void showText(TextView textView, CharSequence text) { + if (textView == null) { + return; + } + if (isNull(text)) { + textView.setVisibility(View.GONE); + textView.setText(""); + } else { + textView.setVisibility(View.VISIBLE); + textView.setText(text); + } + } + + public static boolean isNull(String s) { + if (s == null || s.trim().isEmpty() || "null".equals(s) || "(null)".equals(s)) { + return true; + } + return false; + } + + public static boolean isNull(CharSequence c) { + String s = String.valueOf(c); + if (c == null || s.trim().isEmpty() || "null".equals(s) || "(null)".equals(s)) { + return true; + } + return false; + } + + public Resources getResources() { + if (getOwnActivity() != null) { + return getOwnActivity().getResources(); + } + if (getApplicationContext() == null) { + return Resources.getSystem(); + } + return getApplicationContext().getResources(); + } + + public int dip2px(float dpValue) { + final float scale = getResources().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + public boolean isLightTheme() { + if (theme == DialogX.THEME.AUTO) { + if (getApplicationContext() == null) { + return theme == DialogX.THEME.LIGHT; + } + return (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_NO; + } + return theme == DialogX.THEME.LIGHT; + } + + @Nullable + public FrameLayout getRootFrameLayout() { + Activity activity = getOwnActivity(); + FrameLayout decorView; + if (isActivityImplMode()) { + if (activity == null) { + activity = getTopActivity(); + if (activity == null) { + error("DialogX 错误:在 getRootFrameLayout() 时无法获取绑定的 activity,请确认是否正确初始化:\n" + "DialogX.init(context);\n\n" + "或者使用 .show(activity) 启动对话框\n另外建议您前往查看 DialogX 的文档进行使用:https://github.com/kongzue/DialogX"); + return null; + } + setOwnActivity(activity); + } + decorView = getDecorView(activity); + } else { + decorView = (FrameLayout) getDialogView().getParent(); + } + if (decorView == null) { + error("DialogX 错误:在 getRootFrameLayout() 时无法获 activity(" + activity + ") 的 decorView,请检查该 activity 是否正常显示且可以使 DialogX 基于其显示。\n" + "若该 activity 不可用,可通过以下代码配置豁免 DialogX 对话框绑定至该 activity,例如:\n" + "DialogX.unsupportedActivitiesPackageNames = new String[]{\n" + " \"com.bytedance.sdk.openadsdk.stub.activity\",\n" + " \"com.mobile.auth.gatewayauth\",\n" + " \"com.google.android.gms.ads\"\n" + "};\n\n" + "另外建议您前往查看 DialogX 的文档进行使用:https://github.com/kongzue/DialogX"); + return null; + } + rootFrameLayout = new WeakReference<>(decorView); + return rootFrameLayout.get(); + } + + public void tintColor(View view, int color) { + if (view == null) { + return; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + view.setBackgroundTintList(ColorStateList.valueOf(color)); + } + } + + /** + * 此标记用于拦截重复 dismiss 指令导致的关闭动画抖动异常 + */ + protected boolean dismissAnimFlag; + protected boolean preShow; + + protected void beforeShow() { + preShow = true; + dismissAnimFlag = false; + setOwnActivity(getTopActivity()); + if (getOwnActivity() == null && isActivityImplMode()) { + // 尝试重新获取 activity + init(null); + if (getOwnActivity() == null) { + error("DialogX 未初始化(E5)。\n请检查是否在启动对话框前进行初始化操作,使用以下代码进行初始化:\nDialogX.init(context);\n\n另外建议您前往查看 DialogX 的文档进行使用:https://github.com/kongzue/DialogX"); + return; + } + } + if (style.styleVer != DialogXStyle.styleVer) { + error("DialogX 所引用的 Style 不符合当前适用版本:" + DialogXStyle.styleVer + " 引入的 Style(" + style.getClass().getSimpleName() + ") 版本" + style.styleVer); + } + + if (dialogImplMode != DialogX.IMPL_MODE.VIEW && getOwnActivity() instanceof LifecycleOwner) { + Lifecycle lifecycle = ((LifecycleOwner) getOwnActivity()).getLifecycle(); + lifecycle.addObserver(new LifecycleEventObserver() { + @Override + public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { + if (event == Lifecycle.Event.ON_DESTROY) { + recycleDialog(getOwnActivity()); + } + } + }); + } + + // Hide IME + if (!(this instanceof NoTouchInterface)) { + View view = getOwnActivity().getCurrentFocus(); + if (view != null) { + InputMethodManager imm = (InputMethodManager) getOwnActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + } + } + } + + private boolean isActivityImplMode() { + return getDialogImplMode() == DialogX.IMPL_MODE.VIEW || getDialogImplMode() == DialogX.IMPL_MODE.FLOATING_ACTIVITY || getDialogImplMode() == DialogX.IMPL_MODE.DIALOG_FRAGMENT; + } + + protected String getString(int resId) { + if (getApplicationContext() == null) { + error("DialogX 未初始化(E6)。\n请检查是否在启动对话框前进行初始化操作,使用以下代码进行初始化:\nDialogX.init(context);\n\n另外建议您前往查看 DialogX 的文档进行使用:https://github.com/kongzue/DialogX"); + return null; + } + if (resId == 0) { + return ""; + } + return getResources().getString(resId); + } + + protected int getColor(int backgroundRes) { + if (getApplicationContext() == null) { + error("DialogX 未初始化(E7)。\n请检查是否在启动对话框前进行初始化操作,使用以下代码进行初始化:\nDialogX.init(context);\n\n另外建议您前往查看 DialogX 的文档进行使用:https://github.com/kongzue/DialogX"); + return Color.BLACK; + } + return getResources().getColor(backgroundRes); + } + + protected Integer getColorNullable(Integer res) { + return res == null ? null : getColor(res); + } + + protected Integer getColorNullable(Integer res, Integer defaultResId) { + return res == null ? getColor(defaultResId) : getColor(res); + } + + public enum BOOLEAN { + TRUE, FALSE + } + + public abstract String dialogKey(); + + protected static void runOnMain(Runnable runnable) { + if (!DialogX.autoRunOnUIThread || (getUiThread() != null && Thread.currentThread() == getUiThread())) { + runnable.run(); + return; + } + runOnMain(runnable, true); + } + + protected static Thread getUiThread() { + if (uiThread == null) { + uiThread = Looper.getMainLooper().getThread(); + } + return uiThread; + } + + protected static void runOnMain(Runnable runnable, boolean needWaitMainLooper) { + getMainHandler().post(runnable); + } + + protected static void runOnMainDelay(Runnable runnable, long delay) { + if (delay < 0) { + return; + } + if (!DialogX.autoRunOnUIThread) { + runnable.run(); + } + getMainHandler().postDelayed(runnable, delay); + } + + public View getDialogView() { + if (dialogView == null) { + return null; + } + return dialogView.get(); + } + + public Activity getOwnActivity() { + if (ownActivity == null || ownActivity.get() == null) { + setOwnActivity(getTopActivity()); + } + return ownActivity.get(); + } + + protected void cleanActivityContext() { + if (ownActivity != null) { + ownActivity.clear(); + } + dialogView = null; + ownActivity = null; + } + + public static void cleanAll() { + if (runningDialogList != null) { + CopyOnWriteArrayList copyOnWriteList = new CopyOnWriteArrayList<>(runningDialogList); + for (BaseDialog baseDialog : copyOnWriteList) { + if (baseDialog.isShow()) { + baseDialog.shutdown(); + } + baseDialog.cleanActivityContext(); + runningDialogList.remove(baseDialog); + } + } + } + + public static void recycleDialog(Activity activity) { + switch (DialogX.implIMPLMode) { + case WINDOW: + if (runningDialogList != null) { + CopyOnWriteArrayList copyOnWriteList = new CopyOnWriteArrayList<>(runningDialogList); + for (BaseDialog baseDialog : copyOnWriteList) { + if (baseDialog.getOwnActivity() == activity && baseDialog.dialogView != null) { + WindowUtil.dismiss(baseDialog.dialogView.get()); + if (baseDialog instanceof WaitDialog) { + ((WaitDialog) baseDialog).cleanInstance(); + } + runningDialogList.remove(baseDialog); + } + } + } + break; + case DIALOG_FRAGMENT: + if (runningDialogList != null) { + CopyOnWriteArrayList copyOnWriteList = new CopyOnWriteArrayList<>(runningDialogList); + for (BaseDialog baseDialog : copyOnWriteList) { + if (baseDialog.getOwnActivity() == activity && baseDialog.ownDialogFragmentImpl != null && baseDialog.ownDialogFragmentImpl.get() != null) { + baseDialog.ownDialogFragmentImpl.get().dismiss(); + if (baseDialog instanceof WaitDialog) { + ((WaitDialog) baseDialog).cleanInstance(); + } + runningDialogList.remove(baseDialog); + } + } + } + break; + case FLOATING_ACTIVITY: + + break; + default: + if (runningDialogList != null) { + CopyOnWriteArrayList copyOnWriteList = new CopyOnWriteArrayList<>(runningDialogList); + for (BaseDialog baseDialog : copyOnWriteList) { + if (baseDialog.getOwnActivity() == activity) { + baseDialog.cleanActivityContext(); + runningDialogList.remove(baseDialog); + if (baseDialog instanceof WaitDialog) { + ((WaitDialog) baseDialog).cleanInstance(); + } + } + } + } + break; + } + if (activity == getTopActivity()) { + cleanContext(); + } + } + + public static List getRunningDialogList() { + if (runningDialogList == null) { + return new ArrayList<>(); + } + return new CopyOnWriteArrayList<>(runningDialogList); + } + + public static List getRunningDialogList(Activity activity) { + List result = new ArrayList<>(); + for (BaseDialog dialog : runningDialogList) { + if (dialog != null && dialog.isShow && dialog.getOwnActivity() == activity) { + result.add(dialog); + } + } + return result; + } + + protected void imeShow(EditText editText, boolean show) { + if (getOwnActivity() == null) { + return; + } + InputMethodManager imm = (InputMethodManager) getOwnActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + if (show) { + imm.showSoftInput(editText, InputMethodManager.RESULT_UNCHANGED_SHOWN); + } else { + imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); + } + } + + public DialogX.IMPL_MODE getDialogImplMode() { + return dialogImplMode; + } + + protected static WindowInsets windowInsets; + + public static WindowInsets publicWindowInsets() { + return windowInsets; + } + + protected void bindFloatingActivity(DialogXFloatingWindowActivity activity) { + floatingWindowActivity = new WeakReference<>(activity); + } + + static WeakReference mMainHandler; + + private static Handler getMainHandler() { + if (mMainHandler != null && mMainHandler.get() != null) { + return mMainHandler.get(); + } + mMainHandler = new WeakReference<>(new Handler(Looper.getMainLooper())); + return mMainHandler.get(); + } + + @Nullable + public DialogListBuilder getDialogListBuilder() { + return dialogListBuilder; + } + + public void setDialogListBuilder(@NonNull DialogListBuilder dialogListBuilder) { + this.dialogListBuilder = dialogListBuilder; + } + + public void cleanDialogList() { + if (this.dialogListBuilder != null) { + this.dialogListBuilder.clear(); + } + this.dialogListBuilder = null; + } + + public boolean isPreShow() { + return preShow; + } + + public abstract D show(); + + protected void onDialogShow() { + if (onShowRunnable != null) onShowRunnable.run(this); + } + + protected void refreshUI() { + } + + protected void onDialogInit() { + } + + protected void onDialogRefreshUI() { + } + + protected void onDialogDismiss() { + if (onDismissRunnable != null) onDismissRunnable.run(this); + } + + @NonNull + @Override + public Lifecycle getLifecycle() { + return lifecycle; + } + + public int getMaxWidth() { + if (maxWidth == 0) { + return DialogX.dialogMaxWidth; + } + return maxWidth; + } + + public int getMaxHeight() { + if (maxHeight == 0) { + return DialogX.dialogMaxHeight; + } + return maxHeight; + } + + public int getMinWidth() { + if (minWidth == 0) { + return DialogX.dialogMinWidth; + } + return minWidth; + } + + public int getMinHeight() { + if (minHeight == 0) { + return DialogX.dialogMinHeight; + } + return minHeight; + } + + protected void setLifecycleState(Lifecycle.State s) { + if (lifecycle == null || s == null) return; + try { + lifecycle.setCurrentState(s); + } catch (Exception e) { + } + } + + protected static FrameLayout getDecorView(Activity activity) { + if (activity == null || activity.getWindow() == null || !(activity.getWindow().getDecorView() instanceof FrameLayout)) + return null; + return (FrameLayout) activity.getWindow().getDecorView(); + } + + protected List findAllBlurView(View v) { + List result = new ArrayList<>(); + if (v instanceof BlurViewType) { + result.add(v); + } + if (v instanceof ViewGroup) { + ViewGroup group = (ViewGroup) v; + for (int i = 0; i < group.getChildCount(); i++) { + List child = findAllBlurView(group.getChildAt(i)); + if (child != null) result.addAll(child); + } + } + if (!result.isEmpty()) { + return result; + } + return null; + } + + protected Integer getIntStyleAttr(Integer styleValue) { + return styleValue <= 0 ? null : styleValue; + } + + protected Integer getIntStyleAttr(Integer styleValue, Integer defaultValue) { + return styleValue <= 0 ? defaultValue : styleValue; + } + + protected Float getFloatStyleAttr(Float styleValue) { + return styleValue <= 0 ? null : styleValue; + } + + protected Float getFloatStyleAttr(Float styleValue, Float defaultValue) { + if (styleValue <= 0) { + return defaultValue; + } + return styleValue; + } + + protected void setDialogView(View view) { + dialogView = new WeakReference<>(view); + } + + protected void haptic(View v) { + if (v != null) if (DialogX.useHaptic && isHapticFeedbackEnabled == -1) + v.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + else if (isHapticFeedbackEnabled == 1) + v.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + } + + protected boolean isHide; + + public boolean isHide() { + return isHide; + } + + public T getData(String key) { + return data == null ? null : (T) data.get(key); + } + + public BaseDialog setData(String key, Object obj) { + if (data == null) data = new HashMap<>(); + data.put(key, obj); + return this; + } + + public boolean isEnableImmersiveMode() { + return enableImmersiveMode; + } + + public BaseDialog setEnableImmersiveMode(boolean enableImmersiveMode) { + this.enableImmersiveMode = enableImmersiveMode; + refreshUI(); + return this; + } + + /** + * 启动前执行 + * + * @return 若返回 true 表示拦截启动流程 + */ + public boolean preShow(BaseDialog dialog) { + return false; + } + + /** + * 关闭前执行 + * + * @return 若返回 true 表示拦截退出流程 + */ + public boolean preDismiss(BaseDialog dialog) { + return false; + } + + public int getThisOrderIndex() { + return thisOrderIndex; + } + + public BaseDialog setThisOrderIndex(int thisOrderIndex) { + this.thisOrderIndex = thisOrderIndex; + return this; + } + + protected int getHighestOrderIndex() { + if (getOwnActivity() != null && getDecorView(getOwnActivity()) != null) { + return getDecorView(getOwnActivity()).getChildCount(); + } + return runningDialogList == null ? 1 : runningDialogList.size(); + } + + protected static boolean isActivityDestroyed(Activity activity) { + return activity == null + || activity.isFinishing() + || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && activity.isDestroyed()) + || (activity instanceof LifecycleOwner && ((LifecycleOwner) activity).getLifecycle().getCurrentState() == Lifecycle.State.DESTROYED); + } + + public boolean dispatchTouchEvent(MotionEvent event) { + if (getDialogView() == null) { + if (ownActivity != null && ownActivity.get() != null) { + return ownActivity.get().dispatchTouchEvent(event); + } else { + return false; + } + } + return getDialogView().dispatchTouchEvent(event); + } + + public boolean runAction(int actionId) { + DialogXRunnable runnable = dialogActionRunnableMap.get(actionId); + if (runnable != null) { + runnable.run(this); + return true; + } + return false; + } + + public abstract void callDialogDismiss(); + + protected void bindDismissWithLifecycleOwnerPrivate(LifecycleOwner owner) { + if (owner == null) return; + owner.getLifecycle().addObserver(new LifecycleEventObserver() { + @Override + public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { + if (event == Lifecycle.Event.ON_STOP || event == Lifecycle.Event.ON_DESTROY) { + callDialogDismiss(); + source.getLifecycle().removeObserver(this); + } + } + }); + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BaseOnDialogClickCallback.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BaseOnDialogClickCallback.java new file mode 100644 index 0000000..23fcb6c --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BaseOnDialogClickCallback.java @@ -0,0 +1,14 @@ +package com.kongzue.dialogx.interfaces; + +import android.view.View; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/22 15:07 + */ +public interface BaseOnDialogClickCallback { + +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BlurViewType.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BlurViewType.java new file mode 100644 index 0000000..a451f9f --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BlurViewType.java @@ -0,0 +1,8 @@ +package com.kongzue.dialogx.interfaces; + +import androidx.annotation.Nullable; + +public interface BlurViewType { + void setOverlayColor(@Nullable Integer color); + void setRadiusPx(@Nullable Float r); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BottomDialogSlideEventLifecycleCallback.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BottomDialogSlideEventLifecycleCallback.java new file mode 100644 index 0000000..0923dc1 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BottomDialogSlideEventLifecycleCallback.java @@ -0,0 +1,22 @@ +package com.kongzue.dialogx.interfaces; + +import android.view.MotionEvent; +import android.view.View; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2022/12/11 17:39 + */ +public abstract class BottomDialogSlideEventLifecycleCallback extends DialogLifecycleCallback { + + public boolean onSlideClose(D dialog) { + return false; + } + + public boolean onSlideTouchEvent(D dialog, View v, MotionEvent event) { + return false; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BottomMenuListViewTouchEvent.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BottomMenuListViewTouchEvent.java new file mode 100644 index 0000000..3471e29 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/BottomMenuListViewTouchEvent.java @@ -0,0 +1,17 @@ +package com.kongzue.dialogx.interfaces; + +import android.view.MotionEvent; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/11/18 15:01 + */ +public abstract class BottomMenuListViewTouchEvent { + + public void down(MotionEvent event){}; + public void move(MotionEvent event){}; + public void up(MotionEvent event){}; +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogConvertViewInterface.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogConvertViewInterface.java new file mode 100644 index 0000000..1ea5c70 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogConvertViewInterface.java @@ -0,0 +1,19 @@ +package com.kongzue.dialogx.interfaces; + +import android.view.View; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/6 15:52 + */ +public interface DialogConvertViewInterface { + + void init(); + + void refreshView(); + + void doDismiss(View v); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogLifecycleCallback.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogLifecycleCallback.java new file mode 100644 index 0000000..2ec8efb --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogLifecycleCallback.java @@ -0,0 +1,52 @@ +package com.kongzue.dialogx.interfaces; + +import androidx.annotation.NonNull; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LifecycleRegistry; + +import com.kongzue.dialogx.DialogX; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/22 14:09 + */ +public abstract class DialogLifecycleCallback implements LifecycleOwner { + + private final LifecycleRegistry registry = new LifecycleRegistry(this); + + public void onShow(T dialog) { + try { + //概率性报 no event down from INITIALIZED,目前尚不清楚为何 + if (registry.getCurrentState() != Lifecycle.State.CREATED) { + registry.setCurrentState(Lifecycle.State.CREATED); + } + } catch (Exception e) { + } + if (DialogX.dialogLifeCycleListener != null && DialogX.dialogLifeCycleListener != this) { + DialogX.dialogLifeCycleListener.onShow(dialog); + } + } + + public void onDismiss(T dialog) { + try { + if (registry.getCurrentState() != Lifecycle.State.DESTROYED) { + //概率性报 no event down from INITIALIZED,目前尚不清楚为何 + registry.setCurrentState(Lifecycle.State.DESTROYED); + } + } catch (Exception e) { + } + if (DialogX.dialogLifeCycleListener != null && DialogX.dialogLifeCycleListener != this) { + DialogX.dialogLifeCycleListener.onDismiss(dialog); + } + } + + @NonNull + @Override + public Lifecycle getLifecycle() { + return registry; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXAnimInterface.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXAnimInterface.java new file mode 100644 index 0000000..31a9b9e --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXAnimInterface.java @@ -0,0 +1,22 @@ +package com.kongzue.dialogx.interfaces; + +import android.animation.ValueAnimator; +import android.view.ViewGroup; + +import com.kongzue.dialogx.util.ObjectRunnable; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2022/9/5 9:21 + */ +public abstract class DialogXAnimInterface { + + public void doShowAnim(D dialog, ViewGroup dialogBodyView) { + } + + public void doExitAnim(D dialog, ViewGroup dialogBodyView) { + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXBaseBottomDialog.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXBaseBottomDialog.java new file mode 100644 index 0000000..c63d48d --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXBaseBottomDialog.java @@ -0,0 +1,5 @@ +package com.kongzue.dialogx.interfaces; + +public interface DialogXBaseBottomDialog { + boolean isBottomNonSafetyAreaBySelf(); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXRunnable.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXRunnable.java new file mode 100644 index 0000000..6bb0f26 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXRunnable.java @@ -0,0 +1,7 @@ +package com.kongzue.dialogx.interfaces; + +public interface DialogXRunnable { + + void run(D dialog); + +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXSafetyModeInterface.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXSafetyModeInterface.java new file mode 100644 index 0000000..e8ccf8c --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXSafetyModeInterface.java @@ -0,0 +1,5 @@ +package com.kongzue.dialogx.interfaces; + +public interface DialogXSafetyModeInterface { + int getDialogXSafetyMode(); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DynamicWindowInsetsAnimationListener.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DynamicWindowInsetsAnimationListener.java new file mode 100644 index 0000000..04cf759 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/DynamicWindowInsetsAnimationListener.java @@ -0,0 +1,8 @@ +package com.kongzue.dialogx.interfaces; + +import android.view.WindowInsets; + +public interface DynamicWindowInsetsAnimationListener { + + void onChange(WindowInsets windowInsets); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/MenuIconAdapter.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/MenuIconAdapter.java new file mode 100644 index 0000000..0216b80 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/MenuIconAdapter.java @@ -0,0 +1,20 @@ +package com.kongzue.dialogx.interfaces; + +import android.widget.ImageView; + +public abstract class MenuIconAdapter extends OnIconChangeCallBack { + + public MenuIconAdapter() { + } + + public MenuIconAdapter(boolean autoTintIconInLightOrDarkMode) { + super(autoTintIconInLightOrDarkMode); + } + + public abstract boolean applyIcon(D dialog, int index, String menuText, ImageView iconImageView); + + @Override + public int getIcon(D dialog, int index, String menuText) { + return 0; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/MenuItemLayoutRefreshCallback.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/MenuItemLayoutRefreshCallback.java new file mode 100644 index 0000000..bf993ec --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/MenuItemLayoutRefreshCallback.java @@ -0,0 +1,16 @@ +package com.kongzue.dialogx.interfaces; + +import android.view.View; +import android.view.ViewGroup; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2023/2/22 16:47 + */ +public interface MenuItemLayoutRefreshCallback { + + void getView(D dialog, int position, View convertView, ViewGroup parent); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/MenuItemTextInfoInterceptor.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/MenuItemTextInfoInterceptor.java new file mode 100644 index 0000000..a1a246d --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/MenuItemTextInfoInterceptor.java @@ -0,0 +1,28 @@ +package com.kongzue.dialogx.interfaces; + +import com.kongzue.dialogx.util.TextInfo; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2022/1/22 21:32 + */ +public abstract class MenuItemTextInfoInterceptor { + + private boolean autoTintIconInLightOrDarkMode; + + public MenuItemTextInfoInterceptor() { + } + + public MenuItemTextInfoInterceptor(boolean autoTintIconInLightOrDarkMode) { + this.autoTintIconInLightOrDarkMode = autoTintIconInLightOrDarkMode; + } + + public abstract TextInfo menuItemTextInfo(D dialog, int index, String menuText); + + public boolean isAutoTintIconInLightOrDarkMode() { + return autoTintIconInLightOrDarkMode; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/NoTouchInterface.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/NoTouchInterface.java new file mode 100644 index 0000000..810acda --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/NoTouchInterface.java @@ -0,0 +1,11 @@ +package com.kongzue.dialogx.interfaces; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2022/7/19 17:37 + */ +public interface NoTouchInterface { +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBackPressedListener.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBackPressedListener.java new file mode 100644 index 0000000..f607214 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBackPressedListener.java @@ -0,0 +1,12 @@ +package com.kongzue.dialogx.interfaces; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/25 15:48 + */ +public interface OnBackPressedListener { + boolean onBackPressed(D dialog); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBackgroundMaskClickListener.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBackgroundMaskClickListener.java new file mode 100644 index 0000000..511c4c8 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBackgroundMaskClickListener.java @@ -0,0 +1,14 @@ +package com.kongzue.dialogx.interfaces; + +import android.view.View; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2022/7/8 16:27 + */ +public interface OnBackgroundMaskClickListener { + boolean onClick(D dialog, View v); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBindView.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBindView.java new file mode 100644 index 0000000..8ffff5e --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBindView.java @@ -0,0 +1,225 @@ +package com.kongzue.dialogx.interfaces; + +import android.app.Activity; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.RelativeLayout; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.util.views.ExtendChildLayoutParamsFrameLayout; + +import static com.kongzue.dialogx.DialogX.ERROR_INIT_TIPS; + +import androidx.appcompat.app.AppCompatActivity; + +import java.security.SecureRandom; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/8 17:00 + */ +public abstract class OnBindView { + + int layoutResId; + View customView; + private final int PARENT_FLAG = -109; + + public OnBindView(int layoutResId) { + if (BaseDialog.getTopActivity() == null) { + DialogX.error(ERROR_INIT_TIPS); + return; + } + this.layoutResId = layoutResId; + customView = LayoutInflater.from(BaseDialog.getTopActivity()).inflate(layoutResId, new RelativeLayout(BaseDialog.getTopActivity()), false); + } + + public OnBindView(int layoutResId, boolean async) { + if (BaseDialog.getTopActivity() == null) { + DialogX.error(ERROR_INIT_TIPS); + return; + } + this.layoutResId = layoutResId; + if (async) { + new Thread() { + @Override + public void run() { + super.run(); + synchronized (OnBindView.this) { + customView = LayoutInflater.from(BaseDialog.getTopActivity()).inflate(layoutResId, new RelativeLayout(BaseDialog.getTopActivity()), false); + if (waitBindRunnable != null) { + waitBindRunnable.run(); + waitBindRunnable = null; + } + } + } + }.start(); + } else { + customView = LayoutInflater.from(BaseDialog.getTopActivity()).inflate(layoutResId, new RelativeLayout(BaseDialog.getTopActivity()), false); + } + } + + public OnBindView(View customView) { + this.customView = customView; + } + + private androidx.fragment.app.Fragment fragment; + private android.app.Fragment supportFragment; + private int fragmentParentId = View.NO_ID; + + private int getFragmentParentId() { + if (fragmentParentId == View.NO_ID) { + fragmentParentId = View.generateViewId(); + } + return fragmentParentId; + } + + public OnBindView(androidx.fragment.app.Fragment fragment) { + if (BaseDialog.getTopActivity() == null) return; + this.customView = new ExtendChildLayoutParamsFrameLayout(BaseDialog.getTopActivity()); + this.customView.setId(getFragmentParentId()); + this.fragment = fragment; + this.supportFragment = null; + } + + public OnBindView(android.app.Fragment supportFragment) { + if (BaseDialog.getTopActivity() == null) return; + this.customView = new ExtendChildLayoutParamsFrameLayout(BaseDialog.getTopActivity()); + this.customView.setId(getFragmentParentId()); + this.supportFragment = supportFragment; + this.fragment = null; + } + + public abstract void onBind(D dialog, View v); + + public void setEvent(D dialog, View v){} + + public void onFragmentBind(D dialog, View frameLayout, androidx.fragment.app.Fragment fragment, androidx.fragment.app.FragmentManager fragmentManager) { + } + + public void onFragmentBind(D dialog, View frameLayout, android.app.Fragment fragment, android.app.FragmentManager fragmentManager) { + } + + public int getLayoutResId() { + return layoutResId; + } + + public OnBindView setLayoutResId(int layoutResId) { + this.layoutResId = layoutResId; + return this; + } + + public View getCustomView() { + if (customView == null) { + customView = LayoutInflater.from(BaseDialog.getTopActivity()).inflate(layoutResId, new RelativeLayout(BaseDialog.getTopActivity()), false); + } + return customView; + } + + public OnBindView setCustomView(View customView) { + this.customView = customView; + return this; + } + + public void clean() { + layoutResId = 0; + customView = null; + } + + @Deprecated + public void bindParent(ViewGroup parentView) { + if (getCustomView() == null) { + waitBind(parentView, null); + return; + } + if (getCustomView().getParent() != null) { + if (getCustomView().getParent() == parentView) { + return; + } + ((ViewGroup) getCustomView().getParent()).removeView(getCustomView()); + } + ViewGroup.LayoutParams lp = parentView.getLayoutParams(); + if (lp == null) { + lp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + } + parentView.addView(getCustomView(), lp); + } + + public void bindParent(ViewGroup parentView, BaseDialog dialog) { + if (getCustomView() == null) { + waitBind(parentView, null); + return; + } + if (getCustomView().getParent() == parentView || parentView.getTag(PARENT_FLAG) == getCustomView().toString()) { + return; + } + if (getCustomView().getParent() != null) { + ((ViewGroup) getCustomView().getParent()).removeView(getCustomView()); + } + ViewGroup.LayoutParams lp = getCustomView().getLayoutParams(); + if (lp == null) { + lp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + } + parentView.addView(getCustomView(), lp); + onBind((D) dialog, getCustomView()); + callSetEvent((D) dialog, getCustomView()); + if (fragment != null || supportFragment != null) { + if (dialog.getDialogImplMode() != DialogX.IMPL_MODE.VIEW) { + BaseDialog.error(dialog.dialogKey() + "非 VIEW 实现模式不支持 fragment 作为子布局显示。\n" + + "其原因为 Window 中不存在 FragmentManager,无法对子布局中的 fragment 进行管理。"); + return; + } + getCustomView().post(new Runnable() { + @Override + public void run() { + if (fragment != null && getCustomView() instanceof FrameLayout && dialog.getOwnActivity() instanceof AppCompatActivity) { + AppCompatActivity appCompatActivity = (AppCompatActivity) dialog.getOwnActivity(); + androidx.fragment.app.FragmentTransaction transaction = appCompatActivity.getSupportFragmentManager().beginTransaction(); + transaction.add(getFragmentParentId(), fragment); + transaction.commit(); + onFragmentBind((D) dialog, getCustomView(), fragment, appCompatActivity.getSupportFragmentManager()); + } + if (supportFragment != null && getCustomView() instanceof FrameLayout && dialog.getOwnActivity() instanceof Activity) { + Activity activity = dialog.getOwnActivity(); + android.app.FragmentTransaction transaction = activity.getFragmentManager().beginTransaction(); + transaction.add(getFragmentParentId(), supportFragment); + transaction.commit(); + onFragmentBind((D) dialog, getCustomView(), supportFragment, activity.getFragmentManager()); + } + } + }); + } + } + + private int dialogHash, parentViewHash; + + private void callSetEvent(D dialog, View view) { + if (dialog.hashCode() != dialogHash || view.hashCode() != parentViewHash) { + dialogHash = dialog.hashCode(); + parentViewHash = view.hashCode(); + setEvent(dialog, getCustomView()); + } + } + + private Runnable waitBindRunnable; + + private void waitBind(ViewGroup parentView, BaseDialog dialog) { + waitBindRunnable = new Runnable() { + @Override + public void run() { + if (getCustomView() == null) { + if (dialog == null) { + bindParent(parentView); + } else { + bindParent(parentView, dialog); + } + } + } + }; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBindingView.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBindingView.java new file mode 100644 index 0000000..7b084ba --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnBindingView.java @@ -0,0 +1,108 @@ +package com.kongzue.dialogx.interfaces; + +import static com.kongzue.dialogx.interfaces.BaseDialog.error; + +import android.view.LayoutInflater; +import android.view.View; + +import androidx.fragment.app.Fragment; +import androidx.viewbinding.ViewBinding; + +import com.kongzue.dialogx.R; + +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +public abstract class OnBindingView extends OnBindView { + + protected VB binding; + + public OnBindingView(VB binding) { + super(binding.getRoot()); + this.binding = binding; + } + + public OnBindingView() { + super((View) null); + setCustomView(getBindingRootView(getViewBinding())); + this.binding = (VB) getCustomView().getTag(R.id.dialogx_view_binding_tag_key); + } + + public OnBindingView(Class viewBindingClass) { + super(getBindingRootView(getViewBinding(viewBindingClass))); + this.binding = (VB) getCustomView().getTag(R.id.dialogx_view_binding_tag_key); + } + + public OnBindingView(String viewBindingClassName) { + super(getBindingRootView(getViewBinding(viewBindingClassName))); + this.binding = (VB) getCustomView().getTag(R.id.dialogx_view_binding_tag_key); + } + + private static View getBindingRootView(ViewBinding viewBinding) { + if (viewBinding == null) { + return new View(BaseDialog.getContext()); + } + View view = viewBinding.getRoot(); + view.setTag(R.id.dialogx_view_binding_tag_key, viewBinding); + return view; + } + + private ViewBinding getViewBinding() { + Type superclass = getClass().getGenericSuperclass(); + if (superclass instanceof ParameterizedType) { + Class clazz = (Class) ((ParameterizedType) superclass).getActualTypeArguments()[1]; + return getViewBinding(clazz); + } else { + error("DialogX: OnBindingView初始化异常,若要使用无参构建,必须指定ViewBinding泛型"); + return null; + } + } + + private static ViewBinding getViewBinding(String bindingClassName) { + try { + return getViewBinding(Class.forName(bindingClassName)); + } catch (ClassNotFoundException e) { + error("DialogX: OnBindingView初始化异常,未能根据bindingClassName:" + bindingClassName + "找到对应的ViewBinding,请尝试指定ViewBinding实例"); + throw new RuntimeException(e); + } + } + + private static ViewBinding getViewBinding(Class bindingClass) { + try { + Method inflateMethod = bindingClass.getMethod("inflate", LayoutInflater.class); + return (ViewBinding) inflateMethod.invoke((Object) null, LayoutInflater.from(BaseDialog.getContext())); + } catch (Exception var5) { + error("DialogX: OnBindingView初始化异常,未能根据bindingClass找到对应的ViewBinding,请尝试指定ViewBinding实例"); + var5.printStackTrace(); + } + return null; + } + + public OnBindingView(int layoutResId) { + super(layoutResId); + } + + public OnBindingView(int layoutResId, boolean async) { + super(layoutResId, async); + } + + public OnBindingView(View customView) { + super(customView); + } + + public OnBindingView(Fragment fragment) { + super(fragment); + } + + public OnBindingView(android.app.Fragment supportFragment) { + super(supportFragment); + } + + @Override + public void onBind(D dialog, View v) { + onBind(dialog, v, binding); + } + + public abstract void onBind(D dialog, View v, VB binding); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnDialogButtonClickListener.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnDialogButtonClickListener.java new file mode 100644 index 0000000..1265c82 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnDialogButtonClickListener.java @@ -0,0 +1,16 @@ +package com.kongzue.dialogx.interfaces; + +import android.view.View; + +/** + * Author: @Kongzue + * Github: https://github.com/kongzue/ + * Homepage: http://kongzue.com/ + * Mail: myzcxhh@live.cn + * CreateTime: 2019/3/29 18:44 + */ +public interface OnDialogButtonClickListener extends BaseOnDialogClickCallback{ + + boolean onClick(D dialog, View v); + +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnIconChangeCallBack.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnIconChangeCallBack.java new file mode 100644 index 0000000..e188342 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnIconChangeCallBack.java @@ -0,0 +1,26 @@ +package com.kongzue.dialogx.interfaces; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/9 14:54 + */ +public abstract class OnIconChangeCallBack { + + public OnIconChangeCallBack() { + } + + private Boolean autoTintIconInLightOrDarkMode; + + public OnIconChangeCallBack(boolean autoTintIconInLightOrDarkMode) { + this.autoTintIconInLightOrDarkMode = autoTintIconInLightOrDarkMode; + } + + public abstract int getIcon(D dialog, int index, String menuText); + + public Boolean isAutoTintIconInLightOrDarkMode() { + return autoTintIconInLightOrDarkMode; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnInputDialogButtonClickListener.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnInputDialogButtonClickListener.java new file mode 100644 index 0000000..3f4f7d8 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnInputDialogButtonClickListener.java @@ -0,0 +1,17 @@ +package com.kongzue.dialogx.interfaces; + +import android.view.View; + +import com.kongzue.dialogx.dialogs.MessageDialog; + +/** + * Author: @Kongzue + * Github: https://github.com/kongzue/ + * Homepage: http://kongzue.com/ + * Mail: myzcxhh@live.cn + * CreateTime: 2019/4/8 21:09 + */ +public interface OnInputDialogButtonClickListener extends BaseOnDialogClickCallback{ + + boolean onClick(D dialog, View v, String inputStr); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnMenuButtonClickListener.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnMenuButtonClickListener.java new file mode 100644 index 0000000..9f563c8 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnMenuButtonClickListener.java @@ -0,0 +1,8 @@ +package com.kongzue.dialogx.interfaces; + +import android.view.View; + +public interface OnMenuButtonClickListener extends BaseOnDialogClickCallback{ + + boolean onClick(D dialog, View v); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnMenuItemClickListener.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnMenuItemClickListener.java new file mode 100644 index 0000000..68e313e --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnMenuItemClickListener.java @@ -0,0 +1,12 @@ +package com.kongzue.dialogx.interfaces; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/10 6:26 + */ +public interface OnMenuItemClickListener { + boolean onClick(D dialog, CharSequence text, int index); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnMenuItemSelectListener.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnMenuItemSelectListener.java new file mode 100644 index 0000000..c0e5346 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnMenuItemSelectListener.java @@ -0,0 +1,37 @@ +package com.kongzue.dialogx.interfaces; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/4/11 19:18 + */ +public abstract class OnMenuItemSelectListener implements OnMenuItemClickListener { + + /** + * 警告:不建议重写此方法! + * 如果选择使用 OnMenuItemSelectListener 作为 BottomMenu 的回调,那么点击 Item 后,菜单默认不应该关闭, + * 若选择自行处理菜单点击 onClick,那么请务必 return true 作为返回值, + * 否则不会处理 onOneItemSelect 或 onMultiItemSelect 事件。 + * + * @param dialog BottomMenu实例 + * @param text 菜单文本 + * @param index 菜单索引值 + * @return return true:拦截自动关闭对话框;return false:点击后关闭对话框 + */ + @Deprecated + @Override + public boolean onClick(D dialog, CharSequence text, int index) { + return true; + } + + public void onOneItemSelect(D dialog, CharSequence text, int index, boolean select) { + + } + + public void onMultiItemSelect(D dialog, CharSequence[] text, int[] indexArray) { + + } + +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnSafeInsetsChangeListener.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnSafeInsetsChangeListener.java new file mode 100644 index 0000000..e0fda15 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/OnSafeInsetsChangeListener.java @@ -0,0 +1,15 @@ +package com.kongzue.dialogx.interfaces; + +import android.graphics.Rect; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/19 16:06 + */ +public interface OnSafeInsetsChangeListener { + + void onChange(Rect unsafeRect); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/PopMoveDisplacementInterceptor.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/PopMoveDisplacementInterceptor.java new file mode 100644 index 0000000..79a46aa --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/PopMoveDisplacementInterceptor.java @@ -0,0 +1,41 @@ +package com.kongzue.dialogx.interfaces; + +import android.animation.ValueAnimator; +import android.view.View; + +public abstract class PopMoveDisplacementInterceptor { + + /** + * 重置提示对话框新增时,旧的对话框让位位移的动画具体参数 + * + * @param index 对话框索引 + * @param dialog 对话框 + * @param fromY 从哪来 + * @param toY 往哪去 + * @param dialogHeight 对话框本身的高度 + * @param allTipSize 提示框总数 + * @param moveBack 是否向后位移 + * @return 你要改为往哪去 + */ + public float resetAnimY(int index, D dialog, float fromY, float toY, int dialogHeight, int allTipSize, boolean moveBack) { + return toY; + } + + /** + * 动画更新器 + * + * @param index 对话框索引 + * @param dialog 对话框 + * @param dialogBody 对话框内容布局 + * @param fromY 从哪来 + * @param toY 往哪去 + * @param progress 动画进度(0f~1f) + * @param animation 动画执行器 + * @param allTipSize 提示框总数 + * @param moveBack 是否向后位移 + * @return 返回true表示拦截处理,否则依然会执行原本的动画 + */ + public boolean animUpdater(int index, D dialog, View dialogBody, float fromY, float toY, float progress, ValueAnimator animation, int allTipSize, boolean moveBack) { + return false; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/SELECT_MODE.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/SELECT_MODE.java new file mode 100644 index 0000000..5f5dbc6 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/SELECT_MODE.java @@ -0,0 +1,5 @@ +package com.kongzue.dialogx.interfaces; + +public enum SELECT_MODE { + NONE, SINGLE, MULTIPLE +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/interfaces/ScrollController.java b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/ScrollController.java new file mode 100644 index 0000000..8788b40 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/interfaces/ScrollController.java @@ -0,0 +1,46 @@ +package com.kongzue.dialogx.interfaces; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/8/5 8:39 + */ +public interface ScrollController { + + /** + * 返回已滚动的距离 + * 若该距离为 0,BottomDialog、FullScreenDialog 将直接接管和衔接触控事件 + * 若距离不为 0,由本控件处理触控事件 + * + * @return 已滚动的距离 + */ + int getScrollDistance(); + + /** + * 设置是否可以滑动 + * 若可以滑动,BottomDialog、FullScreenDialog 将尝试接管和衔接触控事件, + * 若不可以滑动,BottomDialog、FullScreenDialog 将直接拦截触控事件 + * + * @return 是否可以滑动 + */ + boolean isCanScroll(); + + /** + * BottomDialog、FullScreenDialog 在接管触控事件时会通过此方法传入 lockScroll, + * 若 lockScroll 为真,请勿处理任何触摸事件。 + * + * 举例:建议增加以下代码: + * @Override + * public boolean onTouchEvent(MotionEvent event) { + * if (lockScroll) return false; + * return super.onTouchEvent(event); + * } + * + * @param lockScroll 是否锁定滑动 + */ + void lockScroll(boolean lockScroll); + + boolean isLockScroll(); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/style/MaterialStyle.java b/DialogX/src/main/java/com/kongzue/dialogx/style/MaterialStyle.java new file mode 100644 index 0000000..1ca0038 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/style/MaterialStyle.java @@ -0,0 +1,336 @@ +package com.kongzue.dialogx.style; + +import android.content.Context; + +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.ProgressViewInterface; +import com.kongzue.dialogx.util.views.ProgressView; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/26 13:09 + */ +public class MaterialStyle extends DialogXStyle { + + public static MaterialStyle style() { + return new MaterialStyle(); + } + + @Override + public int layout(boolean light) { + return light ? R.layout.layout_dialogx_material : R.layout.layout_dialogx_material_dark; + } + + @Override + public int enterAnimResId() { + return R.anim.anim_dialogx_default_enter; + } + + @Override + public int exitAnimResId() { + return R.anim.anim_dialogx_default_exit; + } + + @Override + public int[] verticalButtonOrder() { + return new int[]{BUTTON_OK, BUTTON_OTHER, BUTTON_CANCEL}; + } + + @Override + public int[] horizontalButtonOrder() { + return new int[]{BUTTON_OTHER, SPACE, BUTTON_CANCEL, BUTTON_OK}; + } + + @Override + public int splitWidthPx() { + return 1; + } + + @Override + public int splitColorRes(boolean light) { + return 0; + } + + @Override + public BlurBackgroundSetting messageDialogBlurSettings() { + return null; + } + + @Override + public HorizontalButtonRes overrideHorizontalButtonRes() { + return new DefaultHorizontalButtonRes(); + } + + public class DefaultHorizontalButtonRes extends HorizontalButtonRes { + @Override + public int overrideHorizontalOkButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_light : R.drawable.button_dialogx_material_night; + } + + @Override + public int overrideHorizontalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_light : R.drawable.button_dialogx_material_night; + } + + @Override + public int overrideHorizontalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_light : R.drawable.button_dialogx_material_night; + } + } + + @Override + public VerticalButtonRes overrideVerticalButtonRes() { + return new DefaultVerticalButtonRes(); + } + + public class DefaultVerticalButtonRes extends VerticalButtonRes { + @Override + public int overrideVerticalOkButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_light : R.drawable.button_dialogx_material_night; + } + + @Override + public int overrideVerticalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_light : R.drawable.button_dialogx_material_night; + } + + @Override + public int overrideVerticalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_light : R.drawable.button_dialogx_material_night; + } + } + + @Override + public WaitTipRes overrideWaitTipRes() { + return new DefaultWaitTipRes(); + } + + public class DefaultWaitTipRes extends WaitTipRes { + @Override + public int overrideWaitLayout(boolean light) { + return R.layout.layout_dialogx_wait; + } + + @Override + public int overrideRadiusPx() { + return -1; + } + + @Override + public boolean blurBackground() { + return false; + } + + @Override + public int overrideBackgroundColorRes(boolean light) { + return 0; + } + + @Override + public int overrideTextColorRes(boolean light) { + return light ? R.color.white : R.color.black; + } + + @Override + public ProgressViewInterface overrideWaitView(Context context, boolean light) { + return new ProgressView(context); + } + } + + @Override + public BottomDialogRes overrideBottomDialogRes() { + return new DefaultBottomDialogRes(); + } + + public class DefaultBottomDialogRes extends BottomDialogRes { + @Override + public boolean touchSlide() { + return true; + } + + @Override + public int overrideDialogLayout(boolean light) { + return light ? R.layout.layout_dialogx_bottom_material : R.layout.layout_dialogx_bottom_material_dark; + } + + @Override + public int overrideMenuDividerDrawableRes(boolean light) { + return light ? R.drawable.rect_dialogx_material_menu_split_divider : R.drawable.rect_dialogx_material_menu_split_divider_night; + } + + @Override + public int overrideMenuDividerHeight(boolean light) { + return 1; + } + + @Override + public int overrideMenuTextColor(boolean light) { + return light ? R.color.black90 : R.color.white90; + } + + @Override + public float overrideBottomDialogMaxHeight() { + return 0.6f; + } + + @Override + public int overrideMenuItemLayout(boolean light, int index, int count, boolean isContentVisibility) { + return 0; + } + + @Override + public int overrideSelectionMenuBackgroundColor(boolean light) { + return 0; + } + + @Override + public boolean selectionImageTint(boolean light) { + return false; + } + + @Override + public int overrideSelectionImage(boolean light, boolean isSelected) { + return isSelected ? R.mipmap.img_dialogx_bottom_menu_material_item_selection : R.mipmap.img_dialogx_bottom_menu_material_item_non_select; + } + + @Override + public int overrideMultiSelectionImage(boolean light, boolean isSelected) { + return isSelected ? R.mipmap.img_dialogx_bottom_menu_material_item_multi_selection : R.mipmap.img_dialogx_bottom_menu_material_item_non_multi_select; + } + } + + @Override + public PopTipSettings popTipSettings() { + return new DefaultPopTipSettings(); + } + + public class DefaultPopTipSettings extends PopTipSettings{ + @Override + public int layout(boolean light) { + return light ? R.layout.layout_dialogx_poptip_material : R.layout.layout_dialogx_poptip_material_dark; + } + + @Override + public ALIGN align() { + return ALIGN.BOTTOM; + } + + @Override + public int enterAnimResId(boolean light) { + return R.anim.anim_dialogx_default_enter; + } + + @Override + public int exitAnimResId(boolean light) { + return R.anim.anim_dialogx_default_exit; + } + + @Override + public boolean tintIcon() { + return true; + } + } + + @Override + public PopNotificationSettings popNotificationSettings() { + return new DefaultPopNotificationSettings(); + } + + public class DefaultPopNotificationSettings extends PopNotificationSettings{ + @Override + public int layout(boolean light) { + return light ? R.layout.layout_dialogx_popnotification_material : R.layout.layout_dialogx_popnotification_material_dark; + } + + @Override + public PopNotificationSettings.ALIGN align() { + return ALIGN.TOP; + } + + @Override + public int enterAnimResId(boolean light) { + return R.anim.anim_dialogx_notification_enter; + } + + @Override + public int exitAnimResId(boolean light) { + return R.anim.anim_dialogx_notification_exit; + } + + @Override + public boolean tintIcon() { + return false; + } + } + + @Override + public PopMenuSettings popMenuSettings() { + return new DefaultPopMenuSettings(); + } + + public class DefaultPopMenuSettings extends PopMenuSettings{ + @Override + public int layout(boolean light) { + return light ? R.layout.layout_dialogx_popmenu_material : R.layout.layout_dialogx_popmenu_material_dark; + } + + @Override + public BlurBackgroundSetting blurBackgroundSettings() { + return null; + } + + @Override + public int backgroundMaskColorRes() { + return 0; + } + + @Override + public int overrideMenuDividerDrawableRes(boolean b) { + return 0; + } + + @Override + public int overrideMenuDividerHeight(boolean b) { + return 0; + } + + @Override + public int overrideMenuTextColor(boolean light) { + return light ? R.color.black90 : R.color.white90; + } + + @Override + public int overrideMenuItemLayoutRes(boolean b) { + return 0; + } + + @Override + public int overrideMenuItemBackgroundRes(boolean b, int i, int i1, boolean b1) { + return 0; + } + + @Override + public int overrideSelectionMenuBackgroundColor(boolean b) { + return 0; + } + + @Override + public boolean selectionImageTint(boolean b) { + return false; + } + + @Override + public int paddingVertical() { + return 0; + } + } + + @Override + public boolean tintButtonBackground() { + return true; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/ActivityRunnable.java b/DialogX/src/main/java/com/kongzue/dialogx/util/ActivityRunnable.java new file mode 100644 index 0000000..4c789ec --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/ActivityRunnable.java @@ -0,0 +1,14 @@ +package com.kongzue.dialogx.util; + +import android.app.Activity; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/12/20 15:21 + */ +public interface ActivityRunnable { + void run(Activity activity); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/BottomDialogTouchEventInterceptor.java b/DialogX/src/main/java/com/kongzue/dialogx/util/BottomDialogTouchEventInterceptor.java new file mode 100644 index 0000000..9d1201c --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/BottomDialogTouchEventInterceptor.java @@ -0,0 +1,186 @@ +package com.kongzue.dialogx.util; + +import android.animation.ObjectAnimator; +import android.content.res.Resources; +import android.graphics.RectF; +import android.view.MotionEvent; +import android.view.View; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.dialogs.BottomDialog; +import com.kongzue.dialogx.interfaces.BottomDialogSlideEventLifecycleCallback; +import com.kongzue.dialogx.interfaces.ScrollController; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/7 4:01 + */ +public class BottomDialogTouchEventInterceptor { + + /** + * 下边三个值用于判断触控过程, + * isBkgTouched:标记是否已按下 + * bkgTouchDownY:记录起始触控位置 + * scrolledY:记录 ScrollView 已滚动过的距离,下次触控事件将接着上次的位置继续滑动 + * bkgOldY:记录按下时 bkg 的位置,用于区分松开手指时,bkg 移动的方向。 + */ + private boolean isBkgTouched = false; + private float bkgTouchDownY; + private float scrolledY; + private float bkgOldY; + private boolean onlyRestrictingSlideTouchEventsToScrollLayoutAreas = false; + /** + * 0:bkg接收触控事件,-1:scrollView进行滚动 + * 此标记的意义在于,当从 [scrollView滚动] 与 [bkg接收触控事件] 状态切换时, + * 需要对bkgTouchDownY、scrolledY的值进行刷新,否则触控连续过程会出现闪跳。 + */ + private int oldMode; + + public BottomDialogTouchEventInterceptor(BottomDialog me, BottomDialog.DialogImpl impl) { + refresh(me, impl); + } + + public void refresh(final BottomDialog me, final BottomDialog.DialogImpl impl) { + if (me == null || impl == null || impl.bkg == null || impl.scrollView == null) { + return; + } + /** + * BottomDialog 触控事件说明: + * bkg 将拦截并接管所有触控操作。 + * BottomDialog 的启动方式依据是内容布局高度是否大于可显示安全区域的高度。 + * bkg 会在合适的时机,直接接管控制 ScrollView 的滚动。 + * 因此,请确保内容布局的高度计算方式一定是按照内容高度计算, + * 即,请重写 onMeasure 方法: + * @Override + * protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + * int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); + * super.onMeasure(widthMeasureSpec, expandSpec); + * } + */ + View interceptTouchView = impl.bkg; + if (me.isAllowInterceptTouch()) { + if (isOnlyRestrictingSlideTouchEventsToScrollLayoutAreas()){ + impl.bkg.setOnTouchListener(null); + interceptTouchView = (View) impl.scrollView; + } + View finalInterceptTouchView = interceptTouchView; + interceptTouchView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (me.getDialogLifecycleCallback() instanceof BottomDialogSlideEventLifecycleCallback) { + if (((BottomDialogSlideEventLifecycleCallback) me.getDialogLifecycleCallback()).onSlideTouchEvent(me, v, event)) { + return true; + } + } + //这里 return 什么实际上无关紧要,重点在于 MaxRelativeLayout.java(dispatchTouchEvent:184) 的事件分发会独立触发此处的额外滑动事件 + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + bkgTouchDownY = event.getY(); + isBkgTouched = true; + bkgOldY = impl.boxBkg.getY(); + break; + case MotionEvent.ACTION_MOVE: + if (isBkgTouched && me.isAllowInterceptTouch()) { + float aimY = impl.boxBkg.getY() + event.getY() - bkgTouchDownY; + if (impl.scrollView.isCanScroll() && touchInScrollView(finalInterceptTouchView, impl.scrollView, event)) { + if (aimY > impl.boxRoot.getUnsafePlace().top) { + if (impl.scrollView.getScrollDistance() == 0) { + impl.scrollView.lockScroll(true); + impl.boxBkg.setY(aimY); + } else { + bkgTouchDownY = event.getY(); + } + } else { + impl.scrollView.lockScroll(false); + impl.boxBkg.setY(impl.boxRoot.getUnsafePlace().top); + } + } else { + if (aimY > impl.boxRoot.getUnsafePlace().top) { + impl.boxBkg.setY(aimY); + return true; + } else { + impl.boxBkg.setY(impl.boxRoot.getUnsafePlace().top); + } + } + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + scrolledY = impl.scrollView.getScrollDistance(); + isBkgTouched = false; + if (bkgOldY == impl.boxRoot.getUnsafePlace().top) { + if (impl.boxBkg.getY() > impl.boxRoot.getUnsafePlace().top + impl.bkgEnterAimY + DialogX.touchSlideTriggerThreshold) { + impl.preDismiss(); + } else if (impl.boxBkg.getY() != bkgOldY) { + ObjectAnimator enterAnim = ObjectAnimator.ofFloat(impl.boxBkg, "y", impl.boxBkg.getY(), + impl.bkgEnterAimY); + enterAnim.setDuration(300); + enterAnim.start(); + } + } else { + if (impl.boxBkg.getY() > bkgOldY + DialogX.touchSlideTriggerThreshold) { + impl.preDismiss(); + } else if (impl.boxBkg.getY() != bkgOldY) { + ObjectAnimator enterAnim = ObjectAnimator.ofFloat(impl.boxBkg, "y", impl.boxBkg.getY(), impl.boxRoot.getUnsafePlace().top); + enterAnim.setDuration(300); + enterAnim.start(); + } + } + if (impl.scrollView instanceof ScrollController) { + impl.scrollView.lockScroll(false); + } + break; + } + return false; + } + }); + } else { + if (impl.scrollView instanceof ScrollController) { + ((ScrollController) impl.scrollView).lockScroll(false); + } + interceptTouchView.setOnTouchListener(null); + } + } + + /** + * 检查 MotionEvent 触摸点是否在 scrollView 相对于对话框移动布局 slideView 范围内 + * + * @param slideView 对话框移动布局 + * @param scrollView 内部滚动布局 + * @param event 触摸事件 + * @return 是否在范围内 + */ + private boolean touchInScrollView(View slideView, ScrollController scrollView, MotionEvent event) { + View scrollViewImpl = (View) scrollView; + RectF scrollViewLocation = new RectF(); + int[] scrollViewScreenLoc = new int[2]; + int[] rootViewScreenLoc = new int[2]; + scrollViewImpl.getLocationInWindow(scrollViewScreenLoc); + slideView.getLocationInWindow(rootViewScreenLoc); + + scrollViewLocation.left = scrollViewScreenLoc[0] - rootViewScreenLoc[0]; + scrollViewLocation.top = scrollViewScreenLoc[1] - rootViewScreenLoc[1]; + scrollViewLocation.right = scrollViewLocation.left + scrollViewImpl.getWidth(); + scrollViewLocation.bottom = scrollViewLocation.top + scrollViewImpl.getHeight(); + + return event.getX() >= scrollViewLocation.left && event.getX() <= scrollViewLocation.right && + event.getY() >= scrollViewLocation.top && event.getY() <= scrollViewLocation.bottom; + } + + private int dip2px(float dpValue) { + final float scale = Resources.getSystem().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + public boolean isOnlyRestrictingSlideTouchEventsToScrollLayoutAreas() { + return onlyRestrictingSlideTouchEventsToScrollLayoutAreas; + } + + public BottomDialogTouchEventInterceptor setOnlyRestrictingSlideTouchEventsToScrollLayoutAreas(boolean onlyRestrictingSlideTouchEventsToScrollLayoutAreas) { + this.onlyRestrictingSlideTouchEventsToScrollLayoutAreas = onlyRestrictingSlideTouchEventsToScrollLayoutAreas; + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/BottomMenuArrayAdapter.java b/DialogX/src/main/java/com/kongzue/dialogx/util/BottomMenuArrayAdapter.java new file mode 100644 index 0000000..8d50b87 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/BottomMenuArrayAdapter.java @@ -0,0 +1,278 @@ +package com.kongzue.dialogx.util; + +import static com.kongzue.dialogx.interfaces.BaseDialog.isNull; +import static com.kongzue.dialogx.interfaces.BaseDialog.useTextInfo; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.os.Build; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.Space; +import android.widget.TextView; + +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.dialogs.BottomMenu; +import com.kongzue.dialogx.interfaces.MenuIconAdapter; +import com.kongzue.dialogx.interfaces.SELECT_MODE; + +import java.util.List; + +/** + * @author: Kongzue + * @github: 空祖Github + * @homepage: 主页 + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/7 0:00 + */ +public class BottomMenuArrayAdapter extends BaseAdapter { + + private BottomMenu bottomMenu; + public List objects; + public Context context; + + public BottomMenuArrayAdapter(BottomMenu bottomMenu, Context context, List objects) { + this.objects = objects; + this.context = context; + this.bottomMenu = bottomMenu; + } + + class ViewHolder { + ImageView imgDialogxMenuIcon; + ImageView imgDialogxMenuSelection; + TextView txtDialogxMenuText; + Space spaceDialogxRightPadding; + } + + @Override + public int getCount() { + return objects.size(); + } + + @Override + public CharSequence getItem(int position) { + return objects.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + TextInfo defaultMenuTextInfo; + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + ViewHolder viewHolder = null; + if (convertView == null) { + viewHolder = new ViewHolder(); + LayoutInflater mInflater = LayoutInflater.from(context); + + int resourceId = R.layout.item_dialogx_material_bottom_menu_normal_text; + if (bottomMenu.getStyle().overrideBottomDialogRes() != null) { + resourceId = bottomMenu.getStyle().overrideBottomDialogRes().overrideMenuItemLayout(bottomMenu.isLightTheme(), position, getCount(), false); + if (resourceId == 0) { + resourceId = R.layout.item_dialogx_material_bottom_menu_normal_text; + } else { + if (!isNull(bottomMenu.getTitle()) || !isNull(bottomMenu.getMessage()) || bottomMenu.getCustomView() != null) { + if (position == 0) { + resourceId = bottomMenu.getStyle().overrideBottomDialogRes().overrideMenuItemLayout(bottomMenu.isLightTheme(), position, getCount(), true); + } + } + } + } + convertView = mInflater.inflate(resourceId, null); + + viewHolder.imgDialogxMenuIcon = convertView.findViewById(R.id.img_dialogx_menu_icon); + viewHolder.imgDialogxMenuSelection = convertView.findViewById(R.id.img_dialogx_menu_selection); + viewHolder.txtDialogxMenuText = convertView.findViewById(R.id.txt_dialogx_menu_text); + viewHolder.spaceDialogxRightPadding = convertView.findViewById(R.id.space_dialogx_right_padding); + + convertView.setTag(viewHolder); + } else { + viewHolder = (ViewHolder) convertView.getTag(); + } + if (!bottomMenu.isMenuItemEnable(position)) { + convertView.setAlpha(0.4f); + } else { + convertView.setAlpha(1f); + } + if (bottomMenu.getSelectMode() == SELECT_MODE.SINGLE) { + if (viewHolder.imgDialogxMenuSelection != null) { + if (bottomMenu.getSelection() == position) { + viewHolder.imgDialogxMenuSelection.setVisibility(View.VISIBLE); + int overrideSelectionImageResId = bottomMenu.getStyle().overrideBottomDialogRes().overrideSelectionImage(bottomMenu.isLightTheme(), true); + if (overrideSelectionImageResId != 0) { + viewHolder.imgDialogxMenuSelection.setImageResource(overrideSelectionImageResId); + } + } else { + int overrideSelectionImageResId = bottomMenu.getStyle().overrideBottomDialogRes().overrideSelectionImage(bottomMenu.isLightTheme(), false); + if (overrideSelectionImageResId != 0) { + viewHolder.imgDialogxMenuSelection.setVisibility(View.VISIBLE); + viewHolder.imgDialogxMenuSelection.setImageResource(overrideSelectionImageResId); + } else { + viewHolder.imgDialogxMenuSelection.setVisibility(View.INVISIBLE); + } + } + } + } else if (bottomMenu.getSelectMode() == SELECT_MODE.MULTIPLE) { + if (viewHolder.imgDialogxMenuSelection != null) { + if (bottomMenu.getSelectionList().contains(position)) { + viewHolder.imgDialogxMenuSelection.setVisibility(View.VISIBLE); + int overrideSelectionImageResId = bottomMenu.getStyle().overrideBottomDialogRes().overrideMultiSelectionImage(bottomMenu.isLightTheme(), true); + if (overrideSelectionImageResId != 0) { + viewHolder.imgDialogxMenuSelection.setImageResource(overrideSelectionImageResId); + } + } else { + int overrideSelectionImageResId = bottomMenu.getStyle().overrideBottomDialogRes().overrideMultiSelectionImage(bottomMenu.isLightTheme(), false); + if (overrideSelectionImageResId != 0) { + viewHolder.imgDialogxMenuSelection.setVisibility(View.VISIBLE); + viewHolder.imgDialogxMenuSelection.setImageResource(overrideSelectionImageResId); + } else { + viewHolder.imgDialogxMenuSelection.setVisibility(View.INVISIBLE); + } + } + } + } else { + viewHolder.imgDialogxMenuSelection.setVisibility(View.GONE); + } + int overrideSelectionBackgroundColorRes = 0; + if (bottomMenu.getStyle().overrideBottomDialogRes() != null) { + overrideSelectionBackgroundColorRes = bottomMenu.getStyle().overrideBottomDialogRes().overrideSelectionMenuBackgroundColor(bottomMenu.isLightTheme()); + } + if (bottomMenu.getSelection() == position) { + //选中的背景变色 + if (overrideSelectionBackgroundColorRes != 0) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + convertView.setBackgroundTintList(ColorStateList.valueOf(context.getResources().getColor(overrideSelectionBackgroundColorRes))); + } + } + } + CharSequence text = objects.get(position); + + int textColor = bottomMenu.isLightTheme() ? R.color.black90 : R.color.white90; + if (bottomMenu.getStyle().overrideBottomDialogRes() != null) { + if (bottomMenu.getStyle().overrideBottomDialogRes().overrideMenuTextColor(bottomMenu.isLightTheme()) != 0) { + textColor = bottomMenu.getStyle().overrideBottomDialogRes().overrideMenuTextColor(bottomMenu.isLightTheme()); + } + } + + if (null != text) { + if (defaultMenuTextInfo == null) { + defaultMenuTextInfo = new TextInfo().setShowEllipsis(viewHolder.txtDialogxMenuText.getEllipsize() == TextUtils.TruncateAt.END).setFontColor(viewHolder.txtDialogxMenuText.getTextColors().getDefaultColor()).setBold(viewHolder.txtDialogxMenuText.getPaint().isFakeBoldText()).setFontSize(px2dip(viewHolder.txtDialogxMenuText.getTextSize())).setGravity(viewHolder.txtDialogxMenuText.getGravity()).setMaxLines(viewHolder.txtDialogxMenuText.getMaxLines()); + } + viewHolder.txtDialogxMenuText.setText(text); + viewHolder.txtDialogxMenuText.setTextColor(context.getResources().getColor(textColor)); + if (bottomMenu.getMenuItemTextInfoInterceptor() != null) { + TextInfo textInfo = bottomMenu.getMenuItemTextInfoInterceptor().menuItemTextInfo(bottomMenu, position, text.toString()); + if (textInfo != null) { + useTextInfo(viewHolder.txtDialogxMenuText, textInfo); + } else { + if (bottomMenu.getMenuTextInfo() != null) { + useTextInfo(viewHolder.txtDialogxMenuText, bottomMenu.getMenuTextInfo()); + } else { + useTextInfo(viewHolder.txtDialogxMenuText, defaultMenuTextInfo); + } + } + } else { + if (bottomMenu.getMenuTextInfo() != null) { + useTextInfo(viewHolder.txtDialogxMenuText, bottomMenu.getMenuTextInfo()); + } + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (viewHolder.imgDialogxMenuSelection != null) { + if (bottomMenu.getStyle().overrideBottomDialogRes() != null && bottomMenu.getStyle().overrideBottomDialogRes().selectionImageTint(bottomMenu.isLightTheme())) { + viewHolder.imgDialogxMenuSelection.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } else { + viewHolder.imgDialogxMenuSelection.setImageTintList(null); + } + } + } + + if (bottomMenu.getOnIconChangeCallBack() != null) { + if (bottomMenu.getOnIconChangeCallBack() instanceof MenuIconAdapter) { + boolean result = ((MenuIconAdapter) bottomMenu.getOnIconChangeCallBack()).applyIcon(bottomMenu, position, text.toString(), viewHolder.imgDialogxMenuIcon); + boolean autoTintIconInLightOrDarkMode = bottomMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode() == null ? bottomMenu.isAutoTintIconInLightOrDarkMode() : bottomMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode(); + + viewHolder.imgDialogxMenuIcon.setVisibility(result ? View.VISIBLE : View.GONE); + if (result) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (autoTintIconInLightOrDarkMode) { + viewHolder.imgDialogxMenuIcon.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } + } + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.VISIBLE); + } + } else { + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } else { + int resId = bottomMenu.getOnIconChangeCallBack().getIcon(bottomMenu, position, text.toString()); + boolean autoTintIconInLightOrDarkMode = bottomMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode() == null ? bottomMenu.isAutoTintIconInLightOrDarkMode() : bottomMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode(); + + if (resId != 0) { + viewHolder.imgDialogxMenuIcon.setVisibility(View.VISIBLE); + viewHolder.imgDialogxMenuIcon.setImageResource(resId); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.VISIBLE); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (autoTintIconInLightOrDarkMode) { + viewHolder.imgDialogxMenuIcon.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } + } + } else { + viewHolder.imgDialogxMenuIcon.setVisibility(View.GONE); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } + } else { + if (bottomMenu.getIconResIds() != null) { + int resId = bottomMenu.getIconResIds(position); + boolean autoTintIconInLightOrDarkMode = bottomMenu.isAutoTintIconInLightOrDarkMode(); + + if (resId != 0) { + viewHolder.imgDialogxMenuIcon.setVisibility(View.VISIBLE); + viewHolder.imgDialogxMenuIcon.setImageResource(resId); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.VISIBLE); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (autoTintIconInLightOrDarkMode) { + viewHolder.imgDialogxMenuIcon.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } + } + } else { + viewHolder.imgDialogxMenuIcon.setVisibility(View.GONE); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } else { + viewHolder.imgDialogxMenuIcon.setVisibility(View.GONE); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } + } + if (bottomMenu.getMenuMenuItemLayoutRefreshCallback() != null) { + bottomMenu.getMenuMenuItemLayoutRefreshCallback().getView(bottomMenu, position, convertView, parent); + } + return convertView; + } + + private int px2dip(float pxValue) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (pxValue / scale + 0.5f); + } +} \ No newline at end of file diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/DialogListBuilder.java b/DialogX/src/main/java/com/kongzue/dialogx/util/DialogListBuilder.java new file mode 100644 index 0000000..e372de4 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/DialogListBuilder.java @@ -0,0 +1,74 @@ +package com.kongzue.dialogx.util; + +import com.kongzue.dialogx.interfaces.BaseDialog; + +import java.util.ArrayList; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2022/8/8 15:08 + */ +public class DialogListBuilder { + + ArrayList dialogs; + + public static DialogListBuilder create(BaseDialog... dialogs) { + DialogListBuilder builder = new DialogListBuilder(); + for (BaseDialog d : dialogs) { + if (d != null) builder.add(d); + } + return builder; + } + + public DialogListBuilder add(BaseDialog dialog) { + if (dialogs == null) { + dialogs = new ArrayList<>(); + } + if (dialog == null || dialog.isShow() || dialog.isPreShow()) { + return this; + } + dialog.setDialogListBuilder(this); + dialogs.add(dialog); + return this; + } + + public DialogListBuilder show() { + if (dialogs == null || dialogs.isEmpty()) { + return this; + } + while (!dialogs.isEmpty() && dialogs.get(0) == null) { + dialogs.remove(0); + } + dialogs.get(0).show(); + return this; + } + + public void showNext() { + if (dialogs == null || dialogs.isEmpty()) { + return; + } + while (!dialogs.isEmpty() && dialogs.get(0) == null) { + dialogs.remove(0); + } + if (!dialogs.isEmpty() && dialogs.get(0) != null) { + dialogs.get(0).show(); + } + } + + public boolean isEmpty() { + if (dialogs == null) { + return true; + } + while (!dialogs.isEmpty() && dialogs.get(0) == null) { + dialogs.remove(0); + } + return dialogs.isEmpty(); + } + + public void clear() { + dialogs.clear(); + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXFloatingWindowActivity.java b/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXFloatingWindowActivity.java new file mode 100644 index 0000000..4db0f62 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXFloatingWindowActivity.java @@ -0,0 +1,145 @@ +package com.kongzue.dialogx.util; + +import android.app.Activity; +import android.os.Build; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.View; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; + +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/12/20 14:58 + */ +public class DialogXFloatingWindowActivity extends AppCompatActivity { + + static WeakReference dialogXFloatingWindowActivity; + int fromActivityHashCode; + List shownDialogXList = new ArrayList<>(); + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + dialogXFloatingWindowActivity = new WeakReference<>(this); + super.onCreate(savedInstanceState); + setContentView(R.layout.layout_dialogx_empty); + + int fromActivityUiStatus = getIntent().getIntExtra("fromActivityUiStatus", 0); + if (fromActivityUiStatus == 0) { + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + } else { + getWindow().getDecorView().setSystemUiVisibility(fromActivityUiStatus | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + } + + setFromActivityHashCode(getIntent().getIntExtra("from", 0)); + String dialogXKey; + ActivityRunnable activityRunnable = BaseDialog.getActivityRunnable(dialogXKey = getIntent().getStringExtra("dialogXKey")); + if (activityRunnable == null) { + finish(); + } else { + shownDialogXList.add(dialogXKey); + activityRunnable.run(this); + } + + getWindow().getDecorView().setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() != MotionEvent.ACTION_CANCEL && getFromActivity() != null) { + if (getFromActivity() != null && !(getFromActivity() instanceof DialogXFloatingWindowActivity)) { + return getFromActivity().dispatchTouchEvent(event); + } + } + return false; + } + }); + } + + public boolean isSameFrom(int fromActivityHashCode) { + return fromActivityHashCode == this.fromActivityHashCode; + } + + public void showDialogX(String dialogXKey) { + ActivityRunnable activityRunnable = BaseDialog.getActivityRunnable(dialogXKey); + if (activityRunnable != null) { + shownDialogXList.add(dialogXKey); + activityRunnable.run(this); + } + } + + public int getFromActivityHashCode() { + return fromActivityHashCode; + } + + public DialogXFloatingWindowActivity setFromActivityHashCode(int fromActivityHashCode) { + this.fromActivityHashCode = fromActivityHashCode; + return this; + } + + public static DialogXFloatingWindowActivity getDialogXFloatingWindowActivity() { + if (dialogXFloatingWindowActivity == null) return null; + return dialogXFloatingWindowActivity.get(); + } + + public void finish(String dialogXKey) { + shownDialogXList.remove(dialogXKey); + if (shownDialogXList.isEmpty()) { + if (dialogXFloatingWindowActivity != null) { + dialogXFloatingWindowActivity.clear(); + } + dialogXFloatingWindowActivity = null; + super.finish(); + int version = Integer.valueOf(Build.VERSION.SDK_INT); + if (version > 5) { + overridePendingTransition(0, 0); + } + } + } + + public void finish() { + if (dialogXFloatingWindowActivity != null) { + dialogXFloatingWindowActivity.clear(); + } + dialogXFloatingWindowActivity = null; + super.finish(); + int version = Integer.valueOf(Build.VERSION.SDK_INT); + if (version > 5) { + overridePendingTransition(0, 0); + } + } + + boolean isScreenshot; + + public boolean isScreenshot() { + return isScreenshot; + } + + public DialogXFloatingWindowActivity setScreenshot(boolean screenshot) { + isScreenshot = screenshot; + return this; + } + + /** + * 原始用户操作界面 + */ + WeakReference fromActivity; + + public Activity getFromActivity() { + return fromActivity == null ? null : fromActivity.get(); + } + + public DialogXFloatingWindowActivity setFromActivity(Activity fromActivity) { + this.fromActivity = new WeakReference<>(fromActivity); + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXImplModeAgent.java b/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXImplModeAgent.java new file mode 100644 index 0000000..e6de8e7 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXImplModeAgent.java @@ -0,0 +1,51 @@ +package com.kongzue.dialogx.util; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.interfaces.BaseDialog; + +import java.lang.ref.WeakReference; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2022/5/17 15:16 + */ +public class DialogXImplModeAgent { + + private DialogX.IMPL_MODE implMode; + private WeakReference dialogWeakReference; + + public DialogXImplModeAgent(DialogX.IMPL_MODE implMode, BaseDialog dialog) { + this.implMode = implMode; + this.dialogWeakReference = new WeakReference<>(dialog); + } + + public DialogX.IMPL_MODE getImplMode() { + return implMode; + } + + public DialogXImplModeAgent setImplMode(DialogX.IMPL_MODE implMode) { + this.implMode = implMode; + return this; + } + + public BaseDialog getDialog() { + if (dialogWeakReference == null) { + return null; + } + return dialogWeakReference.get(); + } + + public DialogXImplModeAgent setDialogWeakReference(BaseDialog dialog) { + this.dialogWeakReference = new WeakReference<>(dialog); + return this; + } + + public void recycle() { + if (dialogWeakReference != null) dialogWeakReference.clear(); + dialogWeakReference = null; + implMode = null; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXValueAnimator.java b/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXValueAnimator.java new file mode 100644 index 0000000..e0ab595 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXValueAnimator.java @@ -0,0 +1,200 @@ +package com.kongzue.dialogx.util; + +import android.os.Handler; +import android.os.Looper; +import android.view.animation.Interpolator; + +public class DialogXValueAnimator { + + public static final int RESTART = 1; + public static final int REVERSE = 2; + public static final int INFINITE = -1; + + Handler handler = new Handler(Looper.getMainLooper()); + + private long duration; + private long startTime; + private boolean isRunning = false; + private ValueUpdateListener listener; + private Interpolator interpolator; + private float startValue; + private float endValue; + private int repeatCount = 0; + private int currentRepeatCount = 0; + private int refreshInterval = 16; // 控制更新频率,默认 60 FPS + + public static DialogXValueAnimator ofFloat(float start, float end){ + return new DialogXValueAnimator(start, end); + } + + public DialogXValueAnimator(float startValue, float endValue) { + this.startValue = startValue; + this.endValue = endValue; + } + + public void setFloatValues(float start, float end) { + startValue = start; + endValue = end; + } + + public void addUpdateListener(ValueUpdateListener listener) { + this.listener = listener; + } + + public void start() { + if (isRunning) { + return; + } + + isRunning = true; + startTime = System.currentTimeMillis(); + + // 开启一个线程用于更新动画 + new Thread(new Runnable() { + @Override + public void run() { + while (isRunning) { + long currentTime = System.currentTimeMillis(); + long elapsed = currentTime - startTime; + + if (elapsed < duration) { + float fraction = (float) elapsed / duration; + + // 使用插值器处理动画进度 + if (interpolator != null) { + fraction = interpolator.getInterpolation(fraction); + } + + float animatedValue = startValue + fraction * (endValue - startValue); + + // 通知监听器 + if (listener != null) { + getHandler().post(new Runnable() { + @Override + public void run() { + listener.onValueUpdate(animatedValue); + } + }); + } + } else { + // 动画结束 + isRunning = false; + + onAnimationEnd(); + + // 处理重复播放 + if (repeatCount == INFINITE || currentRepeatCount < repeatCount) { + currentRepeatCount++; + startTime = System.currentTimeMillis(); + isRunning = true; + } + } + + try { + Thread.sleep(refreshInterval); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + }).start(); + } + + private Handler getHandler() { + if (handler==null) { + handler = new Handler(Looper.getMainLooper()); + } + return handler; + } + + private void onAnimationEnd() { + + } + + public long getDuration() { + return duration; + } + + public DialogXValueAnimator setDuration(long duration) { + this.duration = duration; + return this; + } + + public long getStartTime() { + return startTime; + } + + public DialogXValueAnimator setStartTime(long startTime) { + this.startTime = startTime; + return this; + } + + public ValueUpdateListener getListener() { + return listener; + } + + public DialogXValueAnimator setListener(ValueUpdateListener listener) { + this.listener = listener; + return this; + } + + public float getStartValue() { + return startValue; + } + + public DialogXValueAnimator setStartValue(float startValue) { + this.startValue = startValue; + return this; + } + + public float getEndValue() { + return endValue; + } + + public DialogXValueAnimator setEndValue(float endValue) { + this.endValue = endValue; + return this; + } + + public void cancel() { + isRunning = false; + } + + public void setInterpolator(Interpolator interpolator) { + this.interpolator = interpolator; + } + + public void setRepeatCount(int repeatCount) { + this.repeatCount = repeatCount; + } + + // 监听器接口 + public interface ValueUpdateListener { + void onValueUpdate(float animatedValue); + } + + public Interpolator getInterpolator() { + return interpolator; + } + + public boolean isRunning() { + return isRunning; + } + + public int getRepeatCount() { + return repeatCount; + } + + public int getCurrentRepeatCount() { + return currentRepeatCount; + } + + public int getRefreshInterval() { + return refreshInterval; + } + + public DialogXValueAnimator setRefreshInterval(int refreshInterval) { + this.refreshInterval = refreshInterval; + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXViewLoc.java b/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXViewLoc.java new file mode 100644 index 0000000..b175546 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/DialogXViewLoc.java @@ -0,0 +1,88 @@ +package com.kongzue.dialogx.util; + +public class DialogXViewLoc { + + public static boolean skipErrorLoc = true; + + private float x; + private float y; + private float w; + private float h; + + public float getX() { + return x; + } + + public DialogXViewLoc setX(float x) { + this.x = x; + return this; + } + + public float getY() { + return y; + } + + public DialogXViewLoc setY(float y) { + this.y = y; + return this; + } + + public float getW() { + return w; + } + + public DialogXViewLoc setW(float w) { + this.w = w; + return this; + } + + public float getH() { + return h; + } + + public DialogXViewLoc setH(float h) { + this.h = h; + return this; + } + + public boolean isSameLoc(int[] loc) { + if (loc.length == 2) { + return x == loc[0] && y == loc[1]; + } + if (loc.length == 4) { + return x == loc[0] && y == loc[1] && w == loc[2] && h == loc[3]; + } + return false; + } + + public void set(int[] loc) { + if (loc.length == 2) { + if (skipErrorLoc) { + if (loc[0] != 0) { + x = loc[0]; + } + if (loc[1] != 0) { + y = loc[1]; + } + } else { + x = loc[0]; + y = loc[1]; + } + } + if (loc.length == 4) { + if (skipErrorLoc) { + if (loc[0] != 0) { + x = loc[0]; + } + if (loc[1] != 0) { + y = loc[1]; + } + } else { + x = loc[0]; + y = loc[1]; + } + w = loc[2]; + h = loc[3]; + } + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/FullScreenDialogTouchEventInterceptor.java b/DialogX/src/main/java/com/kongzue/dialogx/util/FullScreenDialogTouchEventInterceptor.java new file mode 100644 index 0000000..588dfea --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/FullScreenDialogTouchEventInterceptor.java @@ -0,0 +1,160 @@ +package com.kongzue.dialogx.util; + +import android.animation.ObjectAnimator; +import android.content.res.Resources; +import android.graphics.RectF; +import android.view.MotionEvent; +import android.view.View; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.dialogs.FullScreenDialog; +import com.kongzue.dialogx.interfaces.ScrollController; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/19 13:54 + */ +public class FullScreenDialogTouchEventInterceptor { + + /** + * 下边三个值用于判断触控过程, + * isBkgTouched:标记是否已按下 + * bkgTouchDownY:记录起始触控位置 + * scrolledY:记录 ScrollView 已滚动过的距离,下次触控事件将接着上次的位置继续滑动 + * bkgOldY:记录按下时 bkg 的位置,用于区分松开手指时,bkg 移动的方向。 + */ + private boolean isBkgTouched = false; + private float bkgTouchDownY; + private float bkgOldY; + + public FullScreenDialogTouchEventInterceptor(FullScreenDialog me, FullScreenDialog.DialogImpl impl) { + refresh(me, impl); + } + + public void refresh(final FullScreenDialog me, final FullScreenDialog.DialogImpl impl) { + if (me == null || impl == null || impl.bkg == null) { + return; + } + if (me.isAllowInterceptTouch()) { + View touchView = impl.boxCustom; + if (impl.scrollView != null) { + touchView = impl.bkg; + } + touchView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + bkgTouchDownY = event.getY(); + isBkgTouched = true; + bkgOldY = impl.bkg.getY(); + break; + case MotionEvent.ACTION_MOVE: + if (isBkgTouched) { + float aimY = impl.bkg.getY() + event.getY() - bkgTouchDownY; + if (impl.scrollView != null && impl.scrollView.isCanScroll() && touchInScrollView(v, impl.scrollView, event)) { + if (aimY > me.getDialogImpl().getEnterY()) { + if (impl.scrollView.getScrollDistance() == 0) { + if (impl.scrollView instanceof ScrollController) { + ((ScrollController) impl.scrollView).lockScroll(true); + } + impl.bkg.setY(aimY); + } else { + bkgTouchDownY = event.getY(); + } + } else { + if (impl.scrollView instanceof ScrollController) { + ((ScrollController) impl.scrollView).lockScroll(false); + } + aimY = (me.getDialogImpl().getEnterY()); + impl.bkg.setY(aimY); + } + } else { + if (aimY < me.getDialogImpl().getEnterY()) { + aimY = me.getDialogImpl().getEnterY(); + } + impl.bkg.setY(aimY); + } + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + isBkgTouched = false; + if (bkgOldY == me.getDialogImpl().getEnterY()) { + if (impl.bkg.getY() < DialogX.touchSlideTriggerThreshold) { + ObjectAnimator enterAnim = ObjectAnimator.ofFloat(impl.bkg, "y", impl.bkg.getY(), me.getDialogImpl().getEnterY()); + enterAnim.setDuration(300); + enterAnim.start(); + } else if (impl.bkg.getY() > impl.getEnterY() + DialogX.touchSlideTriggerThreshold) { + impl.preDismiss(); + } else { + ObjectAnimator enterAnim = ObjectAnimator.ofFloat(impl.bkg, "y", impl.bkg.getY(), impl.getEnterY()); + enterAnim.setDuration(300); + enterAnim.start(); + } + } else { + if (impl.bkg.getY() < bkgOldY - DialogX.touchSlideTriggerThreshold) { + ObjectAnimator enterAnim = ObjectAnimator.ofFloat(impl.bkg, "y", impl.bkg.getY(), me.getDialogImpl().getEnterY()); + enterAnim.setDuration(300); + enterAnim.start(); + } else if (impl.bkg.getY() > bkgOldY + DialogX.touchSlideTriggerThreshold) { + impl.preDismiss(); + } else { + ObjectAnimator enterAnim = ObjectAnimator.ofFloat(impl.bkg, "y", impl.bkg.getY(), impl.getEnterY()); + enterAnim.setDuration(300); + enterAnim.start(); + } + } + if (impl.scrollView instanceof ScrollController) { + impl.scrollView.lockScroll(false); + } + break; + } + return false; + } + }); + } else { + View touchView = impl.boxCustom; + if (impl.scrollView != null) { + touchView = impl.bkg; + } + if (impl.scrollView instanceof ScrollController) { + ((ScrollController) impl.scrollView).lockScroll(false); + } + touchView.setOnTouchListener(null); + } + } + + /** + * 检查 MotionEvent 触摸点是否在 scrollView 相对于对话框移动布局 slideView 范围内 + * + * @param slideView 对话框移动布局 + * @param scrollView 内部滚动布局 + * @param event 触摸事件 + * @return 是否在范围内 + */ + private boolean touchInScrollView(View slideView, ScrollController scrollView, MotionEvent event) { + View scrollViewImpl = (View) scrollView; + RectF scrollViewLocation = new RectF(); + int[] scrollViewScreenLoc = new int[2]; + int[] rootViewScreenLoc = new int[2]; + scrollViewImpl.getLocationInWindow(scrollViewScreenLoc); + slideView.getLocationInWindow(rootViewScreenLoc); + + scrollViewLocation.left = scrollViewScreenLoc[0] - rootViewScreenLoc[0]; + scrollViewLocation.top = scrollViewScreenLoc[1] - rootViewScreenLoc[1]; + scrollViewLocation.right = scrollViewLocation.left + scrollViewImpl.getWidth(); + scrollViewLocation.bottom = scrollViewLocation.top + scrollViewImpl.getHeight(); + + return event.getX() >= scrollViewLocation.left && event.getX() <= scrollViewLocation.right && + event.getY() >= scrollViewLocation.top && event.getY() <= scrollViewLocation.bottom; + } + + private int dip2px(float dpValue) { + final float scale = Resources.getSystem().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/IOSMenuArrayAdapter.java b/DialogX/src/main/java/com/kongzue/dialogx/util/IOSMenuArrayAdapter.java new file mode 100644 index 0000000..1c38896 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/IOSMenuArrayAdapter.java @@ -0,0 +1,19 @@ +package com.kongzue.dialogx.util; + +import android.content.Context; +import android.widget.ArrayAdapter; + +import androidx.annotation.NonNull; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/10 19:56 + */ +public class IOSMenuArrayAdapter extends ArrayAdapter { + public IOSMenuArrayAdapter(@NonNull Context context, int resource) { + super(context, resource); + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/InputInfo.java b/DialogX/src/main/java/com/kongzue/dialogx/util/InputInfo.java new file mode 100644 index 0000000..68512ca --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/InputInfo.java @@ -0,0 +1,128 @@ +package com.kongzue.dialogx.util; + +import android.text.InputFilter; + +import androidx.annotation.ColorInt; + +import java.util.Arrays; + +/** + * Author: @Kongzue + * Github: https://github.com/kongzue/ + * Homepage: http://kongzue.com/ + * Mail: myzcxhh@live.cn + * CreateTime: 2018/11/8 21:41 + */ +public class InputInfo { + + private int MAX_LENGTH = -1; //最大长度,-1不生效 + private int inputType; //类型详见 android.text.InputType + private TextInfo textInfo; //默认字体样式 + private boolean multipleLines; //支持多行 + private boolean selectAllText; //默认选中所有文字(便于修改) + + private Integer cursorColor; //输入框光标颜色 + private Integer bottomLineColor;//输入框横线颜色 + + private InputFilter[] inputFilters; //输入过滤器 + + public int getMAX_LENGTH() { + return MAX_LENGTH; + } + + public InputInfo setMAX_LENGTH(int MAX_LENGTH) { + this.MAX_LENGTH = MAX_LENGTH; + return this; + } + + public int getInputType() { + return inputType; + } + + public InputInfo setInputType(int inputType) { + this.inputType = inputType; + return this; + } + + public TextInfo getTextInfo() { + return textInfo; + } + + public InputInfo setTextInfo(TextInfo textInfo) { + this.textInfo = textInfo; + return this; + } + + public boolean isMultipleLines() { + return multipleLines; + } + + public InputInfo setMultipleLines(boolean multipleLines) { + this.multipleLines = multipleLines; + return this; + } + + public boolean isSelectAllText() { + return selectAllText; + } + + public InputInfo setSelectAllText(boolean selectAllText) { + this.selectAllText = selectAllText; + return this; + } + + public Integer getCursorColor() { + return cursorColor; + } + + public InputInfo setCursorColor(@ColorInt int cursorColor) { + this.cursorColor = cursorColor; + return this; + } + + public InputInfo setThemeColor(@ColorInt int themeColor) { + this.cursorColor = themeColor; + this.bottomLineColor = themeColor; + return this; + } + + public Integer getBottomLineColor() { + return bottomLineColor; + } + + public InputInfo setBottomLineColor(int bottomLineColor) { + this.bottomLineColor = bottomLineColor; + return this; + } + + public InputFilter[] getInputFilters() { + return inputFilters; + } + + public InputInfo setInputFilters(InputFilter[] inputFilters) { + this.inputFilters = inputFilters; + return this; + } + + public InputInfo addInputFilter(InputFilter inputFilter) { + if (this.inputFilters == null) { + this.inputFilters = new InputFilter[]{inputFilter}; + } else { + this.inputFilters = Arrays.copyOf(this.inputFilters, this.inputFilters.length + 1); + this.inputFilters[this.inputFilters.length - 1] = inputFilter; + } + return this; + } + + public InputInfo removeInputFilter(InputFilter inputFilter) { + if (this.inputFilters!= null) { + for (int i = 0; i < this.inputFilters.length; i++) { + if (this.inputFilters[i] == inputFilter) { + this.inputFilters = Arrays.copyOf(this.inputFilters, this.inputFilters.length - 1); + return this; + } + } + } + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/ItemDivider.java b/DialogX/src/main/java/com/kongzue/dialogx/util/ItemDivider.java new file mode 100644 index 0000000..1b8c185 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/ItemDivider.java @@ -0,0 +1,96 @@ +package com.kongzue.dialogx.util; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.InsetDrawable; + +public class ItemDivider { + + private int left; //左边距(dp) + private int right; //右边距(dp) + private int width = 1; //分割线宽度(px) + private int[] color = {0xFFDFE1E5, 0xFF3A3A3A}; //颜色 + + public ItemDivider() { + } + + public ItemDivider(int left, int right, int width) { + this.left = left; + this.right = right; + this.width = width; + } + + public int getLeft() { + return left; + } + + public ItemDivider setLeft(int left) { + this.left = left; + return this; + } + + public int getRight() { + return right; + } + + public ItemDivider setRight(int right) { + this.right = right; + return this; + } + + public int getWidth() { + return width; + } + + public ItemDivider setWidth(int width) { + this.width = width; + return this; + } + + public int getColor(boolean light) { + return light ? color[0] : color[1]; + } + + public ItemDivider setColor(boolean light, int color) { + if (light) { + this.color[0] = color; + } else { + this.color[1] = color; + } + return this; + } + + public ItemDivider setColor(int color) { + this.color = new int[]{color, color}; + return this; + } + + @Override + public String toString() { + return "ItemDivider{" + + "left(dp)=" + left + + ", right(dp)=" + right + + ", width(px)=" + width + + ", color(light)=" + String.format("#%06X", (0xFFFFFF & color[0])) + + ", color(night)=" + String.format("#%06X", (0xFFFFFF & color[1])) + + '}'; + } + + public Drawable createDividerDrawable(Context c, boolean light) { + GradientDrawable dividerShape = new GradientDrawable(); + dividerShape.setShape(GradientDrawable.RECTANGLE); + dividerShape.setColor(getColor(light)); + + InsetDrawable insetDivider = new InsetDrawable( + dividerShape, + dip2px(c, left), 0, dip2px(c, right), 0 + ); + return insetDivider; + } + + private int dip2px(Context c, float dpValue) { + final float scale = c.getResources().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/MessageMenuArrayAdapter.java b/DialogX/src/main/java/com/kongzue/dialogx/util/MessageMenuArrayAdapter.java new file mode 100644 index 0000000..2b9754f --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/MessageMenuArrayAdapter.java @@ -0,0 +1,270 @@ +package com.kongzue.dialogx.util; + +import static com.kongzue.dialogx.interfaces.BaseDialog.isNull; +import static com.kongzue.dialogx.interfaces.BaseDialog.useTextInfo; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.os.Build; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.Space; +import android.widget.TextView; + +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.dialogs.MessageMenu; +import com.kongzue.dialogx.interfaces.MenuIconAdapter; +import com.kongzue.dialogx.interfaces.SELECT_MODE; + +import java.util.List; + +public class MessageMenuArrayAdapter extends BaseAdapter { + private MessageMenu messageMenu; + public List objects; + public Context context; + + public MessageMenuArrayAdapter(MessageMenu messageMenu, Context context, List objects) { + this.objects = objects; + this.context = context; + this.messageMenu = messageMenu; + } + + class ViewHolder { + ImageView imgDialogxMenuIcon; + ImageView imgDialogxMenuSelection; + TextView txtDialogxMenuText; + Space spaceDialogxRightPadding; + } + + @Override + public int getCount() { + return objects.size(); + } + + @Override + public CharSequence getItem(int position) { + return objects.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + TextInfo defaultMenuTextInfo; + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + MessageMenuArrayAdapter.ViewHolder viewHolder = null; + if (convertView == null) { + viewHolder = new MessageMenuArrayAdapter.ViewHolder(); + LayoutInflater mInflater = LayoutInflater.from(context); + + int resourceId = R.layout.item_dialogx_material_bottom_menu_normal_text; + if (messageMenu.getStyle().overrideBottomDialogRes() != null) { + resourceId = messageMenu.getStyle().overrideBottomDialogRes().overrideMenuItemLayout(messageMenu.isLightTheme(), position, getCount(), false); + if (resourceId == 0) { + resourceId = R.layout.item_dialogx_material_bottom_menu_normal_text; + } else { + if (!isNull(messageMenu.getTitle()) || !isNull(messageMenu.getMessage()) || messageMenu.getCustomView() != null) { + if (position == 0) { + resourceId = messageMenu.getStyle().overrideBottomDialogRes().overrideMenuItemLayout(messageMenu.isLightTheme(), position, getCount(), true); + } + } + } + } + convertView = mInflater.inflate(resourceId, null); + + viewHolder.imgDialogxMenuIcon = convertView.findViewById(R.id.img_dialogx_menu_icon); + viewHolder.imgDialogxMenuSelection = convertView.findViewById(R.id.img_dialogx_menu_selection); + viewHolder.txtDialogxMenuText = convertView.findViewById(R.id.txt_dialogx_menu_text); + viewHolder.spaceDialogxRightPadding = convertView.findViewById(R.id.space_dialogx_right_padding); + + convertView.setTag(viewHolder); + } else { + viewHolder = (MessageMenuArrayAdapter.ViewHolder) convertView.getTag(); + } + if (!messageMenu.isMenuItemEnable(position)) { + convertView.setAlpha(0.4f); + } else { + convertView.setAlpha(1f); + } + if (messageMenu.getSelectMode() == SELECT_MODE.SINGLE) { + if (viewHolder.imgDialogxMenuSelection != null) { + if (messageMenu.getSelection() == position) { + viewHolder.imgDialogxMenuSelection.setVisibility(View.VISIBLE); + int overrideSelectionImageResId = messageMenu.getStyle().overrideBottomDialogRes().overrideSelectionImage(messageMenu.isLightTheme(), true); + if (overrideSelectionImageResId != 0) { + viewHolder.imgDialogxMenuSelection.setImageResource(overrideSelectionImageResId); + } + } else { + int overrideSelectionImageResId = messageMenu.getStyle().overrideBottomDialogRes().overrideSelectionImage(messageMenu.isLightTheme(), false); + if (overrideSelectionImageResId != 0) { + viewHolder.imgDialogxMenuSelection.setVisibility(View.VISIBLE); + viewHolder.imgDialogxMenuSelection.setImageResource(overrideSelectionImageResId); + } else { + viewHolder.imgDialogxMenuSelection.setVisibility(View.INVISIBLE); + } + } + } + } else if (messageMenu.getSelectMode() == SELECT_MODE.MULTIPLE) { + if (viewHolder.imgDialogxMenuSelection != null) { + if (messageMenu.getSelectionList().contains(position)) { + viewHolder.imgDialogxMenuSelection.setVisibility(View.VISIBLE); + int overrideSelectionImageResId = messageMenu.getStyle().overrideBottomDialogRes().overrideMultiSelectionImage(messageMenu.isLightTheme(), true); + if (overrideSelectionImageResId != 0) { + viewHolder.imgDialogxMenuSelection.setImageResource(overrideSelectionImageResId); + } + } else { + int overrideSelectionImageResId = messageMenu.getStyle().overrideBottomDialogRes().overrideMultiSelectionImage(messageMenu.isLightTheme(), false); + if (overrideSelectionImageResId != 0) { + viewHolder.imgDialogxMenuSelection.setVisibility(View.VISIBLE); + viewHolder.imgDialogxMenuSelection.setImageResource(overrideSelectionImageResId); + } else { + viewHolder.imgDialogxMenuSelection.setVisibility(View.INVISIBLE); + } + } + } + } else { + viewHolder.imgDialogxMenuSelection.setVisibility(View.GONE); + } + int overrideSelectionBackgroundColorRes = 0; + if (messageMenu.getStyle().overrideBottomDialogRes() != null) { + overrideSelectionBackgroundColorRes = messageMenu.getStyle().overrideBottomDialogRes().overrideSelectionMenuBackgroundColor(messageMenu.isLightTheme()); + } + if (messageMenu.getSelection() == position) { + //选中的背景变色 + if (overrideSelectionBackgroundColorRes != 0) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + convertView.setBackgroundTintList(ColorStateList.valueOf(context.getResources().getColor(overrideSelectionBackgroundColorRes))); + } + } + } + CharSequence text = objects.get(position); + + int textColor = messageMenu.isLightTheme() ? R.color.black90 : R.color.white90; + if (messageMenu.getStyle().overrideBottomDialogRes() != null) { + if (messageMenu.getStyle().overrideBottomDialogRes().overrideMenuTextColor(messageMenu.isLightTheme()) != 0) { + textColor = messageMenu.getStyle().overrideBottomDialogRes().overrideMenuTextColor(messageMenu.isLightTheme()); + } + } + + if (null != text) { + if (defaultMenuTextInfo == null) { + defaultMenuTextInfo = new TextInfo().setShowEllipsis(viewHolder.txtDialogxMenuText.getEllipsize() == TextUtils.TruncateAt.END).setFontColor(viewHolder.txtDialogxMenuText.getTextColors().getDefaultColor()).setBold(viewHolder.txtDialogxMenuText.getPaint().isFakeBoldText()).setFontSize(px2dip(viewHolder.txtDialogxMenuText.getTextSize())).setGravity(viewHolder.txtDialogxMenuText.getGravity()).setMaxLines(viewHolder.txtDialogxMenuText.getMaxLines()); + } + viewHolder.txtDialogxMenuText.setText(text); + viewHolder.txtDialogxMenuText.setTextColor(context.getResources().getColor(textColor)); + if (messageMenu.getMenuItemTextInfoInterceptor() != null) { + TextInfo textInfo = messageMenu.getMenuItemTextInfoInterceptor().menuItemTextInfo(messageMenu, position, text.toString()); + if (textInfo != null) { + useTextInfo(viewHolder.txtDialogxMenuText, textInfo); + } else { + if (messageMenu.getMenuTextInfo() != null) { + useTextInfo(viewHolder.txtDialogxMenuText, messageMenu.getMenuTextInfo()); + } else { + useTextInfo(viewHolder.txtDialogxMenuText, defaultMenuTextInfo); + } + } + } else { + if (messageMenu.getMenuTextInfo() != null) { + useTextInfo(viewHolder.txtDialogxMenuText, messageMenu.getMenuTextInfo()); + } + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (viewHolder.imgDialogxMenuSelection != null) { + if (messageMenu.getStyle().overrideBottomDialogRes() != null && messageMenu.getStyle().overrideBottomDialogRes().selectionImageTint(messageMenu.isLightTheme())) { + viewHolder.imgDialogxMenuSelection.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } else { + viewHolder.imgDialogxMenuSelection.setImageTintList(null); + } + } + } + + if (messageMenu.getOnIconChangeCallBack() != null) { + if (messageMenu.getOnIconChangeCallBack() instanceof MenuIconAdapter) { + boolean result = ((MenuIconAdapter) messageMenu.getOnIconChangeCallBack()).applyIcon(messageMenu, position, text.toString(), viewHolder.imgDialogxMenuIcon); + boolean autoTintIconInLightOrDarkMode = messageMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode() == null ? messageMenu.isAutoTintIconInLightOrDarkMode() : messageMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode(); + + viewHolder.imgDialogxMenuIcon.setVisibility(result ? View.VISIBLE : View.GONE); + if (result) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (autoTintIconInLightOrDarkMode) { + viewHolder.imgDialogxMenuIcon.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } + } + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.VISIBLE); + } + } else { + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } else { + int resId = messageMenu.getOnIconChangeCallBack().getIcon(messageMenu, position, text.toString()); + boolean autoTintIconInLightOrDarkMode = messageMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode() == null ? messageMenu.isAutoTintIconInLightOrDarkMode() : messageMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode(); + + if (resId != 0) { + viewHolder.imgDialogxMenuIcon.setVisibility(View.VISIBLE); + viewHolder.imgDialogxMenuIcon.setImageResource(resId); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.VISIBLE); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (autoTintIconInLightOrDarkMode) { + viewHolder.imgDialogxMenuIcon.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } + } + } else { + viewHolder.imgDialogxMenuIcon.setVisibility(View.GONE); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } + } else { + if (messageMenu.getIconResIds() != null) { + int resId = messageMenu.getIconResIds(position); + boolean autoTintIconInLightOrDarkMode = messageMenu.isAutoTintIconInLightOrDarkMode(); + + if (resId != 0) { + viewHolder.imgDialogxMenuIcon.setVisibility(View.VISIBLE); + viewHolder.imgDialogxMenuIcon.setImageResource(resId); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.VISIBLE); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (autoTintIconInLightOrDarkMode) { + viewHolder.imgDialogxMenuIcon.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } + } + } else { + viewHolder.imgDialogxMenuIcon.setVisibility(View.GONE); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } else { + viewHolder.imgDialogxMenuIcon.setVisibility(View.GONE); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } + } + if (messageMenu.getMenuMenuItemLayoutRefreshCallback() != null) { + messageMenu.getMenuMenuItemLayoutRefreshCallback().getView(messageMenu, position, convertView, parent); + } + return convertView; + } + + private int px2dip(float pxValue) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (pxValue / scale + 0.5f); + } +} \ No newline at end of file diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/ObjectRunnable.java b/DialogX/src/main/java/com/kongzue/dialogx/util/ObjectRunnable.java new file mode 100644 index 0000000..6e569f4 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/ObjectRunnable.java @@ -0,0 +1,12 @@ +package com.kongzue.dialogx.util; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2022/8/26 17:10 + */ +public abstract class ObjectRunnable { + public abstract void run(D d); +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/PopMenuArrayAdapter.java b/DialogX/src/main/java/com/kongzue/dialogx/util/PopMenuArrayAdapter.java new file mode 100644 index 0000000..ebb2b3d --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/PopMenuArrayAdapter.java @@ -0,0 +1,218 @@ +package com.kongzue.dialogx.util; + +import static com.kongzue.dialogx.interfaces.BaseDialog.useTextInfo; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.os.Build; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.Space; +import android.widget.TextView; + +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.dialogs.PopMenu; +import com.kongzue.dialogx.interfaces.MenuIconAdapter; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/8/18 15:25 + */ +public class PopMenuArrayAdapter extends BaseAdapter { + + public List menuList; + public Context context; + private PopMenu popMenu; + LayoutInflater mInflater; + + public PopMenuArrayAdapter(PopMenu popMenu, Context context, List menuList) { + this.popMenu = popMenu; + this.menuList = menuList; + this.context = context; + mInflater = LayoutInflater.from(context); + } + + public List getMenuList() { + return menuList; + } + + @Override + public int getCount() { + if (menuList == null) { + menuList = new ArrayList<>(); + } + return menuList.size(); + } + + @Override + public Object getItem(int position) { + return menuList.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + ViewHolder viewHolder; + if (convertView == null) { + viewHolder = new ViewHolder(); + int resourceId = R.layout.item_dialogx_material_context_menu_normal_text; + if (popMenu.getStyle().popMenuSettings() != null) { + int customItemLayout = popMenu.getStyle().popMenuSettings().overrideMenuItemLayoutRes(popMenu.isLightTheme()); + if (customItemLayout != 0) { + resourceId = customItemLayout; + } + } + + convertView = mInflater.inflate(resourceId, null); + + viewHolder.boxItem = convertView.findViewById(R.id.box_item); + viewHolder.imgDialogxMenuIcon = convertView.findViewById(R.id.img_dialogx_menu_icon); + viewHolder.txtDialogxMenuText = convertView.findViewById(R.id.txt_dialogx_menu_text); + viewHolder.spaceDialogxRightPadding = convertView.findViewById(R.id.space_dialogx_right_padding); + + convertView.setTag(viewHolder); + } else { + viewHolder = (ViewHolder) convertView.getTag(); + } + if (!popMenu.isMenuItemEnable(position)) { + convertView.setAlpha(0.4f); + } else { + convertView.setAlpha(1f); + } + int customBackgroundRes = popMenu.getStyle().popMenuSettings() == null ? 0 : popMenu.getStyle().popMenuSettings().overrideMenuItemBackgroundRes(popMenu.isLightTheme(), position, getCount(), false); + if (customBackgroundRes != 0) { + convertView.setBackgroundResource(customBackgroundRes); + } + if (viewHolder.boxItem != null) { + if (popMenu.getPressedIndex() == position) { + viewHolder.boxItem.setBackgroundResource(popMenu.isLightTheme() ? R.color.black5 : R.color.white5); + } else { + viewHolder.boxItem.setBackgroundResource(R.color.empty); + } + } + viewHolder.imgDialogxMenuIcon.setVisibility(View.GONE); + viewHolder.txtDialogxMenuText.setText(menuList.get(position)); + if (popMenu.getStyle().popMenuSettings() != null && popMenu.getStyle().popMenuSettings().paddingVertical() != 0) { + if (position == 0) { + convertView.setPadding(0, popMenu.getStyle().popMenuSettings().paddingVertical(), 0, 0); + } else if (position == getCount() - 1) { + convertView.setPadding(0, 0, 0, popMenu.getStyle().popMenuSettings().paddingVertical()); + } else { + convertView.setPadding(0, 0, 0, 0); + } + } + + if (popMenu.getMenuTextInfo() != null) { + useTextInfo(viewHolder.txtDialogxMenuText, popMenu.getMenuTextInfo()); + } + + int textColor = popMenu.isLightTheme() ? R.color.black90 : R.color.white90; + viewHolder.txtDialogxMenuText.setTextColor(context.getResources().getColor(textColor)); + + if (popMenu.getOnIconChangeCallBack() != null) { + if (popMenu.getOnIconChangeCallBack() instanceof MenuIconAdapter) { + boolean result = ((MenuIconAdapter) popMenu.getOnIconChangeCallBack()).applyIcon(popMenu, position, menuList.get(position).toString(), viewHolder.imgDialogxMenuIcon); + boolean autoTintIconInLightOrDarkMode = popMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode() == null ? popMenu.isAutoTintIconInLightOrDarkMode() : popMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode(); + + viewHolder.imgDialogxMenuIcon.setVisibility(result ? View.VISIBLE : View.GONE); + if (result) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (autoTintIconInLightOrDarkMode) { + viewHolder.imgDialogxMenuIcon.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } + } + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.VISIBLE); + } + } else { + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + }else{ + int resId = popMenu.getOnIconChangeCallBack().getIcon(popMenu, position, menuList.get(position).toString()); + boolean autoTintIconInLightOrDarkMode = popMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode() == null ? popMenu.isAutoTintIconInLightOrDarkMode() : popMenu.getOnIconChangeCallBack().isAutoTintIconInLightOrDarkMode(); + + if (resId != 0) { + viewHolder.imgDialogxMenuIcon.setVisibility(View.VISIBLE); + viewHolder.imgDialogxMenuIcon.setImageResource(resId); + if (isHaveProperties(viewHolder.txtDialogxMenuText.getGravity(), Gravity.CENTER) || isHaveProperties(viewHolder.txtDialogxMenuText.getGravity(), Gravity.CENTER_HORIZONTAL)) { + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.VISIBLE); + } + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (autoTintIconInLightOrDarkMode) { + viewHolder.imgDialogxMenuIcon.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } + } + } else { + viewHolder.imgDialogxMenuIcon.setVisibility(View.GONE); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } + } else { + if (popMenu.getIconResIds() != null) { + int resId = popMenu.getIconResIds(position); + boolean autoTintIconInLightOrDarkMode = popMenu.isAutoTintIconInLightOrDarkMode(); + + if (resId != 0) { + viewHolder.imgDialogxMenuIcon.setVisibility(View.VISIBLE); + viewHolder.imgDialogxMenuIcon.setImageResource(resId); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.VISIBLE); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (autoTintIconInLightOrDarkMode) { + viewHolder.imgDialogxMenuIcon.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(textColor))); + } + } + } else { + viewHolder.imgDialogxMenuIcon.setVisibility(View.GONE); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } else { + viewHolder.imgDialogxMenuIcon.setVisibility(View.GONE); + if (viewHolder.spaceDialogxRightPadding != null) { + viewHolder.spaceDialogxRightPadding.setVisibility(View.GONE); + } + } + } + + if (popMenu.getMenuMenuItemLayoutRefreshCallback() != null) { + popMenu.getMenuMenuItemLayoutRefreshCallback().getView(popMenu, position, convertView, parent); + } + + return convertView; + } + + private boolean isHaveProperties(int gravity, int property) { + return (gravity & property) == property; + } + + class ViewHolder { + LinearLayout boxItem; + ImageView imgDialogxMenuIcon; + TextView txtDialogxMenuText; + Space spaceDialogxRightPadding; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/PopValueAnimator.java b/DialogX/src/main/java/com/kongzue/dialogx/util/PopValueAnimator.java new file mode 100644 index 0000000..e84ac32 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/PopValueAnimator.java @@ -0,0 +1,39 @@ +package com.kongzue.dialogx.util; + +import android.animation.ValueAnimator; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2022/7/19 14:31 + */ +public class PopValueAnimator extends ValueAnimator { + + float startValue; + float endValue; + + public static PopValueAnimator ofFloat(float... values) { + PopValueAnimator anim = new PopValueAnimator(); + anim.setFloatValues(values); + return anim; + } + + @Override + public void setFloatValues(float... values) { + if (values.length > 1) { + startValue = values[0]; + endValue = values[values.length - 1]; + } + super.setFloatValues(values); + } + + public float getStartValue() { + return startValue; + } + + public float getEndValue() { + return endValue; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/TextInfo.java b/DialogX/src/main/java/com/kongzue/dialogx/util/TextInfo.java new file mode 100644 index 0000000..0e7d5be --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/TextInfo.java @@ -0,0 +1,130 @@ +package com.kongzue.dialogx.util; + +import android.graphics.Typeface; +import android.util.TypedValue; + +import androidx.annotation.ColorInt; + +/** + * Author: @Kongzue + * Github: https://github.com/kongzue/ + * Homepage: http://kongzue.com/ + * Mail: myzcxhh@live.cn + * CreateTime: 2018/11/10 22:01 + */ +public class TextInfo { + + private int fontSize = -1; //字号大小,值为-1时使用默认样式,单位:dp + private FONT_SIZE_UNIT fontSizeUnit = FONT_SIZE_UNIT.DP; //字号单位 + private int gravity = -1; //对齐方式,值为-1时使用默认样式,取值可使用Gravity.CENTER等对齐方式 + private int fontColor = 1; //文字颜色,值为1时使用默认样式,取值可以用Color.rgb(r,g,b)等方式获取 + private boolean bold = false; //是否粗体 + private int maxLines = -1; //最大行数 + private boolean showEllipsis = false; //显示省略号 + private Typeface typeface; //指定字体样式 + + public enum FONT_SIZE_UNIT { + DP, + PX, + SP + } + + public int getFontSize() { + return fontSize; + } + + public int getFontSizeComplexUnit() { + if (fontSizeUnit == null) { + return TypedValue.COMPLEX_UNIT_DIP; + } + switch (fontSizeUnit) { + case PX: + return TypedValue.COMPLEX_UNIT_PX; + case SP: + return TypedValue.COMPLEX_UNIT_SP; + default: + return TypedValue.COMPLEX_UNIT_DIP; + } + } + + public TextInfo setFontSize(int fontSize) { + this.fontSize = fontSize; + return this; + } + + public int getGravity() { + return gravity; + } + + public TextInfo setGravity(int gravity) { + this.gravity = gravity; + return this; + } + + public int getFontColor() { + return fontColor; + } + + public TextInfo setFontColor(@ColorInt int fontColor) { + this.fontColor = fontColor; + return this; + } + + public boolean isBold() { + return bold; + } + + public TextInfo setBold(boolean bold) { + this.bold = bold; + return this; + } + + public int getMaxLines() { + return maxLines; + } + + public TextInfo setMaxLines(int maxLines) { + this.maxLines = maxLines; + return this; + } + + public boolean isShowEllipsis() { + return showEllipsis; + } + + public TextInfo setShowEllipsis(boolean showEllipsis) { + this.showEllipsis = showEllipsis; + return this; + } + + public FONT_SIZE_UNIT getFontSizeUnit() { + return fontSizeUnit; + } + + public TextInfo setFontSizeUnit(FONT_SIZE_UNIT fontSizeUnit) { + this.fontSizeUnit = fontSizeUnit; + return this; + } + + public Typeface getTypeface() { + return typeface; + } + + public TextInfo setTypeface(Typeface typeface) { + this.typeface = typeface; + return this; + } + + @Override + public String toString() { + return "TextInfo{" + + "fontSize=" + fontSize + + ", gravity=" + gravity + + ", fontColor=" + fontColor + + ", bold=" + bold + + ", maxLines=" + maxLines + + ", showEllipsis=" + showEllipsis + + ", typeface=" + typeface + + '}'; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/WindowUtil.java b/DialogX/src/main/java/com/kongzue/dialogx/util/WindowUtil.java new file mode 100644 index 0000000..ed4f53d --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/WindowUtil.java @@ -0,0 +1,136 @@ +package com.kongzue.dialogx.util; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.graphics.PixelFormat; +import android.os.Build; +import android.provider.Settings; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.FrameLayout; +import android.widget.Toast; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.dialogs.PopTip; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.NoTouchInterface; + +import static android.view.WindowManager.LayoutParams.*; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/4/29 16:02 + */ +public class WindowUtil { + + public abstract static class WindowSettings { + + //自定义window参数 + public abstract WindowManager.LayoutParams overrideWindowLayoutParamsInterface(Context context, View dialogView, WindowManager.LayoutParams originWindowLayoutParams); + + //自定义根布局 + public ViewGroup overrideRootView(Context context){return null;}; + } + + //自定义window设置 + public static WindowSettings windowSettings; + + public static void show(Activity activity, View dialogView, boolean touchEnable) { + try { + if (activity.getWindow().getDecorView().isAttachedToWindow()) { + showNow(activity, dialogView, touchEnable); + } else { + activity.getWindow().getDecorView().post(new Runnable() { + @Override + public void run() { + showNow(activity, dialogView, touchEnable); + } + }); + } + } catch (Exception e) { + if (activity != null && !activity.isDestroyed()) { + showNow(activity, dialogView, touchEnable); + } + } + } + + private static void showNow(Activity activity, View dialogView, boolean touchEnable) { + if (DialogX.globalHoverWindow && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(activity)) { + Toast.makeText(activity, "使用 DialogX.globalHoverWindow 必须开启悬浮窗权限", Toast.LENGTH_LONG).show(); + Intent intent = new Intent(); + intent.setAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); + activity.startActivity(intent); + return; + } + ViewGroup customRootView = null; + if (windowSettings != null) { + customRootView = windowSettings.overrideRootView(activity); + } + ViewGroup rootLayout = customRootView == null ? new FrameLayout(activity) : customRootView; + if (dialogView.getParent() != null) { + ((ViewGroup) dialogView.getParent()).removeView(dialogView); + } + rootLayout.addView(dialogView, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); + WindowManager manager = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE); + WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); + layoutParams.gravity = Gravity.CENTER_VERTICAL; + layoutParams.format = PixelFormat.TRANSPARENT; + if (DialogX.globalHoverWindow) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + layoutParams.type = TYPE_APPLICATION_OVERLAY; + } else { + layoutParams.type = TYPE_PHONE; + } + } else { + layoutParams.type = TYPE_APPLICATION_ATTACHED_DIALOG; + } + layoutParams.flags = FLAG_FULLSCREEN | + FLAG_TRANSLUCENT_STATUS | + FLAG_TRANSLUCENT_NAVIGATION | + FLAG_LAYOUT_IN_SCREEN + ; + layoutParams.softInputMode = SOFT_INPUT_ADJUST_RESIZE; + if (!touchEnable) { + dialogView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + for (int i = BaseDialog.getRunningDialogList().size() - 1; i >= 0; i--) { + BaseDialog baseDialog = BaseDialog.getRunningDialogList().get(i); + if (!(baseDialog instanceof NoTouchInterface) && baseDialog.getOwnActivity() == activity) { + if (baseDialog.getDialogView() == null) { + return false; + } + return baseDialog.dispatchTouchEvent(event); + } + } + return activity.dispatchTouchEvent(event); + } + }); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + } + if (windowSettings != null) { + WindowManager.LayoutParams layoutParamsTemp = windowSettings.overrideWindowLayoutParamsInterface(activity, dialogView, layoutParams); + if (layoutParamsTemp != null) { + layoutParams = layoutParamsTemp; + } + } + manager.addView(rootLayout, layoutParams); + } + + public static void dismiss(View dialogView) { + BaseDialog baseDialog = (BaseDialog) dialogView.getTag(); + if (baseDialog != null && baseDialog.getOwnActivity() != null) { + WindowManager manager = (WindowManager) baseDialog.getOwnActivity().getSystemService(Context.WINDOW_SERVICE); + manager.removeViewImmediate((View) dialogView.getParent()); + } + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/ActivityScreenShotImageView.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/ActivityScreenShotImageView.java new file mode 100644 index 0000000..c1368cb --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/ActivityScreenShotImageView.java @@ -0,0 +1,218 @@ +package com.kongzue.dialogx.util.views; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Path; +import android.graphics.Rect; +import android.os.Build; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.impl.ActivityLifecycleImpl; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.util.DialogXFloatingWindowActivity; + +import java.lang.ref.WeakReference; +import java.util.Objects; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2019/11/17 23:53 + */ +@SuppressLint("AppCompatCustomView") +public class ActivityScreenShotImageView extends ImageView { + + public boolean hideActivityContentView; + float width, height, mRadius; + public static boolean useHardwareRenderingMode = true; + + public ActivityScreenShotImageView(Context context) { + super(context); + init(null); + } + + public ActivityScreenShotImageView(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs); + } + + public ActivityScreenShotImageView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(attrs); + } + + private void init(AttributeSet attrs) { + requestLayoutType(); + } + + private void requestLayoutType() { + setLayerType(useHardwareRenderingMode ? LAYER_TYPE_HARDWARE : LAYER_TYPE_SOFTWARE, null); + } + + public void setRadius(float mRadius) { + this.mRadius = mRadius; + invalidate(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + if (width != getWidth() || height != getHeight()) { + refreshImage(); + } + width = getWidth(); + height = getHeight(); + } + + boolean readyDraw = false; + + @Override + protected void onDraw(Canvas canvas) { + if (!readyDraw) { + super.onDraw(canvas); + } + if (width >= mRadius && height > mRadius) { + if (isScreenshotSuccess) { + canvas.drawColor(Color.BLACK); + } + Path path = new Path(); + path.moveTo(mRadius, 0); + path.lineTo(width - mRadius, 0); + path.quadTo(width, 0, width, mRadius); + path.lineTo(width, height - mRadius); + path.quadTo(width, height, width - mRadius, height); + path.lineTo(mRadius, height); + path.quadTo(0, height, 0, height - mRadius); + path.lineTo(0, mRadius); + path.quadTo(0, 0, mRadius, 0); + + canvas.clipPath(path); + } + canvas.drawColor(Color.WHITE); + super.onDraw(canvas); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (!isScreenshotSuccess) refreshImage(); + } + + private int screenWidth, screenHeight; + + private void refreshImage() { + if (!isAttachedToWindow()) { + return; + } + if (screenWidth != getMeasuredWidth() || screenHeight != getMeasuredHeight()) { + screenWidth = getMeasuredWidth(); + screenHeight = getMeasuredHeight(); + doScreenshotActivityAndZoom(); + } + } + + private void doScreenshotActivityAndZoom() { + ViewGroup decorView = getDecorView(); + if (decorView == null) return; + drawViewImage(decorView); + setVisibility(VISIBLE); + inited = true; + } + + private ViewGroup getDecorView() { + if (dialog != null) { + Activity ownActivity = dialog.getOwnActivity(); + return (ViewGroup) ownActivity.getWindow().getDecorView(); + } + Activity topActivity = ActivityLifecycleImpl.getTopActivity(); + if (topActivity != null) { + if (topActivity instanceof DialogXFloatingWindowActivity) { + return (ViewGroup) ((DialogXFloatingWindowActivity) topActivity).getFromActivity().getWindow().getDecorView(); + } + return (ViewGroup) topActivity.getWindow().getDecorView(); + } + return null; + } + + private boolean inited = false; + private boolean isScreenshotSuccess; + private WeakReference contentView; + public static boolean hideContentView = false; + + private void drawViewImage(View view) { + if (view.getWidth() == 0 || view.getHeight() == 0) return; + dialog.getDialogView().setVisibility(GONE); + setContentViewVisibility(true); + if (view.getWidth() + view.getHeight() == 0) { + view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); + view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); + } + int viewWidth = view.getWidth(); + int viewHeight = view.getHeight(); + Bitmap bitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + try { + view.draw(canvas); + } catch (Exception e) { + if (DialogX.DEBUGMODE) e.printStackTrace(); + if (useHardwareRenderingMode) { + useHardwareRenderingMode = false; + requestLayoutType(); + drawViewImage(view); + } + } + setImageBitmap(Bitmap.createBitmap(bitmap, 0, 0, view.getWidth(), view.getHeight())); + isScreenshotSuccess = true; + setContentViewVisibility(false); + dialog.getDialogView().setVisibility(VISIBLE); + dialog.getDialogView().requestFocus(); + } + + protected void setContentViewVisibility(boolean show) { + if (hideContentView || hideActivityContentView) { + if (show) { + if (contentView != null && contentView.get() != null) { + contentView.get().setVisibility(VISIBLE); + } + } else { + View userContentView = Objects.requireNonNull(getDecorView()).getChildAt(0); + if (userContentView != null) { + userContentView.setVisibility(GONE); + contentView = new WeakReference<>(userContentView); + } + } + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + setContentViewVisibility(true); + if (contentView != null) { + contentView.clear(); + } + } + + public void setScale(float scale) { + setScaleX(scale); + setScaleY(scale); + readyDraw = true; + } + + BaseDialog dialog; + + public void bindDialog(BaseDialog dialog) { + this.dialog = dialog; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/BlurView.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/BlurView.java new file mode 100644 index 0000000..31c86f4 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/BlurView.java @@ -0,0 +1,539 @@ +package com.kongzue.dialogx.util.views; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Outline; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Shader; +import android.os.Build; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.ViewTreeObserver; + +import androidx.annotation.ColorInt; +import androidx.annotation.Nullable; +import androidx.renderscript.Allocation; +import androidx.renderscript.Element; +import androidx.renderscript.RenderScript; +import androidx.renderscript.ScriptIntrinsicBlur; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; + +@Deprecated +public class BlurView extends View { + + private float mDownsampleFactor = 4; + private int mOverlayColor = Color.WHITE; + private float mBlurRadius = 35; + private boolean overrideOverlayColor = false; + private float mRadius = 0; + private boolean mDirty; + private Bitmap mBitmapToBlur, mBlurredBitmap; + private Canvas mBlurringCanvas; + private RenderScript mRenderScript; + private ScriptIntrinsicBlur mBlurScript; + private Allocation mBlurInput, mBlurOutput; + private boolean mIsRendering; + private final Rect mRectSrc = new Rect(), mRectDst = new Rect(); + private View mDecorView; + private boolean mDifferentRoot; + private static int RENDERING_COUNT; + + private Paint mPaint; + private RectF mRectF; + + public BlurView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public BlurView(Context context) { + super(context); + + init(context, null); + } + + public BlurView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + init(context, attrs); + } + + private boolean isInit = false; + + Paint cutPaint; + Paint overlayPaint; + + private void init(Context context, AttributeSet attrs) { + if (!isInit && context != null) { + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RealtimeBlurView); + mBlurRadius = a.getDimension( + R.styleable.RealtimeBlurView_realtimeBlurRadius, + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 35, context.getResources().getDisplayMetrics()) + ); + mDownsampleFactor = a.getFloat(R.styleable.RealtimeBlurView_realtimeDownsampleFactor, 4); + mOverlayColor = a.getColor(R.styleable.RealtimeBlurView_realtimeOverlayColor, 0x00ffffff); + + //ready rounded corner + mPaint = new Paint(); + mPaint.setAntiAlias(true); + mRectF = new RectF(); + + cutPaint = new Paint(); + cutPaint.setAntiAlias(true); + cutPaint.setColor(mOverlayColor); + + overlayPaint = new Paint(); + overlayPaint.setAntiAlias(true); + + mRadius = a.getDimension(R.styleable.RealtimeBlurView_realtimeRadius, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15, context.getResources().getDisplayMetrics())); + a.recycle(); + + isInit = true; + + if (!isCompatMode()) { + setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mRadius); + } + }); + setClipToOutline(true); + } + } + } + + private boolean isCompatMode() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP; + } + + public void setBlurRadius(float radius) { + if (mBlurRadius != radius) { + mBlurRadius = radius; + mDirty = true; + invalidate(); + } + } + + public void setRadiusPx(float r) { + if (mRadius != r) { + mRadius = r; + mDirty = true; + invalidate(); + } + } + + public void setDownsampleFactor(float factor) { + if (factor <= 0) { + throw new IllegalArgumentException("Downsample factor must be greater than 0."); + } + + if (mDownsampleFactor != factor) { + mDownsampleFactor = factor; + mDirty = true; // may also change blur radius + releaseBitmap(); + invalidate(); + } + } + + public void setOverlayColor(int color) { + if (mOverlayColor != color) { + mOverlayColor = color; + invalidate(); + } + } + + private void releaseBitmap() { + if (mBlurInput != null) { + mBlurInput.destroy(); + mBlurInput = null; + } + if (mBlurOutput != null) { + mBlurOutput.destroy(); + mBlurOutput = null; + } + if (mBitmapToBlur != null) { + mBitmapToBlur.recycle(); + mBitmapToBlur = null; + } + if (mBlurredBitmap != null) { + mBlurredBitmap.recycle(); + mBlurredBitmap = null; + } + } + + private void releaseScript() { + if (mRenderScript != null) { + mRenderScript.destroy(); + mRenderScript = null; + } + if (mBlurScript != null) { + mBlurScript.destroy(); + mBlurScript = null; + } + } + + protected void release() { + releaseBitmap(); + releaseScript(); + } + + protected boolean prepare() { + if (mBlurRadius == 0) { + release(); + return false; + } + + float downsampleFactor = mDownsampleFactor; + + if (mDirty || mRenderScript == null) { + if (supportRenderScript && useBlur) { + if (mRenderScript == null) { + try { + mRenderScript = RenderScript.create(getContext()); + mBlurScript = ScriptIntrinsicBlur.create(mRenderScript, Element.U8_4(mRenderScript)); + + } catch (Exception e) { + supportRenderScript = false; + if (isDebug()) { + e.printStackTrace(); + } + } + } + + mDirty = false; + float radius = mBlurRadius / downsampleFactor; + if (radius > 25) { + downsampleFactor = downsampleFactor * radius / 25; + radius = 25; + } + if (mBlurScript != null) mBlurScript.setRadius(radius); + } + } + + final int width = getWidth(); + final int height = getHeight(); + + int scaledWidth = Math.max(1, (int) (width / downsampleFactor)); + int scaledHeight = Math.max(1, (int) (height / downsampleFactor)); + + if (mBlurringCanvas == null || mBlurredBitmap == null || mBlurredBitmap.getWidth() != scaledWidth || mBlurredBitmap.getHeight() != scaledHeight) { + releaseBitmap(); + + boolean r = false; + try { + mBitmapToBlur = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888); + if (mBitmapToBlur == null) { + return false; + } + mBlurringCanvas = new Canvas(mBitmapToBlur); + + if (!supportRenderScript || !useBlur) { + return true; + } + + mBlurInput = Allocation.createFromBitmap(mRenderScript, mBitmapToBlur, + Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT + ); + mBlurOutput = Allocation.createTyped(mRenderScript, mBlurInput.getType()); + + mBlurredBitmap = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888); + if (mBlurredBitmap == null) { + return false; + } + + r = true; + } catch (Exception e) { + if (isDebug()) e.printStackTrace(); + } finally { + if (!r) { + releaseBitmap(); + return false; + } + } + } + return true; + } + + protected void blur(Bitmap bitmapToBlur, Bitmap blurredBitmap) { + mBlurInput.copyFrom(bitmapToBlur); + mBlurScript.setInput(mBlurInput); + mBlurScript.forEach(mBlurOutput); + mBlurOutput.copyTo(blurredBitmap); + } + + private final ViewTreeObserver.OnPreDrawListener preDrawListener = new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + final int[] locations = new int[2]; + Bitmap oldBmp = mBlurredBitmap; + View decor = mDecorView; + if (decor != null && isShown() && prepare()) { + boolean redrawBitmap = mBlurredBitmap != oldBmp; + decor.getLocationInWindow(locations); + int x = -locations[0]; + int y = -locations[1]; + + getLocationInWindow(locations); + x += locations[0]; + y += locations[1]; + + mBitmapToBlur.eraseColor(mOverlayColor & 0xffffff); + + int rc = mBlurringCanvas.save(); + mIsRendering = true; + RENDERING_COUNT++; + try { + mBlurringCanvas.scale(1.f * mBitmapToBlur.getWidth() / getWidth(), 1.f * mBitmapToBlur.getHeight() / getHeight()); + mBlurringCanvas.translate(-x, -y); + if (decor.getBackground() != null) { + decor.getBackground().draw(mBlurringCanvas); + } + decor.draw(mBlurringCanvas); + } catch (Exception e) { + if (isDebug()) e.printStackTrace(); + } finally { + mIsRendering = false; + RENDERING_COUNT--; + mBlurringCanvas.restoreToCount(rc); + } + + blur(mBitmapToBlur, mBlurredBitmap); + + if (redrawBitmap || mDifferentRoot) { + invalidate(); + } + } + + return true; + } + }; + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + Activity activity; + log(getContext()); + if (getContext() instanceof Activity) { + activity = (Activity) getContext(); + } else { + activity = BaseDialog.getTopActivity(); + } + ViewGroup decorView = ((ViewGroup) activity.getWindow().getDecorView()); + if (decorView.getChildCount() >= 1) { + mDecorView = decorView.getChildAt(0); + } + if (mDecorView != null) { + log("mDecorView is ok."); + mDecorView.getViewTreeObserver().addOnPreDrawListener(preDrawListener); + mDifferentRoot = mDecorView.getRootView() != getRootView(); + if (mDifferentRoot) { + mDecorView.postInvalidate(); + } + } else { + log("mDecorView is NULL."); + mDifferentRoot = false; + } + } + + @Override + protected void onDetachedFromWindow() { + if (mDecorView != null) { + mDecorView.getViewTreeObserver().removeOnPreDrawListener(preDrawListener); + } + release(); + super.onDetachedFromWindow(); + } + + @Override + public void draw(Canvas canvas) { + if (!useBlur || !supportRenderScript) { + mRectF.right = getWidth(); + mRectF.bottom = getHeight(); + overlayPaint.setColor(needRemoveAlphaColor() ? removeAlphaColor(mOverlayColor) : mOverlayColor); + canvas.drawRoundRect(mRectF, mRadius, mRadius, overlayPaint); + } else { + if (!mIsRendering && RENDERING_COUNT <= 0) { + super.draw(canvas); + } + } + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (isCompatMode()) { + drawBlurredBitmapCompat(canvas); + } else { + drawBlurredBitmap(canvas, mBlurredBitmap); + } + } + + private void drawBlurredBitmapCompat(Canvas canvas) { + if (mBlurredBitmap != null) { + mRectDst.right = getWidth(); + mRectDst.bottom = getHeight(); + if (getWidth() > 0 && getHeight() > 0) { + Bitmap readyDrawBitmap = getRoundedCornerBitmap(resizeImage(mBlurredBitmap, getWidth(), getHeight()), mRectDst); + if (readyDrawBitmap != null) { + canvas.drawBitmap(readyDrawBitmap, 0, 0, null); + } else { + Bitmap overlyBitmap = drawOverlyColor(Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888)); + if (overlyBitmap != null) canvas.drawBitmap(overlyBitmap, 0, 0, null); + } + } + } else { + Bitmap overlyBitmap = drawOverlyColor(Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888)); + if (overlyBitmap != null) canvas.drawBitmap(overlyBitmap, 0, 0, null); + } + } + + protected void drawBlurredBitmap(Canvas canvas, Bitmap blurredBitmap) { + if (blurredBitmap != null) { + mRectSrc.right = blurredBitmap.getWidth(); + mRectSrc.bottom = blurredBitmap.getHeight(); + mRectDst.right = getWidth(); + mRectDst.bottom = getHeight(); + canvas.drawBitmap(blurredBitmap, mRectSrc, mRectDst, null); + canvas.drawColor(needRemoveAlphaColor() ? removeAlphaColor(mOverlayColor) : mOverlayColor); + } else { + Bitmap overlyBitmap = drawOverlyColor(Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888)); + if (overlyBitmap != null) canvas.drawBitmap(overlyBitmap, 0, 0, null); + } + } + + private Bitmap getRoundedCornerBitmap(Bitmap bitmap, Rect mRectDst) { + bitmap = drawOverlyColor(resizeImage(bitmap, mRectDst.width(), mRectDst.height())); + if (bitmap == null) return null; + Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(output); + BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setShader(bitmapShader); + canvas.drawRoundRect(new RectF(mRectDst), mRadius, mRadius, paint); + return output; + } + + private Bitmap drawOverlyColor(Bitmap bitmap) { + if (bitmap != null) { + Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(output); + Rect originRect = new Rect(); + originRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight()); + canvas.drawBitmap(bitmap, originRect, originRect, overlayPaint); + canvas.drawColor(needRemoveAlphaColor() ? removeAlphaColor(mOverlayColor) : mOverlayColor); + return output; + } else { + return null; + } + } + + private Bitmap resizeImage(Bitmap bitmap, int newWidth, int newHeight) { + if (bitmap != null) { + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + float scaleWidth = ((float) newWidth) / width; + float scaleHeight = ((float) newHeight) / height; + Matrix matrix = new Matrix(); + matrix.postScale(scaleWidth, scaleHeight); + return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true); + } else { + return null; + } + } + + private static boolean supportRenderScript = false; + private boolean useBlur = true; + + public boolean isUseBlur() { + return useBlur; + } + + public BlurView setUseBlur(boolean useBlur) { + this.useBlur = useBlur; + invalidate(); + return this; + } + + private boolean needRemoveAlphaColor() { + if (overrideOverlayColor) { + return false; + } else { + return !(supportRenderScript && useBlur); + } + } + + private static int removeAlphaColor(@ColorInt int color) { + int alpha = 255; + int red = Color.red(color); + int green = Color.green(color); + int blue = Color.blue(color); + return Color.argb(alpha, red, green, blue); + } + + private static int replaceAlphaColor(@ColorInt int color, int alpha) { + int red = Color.red(color); + int green = Color.green(color); + int blue = Color.blue(color); + return Color.argb(alpha, red, green, blue); + } + + static { + /** + * 之所以需要启动一个新线程检测RenderScript是否可用的原因是不清楚Android什么时候对loadClass做了变更, + * 会直接抛出NoClassDefFoundError无法拦截,在主线程检测会导致程序直接闪退,因此改为异步检测。 + * 检测后会给定(boolean)supportRenderScript用于判断时光支持 + */ + new Thread() { + @Override + public void run() { + try { + BlurView.class.getClassLoader().loadClass(RenderScript.class.getCanonicalName()); + supportRenderScript = true; + } catch (Throwable e) { + if (isDebug()) { + e.printStackTrace(); + } + supportRenderScript = false; + } + } + }.start(); + } + + public static boolean DEBUGMODE = false; + + static boolean isDebug() { + return DEBUGMODE && DialogX.DEBUGMODE; + } + + private static void log(Object o) { + if (isDebug()) Log.i(">>>", "DialogX.BlurView: " + o.toString()); + } + + public static void error(Object o) { + if (isDebug()) Log.e(">>>", o.toString()); + } + + public BlurView setOverrideOverlayColor(boolean overrideOverlayColor) { + log("setOverrideOverlayColor: " + overrideOverlayColor); + this.overrideOverlayColor = overrideOverlayColor; + return this; + } +} \ No newline at end of file diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/DialogListView.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/DialogListView.java new file mode 100644 index 0000000..e9c08bb --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/DialogListView.java @@ -0,0 +1,129 @@ +package com.kongzue.dialogx.util.views; + +import android.content.Context; +import android.content.res.Resources; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.widget.ListAdapter; +import android.widget.ListView; + +import androidx.appcompat.view.ContextThemeWrapper; + +import com.kongzue.dialogx.interfaces.BottomMenuListViewTouchEvent; +import com.kongzue.dialogx.interfaces.DialogConvertViewInterface; +import com.kongzue.dialogx.interfaces.ScrollController; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/10/6 23:42 + */ +public class DialogListView extends ListView implements ScrollController { + + private BottomMenuListViewTouchEvent bottomMenuListViewTouchEvent; + + public DialogListView(Context context) { + super(context); + } + + public DialogListView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public DialogListView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + private DialogConvertViewInterface dialogImpl; + + public DialogListView(DialogConvertViewInterface dialog, Context context) { + super(context); + dialogImpl = dialog; + setVerticalScrollBarEnabled(false); + } + + public DialogListView(DialogConvertViewInterface dialog, Context context, int theme) { + super(new ContextThemeWrapper(context, theme)); + dialogImpl = dialog; + setVerticalScrollBarEnabled(false); + } + +// private int dip2px(float dpValue) { +// final float scale = Resources.getSystem().getDisplayMetrics().density; +// return (int) (dpValue * scale + 0.5f); +// } +// +// private int mPosition; +// private float touchDownY; +// + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + final int actionMasked = ev.getActionMasked() & MotionEvent.ACTION_MASK; + switch (actionMasked){ + case MotionEvent.ACTION_DOWN: + if (bottomMenuListViewTouchEvent != null) { + bottomMenuListViewTouchEvent.down(ev); + } + break; + case MotionEvent.ACTION_MOVE: + if (bottomMenuListViewTouchEvent != null) { + bottomMenuListViewTouchEvent.move(ev); + } + break; + case MotionEvent.ACTION_UP: + if (bottomMenuListViewTouchEvent != null) { + bottomMenuListViewTouchEvent.up(ev); + } + break; + } + return super.dispatchTouchEvent(ev); + } + + public BottomMenuListViewTouchEvent getBottomMenuListViewTouchEvent() { + return bottomMenuListViewTouchEvent; + } + + public DialogListView setBottomMenuListViewTouchEvent(BottomMenuListViewTouchEvent bottomMenuListViewTouchEvent) { + this.bottomMenuListViewTouchEvent = bottomMenuListViewTouchEvent; + return this; + } + + @Override + public int getScrollDistance() { + View c = getChildAt(0); + if (c == null) { + return 0; + } + int firstVisiblePosition = getFirstVisiblePosition(); + int top = c.getTop(); + int scrollY = -top + firstVisiblePosition * c.getHeight(); + return scrollY; + } + + @Override + public boolean isCanScroll() { + return true; + } + + boolean lockScroll; + + @Override + public void lockScroll(boolean lockScroll) { + this.lockScroll = lockScroll; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (lockScroll) return false; + return super.onTouchEvent(event); + } + + @Override + public boolean isLockScroll() { + return lockScroll; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/DialogScrollView.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/DialogScrollView.java new file mode 100644 index 0000000..2d254f9 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/DialogScrollView.java @@ -0,0 +1,73 @@ +package com.kongzue.dialogx.util.views; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.widget.ScrollView; +import com.kongzue.dialogx.interfaces.ScrollController; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/11/17 15:29 + */ +public class DialogScrollView extends ScrollView implements ScrollController { + + public DialogScrollView(Context context) { + super(context); + } + + public DialogScrollView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public DialogScrollView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public DialogScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + boolean lockScroll; + + @Override + public boolean isLockScroll() { + return lockScroll; + } + + public void lockScroll(boolean lockScroll) { + this.lockScroll = lockScroll; + } + + @Override + public int getScrollDistance() { + return getScrollY(); + } + + @Override + public boolean isCanScroll() { + View child = getChildAt(0); + if (child != null) { + int childHeight = child.getHeight(); + return getHeight() < childHeight; + } + return false; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (lockScroll) { + return false; + } + return super.onTouchEvent(ev); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + return super.dispatchTouchEvent(ev); + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/DialogXBaseRelativeLayout.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/DialogXBaseRelativeLayout.java new file mode 100644 index 0000000..3b9d234 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/DialogXBaseRelativeLayout.java @@ -0,0 +1,484 @@ +package com.kongzue.dialogx.util.views; + +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.TypedArray; +import android.graphics.Rect; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.util.AttributeSet; +import android.util.Log; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; +import android.widget.RelativeLayout; + +import androidx.annotation.Px; +import androidx.core.graphics.Insets; +import androidx.core.view.WindowInsetsCompat; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.BaseDialog; +import com.kongzue.dialogx.interfaces.DialogXBaseBottomDialog; +import com.kongzue.dialogx.interfaces.DialogXSafetyModeInterface; +import com.kongzue.dialogx.interfaces.NoTouchInterface; +import com.kongzue.dialogx.interfaces.OnSafeInsetsChangeListener; + +import java.lang.ref.WeakReference; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/22 13:53 + */ +public class DialogXBaseRelativeLayout extends RelativeLayout { + + public static boolean debugMode = false; + + private OnSafeInsetsChangeListener onSafeInsetsChangeListener; + private WeakReference parentDialog; + private boolean autoUnsafePlacePadding = true; + private boolean focusable = true; + private boolean interceptBack = true; + + private OnLifecycleCallBack onLifecycleCallBack; + private PrivateBackPressedListener onBackPressedListener; + + private FitSystemBarUtils fitSystemBarUtils; + + public FitSystemBarUtils getFitSystemBarUtils() { + return fitSystemBarUtils; + } + + public DialogXBaseRelativeLayout(Context context) { + super(context); + init(null); + } + + public DialogXBaseRelativeLayout(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs); + } + + public DialogXBaseRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(attrs); + } + + private boolean isInited = false; + + private void init(AttributeSet attrs) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + setForceDarkAllowed(false); + } + if (!isInited) { + if (attrs != null) { + TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.DialogXBaseRelativeLayout); + focusable = a.getBoolean(R.styleable.DialogXBaseRelativeLayout_baseFocusable, true); + autoUnsafePlacePadding = a.getBoolean(R.styleable.DialogXBaseRelativeLayout_autoSafeArea, true); + interceptBack = a.getBoolean(R.styleable.DialogXBaseRelativeLayout_interceptBack, true); + a.recycle(); + isInited = true; + } + if (focusable) { + setFocusable(true); + setFocusableInTouchMode(true); + } + setBkgAlpha(0f); + if (getParentDialog() != null && getParentDialog().getDialogImplMode() != DialogX.IMPL_MODE.VIEW) { + setFitsSystemWindows(true); + } + setClipChildren(false); + setClipToPadding(false); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + setDefaultFocusHighlightEnabled(false); + } + + //新增的 设置监听 OnApplyWindowInsetsListener + log("KONGZUE DEBUG DIALOGX: create fitSystemBarUtils"); + fitSystemBarUtils = FitSystemBarUtils.attachView(this, new FitSystemBarUtils.CallBack() { + @Override + public boolean isEnable(FitSystemBarUtils.Orientation orientation) { + return true; + } + + @Override + public void unsafeRect(int start, int top, int end, int bottom) { + log("KONGZUE DEBUG DIALOGX: unsafeRect t=" + top + " b=" + bottom); + if (unsafePlace == null) { + unsafePlace = new Rect(); + } + // TODO Fix bug #370 在这里对这个重新做下处理,未详细测试,比如键盘弹起时的情况 + Insets systemBarInsets = null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && getRootWindowInsets() != null) { + WindowInsetsCompat windowInsetsCompat = WindowInsetsCompat.toWindowInsetsCompat(getRootWindowInsets()); + boolean navigationBarsVisible = windowInsetsCompat.isVisible(WindowInsetsCompat.Type.navigationBars()); + boolean imeVisible = windowInsetsCompat.isVisible(WindowInsetsCompat.Type.ime()); + if (!imeVisible && navigationBarsVisible) { + systemBarInsets = windowInsetsCompat.getInsets(WindowInsetsCompat.Type.systemBars()); + if (systemBarInsets.bottom == bottom && systemBarInsets.top == top && systemBarInsets.left == start && systemBarInsets.right == end) { + systemBarInsets = null; + } + } + } + if (systemBarInsets != null) { + unsafePlace.left = Math.max(systemBarInsets.left, start); + unsafePlace.top = Math.max(systemBarInsets.top, top); + unsafePlace.right = Math.max(systemBarInsets.right, end); + unsafePlace.bottom = Math.max(systemBarInsets.bottom, bottom); + } else { + unsafePlace.left = start; + unsafePlace.top = top; + unsafePlace.right = end; + unsafePlace.bottom = bottom; + } + + if (onSafeInsetsChangeListener != null) { + onSafeInsetsChangeListener.onChange(unsafePlace); + } + + setUnsafePadding(unsafePlace.left, unsafePlace.top, unsafePlace.right, unsafePlace.bottom); + } + + @Override + public int initialPadding(FitSystemBarUtils.Orientation orientation) { + switch (orientation) { + case Start: + return extraPadding[0]; + case Top: + return extraPadding[1]; + case End: + return extraPadding[2]; + case Bottom: + return extraPadding[3]; + } + return 0; + } + }); + } + } + + public void setUnsafePadding(@Px int start, @Px int top, @Px int end, @Px int bottom) { + log("KONGZUE DEBUG DIALOGX: setUnsafePadding=" + getParentDialog() + " t=" + top + " b=" + bottom); + if (DialogX.ignoreUnsafeInsetsHorizontal) { + log(" KONGZUE DEBUG DIALOGX: ignoreUnsafeInsetsHorizontal, start and end set 0"); + start = 0; + end = 0; + } + if (isAlignBottomDialog(getParentDialog())) { + log(" KONGZUE DEBUG DIALOGX: Dialog is align bottom"); + View dialogXSafetyArea = findViewWithTag("DialogXSafetyArea"); + if (dialogXSafetyArea instanceof DialogXSafetyModeInterface) { + int dialogxSafetyMode = ((DialogXSafetyModeInterface) dialogXSafetyArea).getDialogXSafetyMode(); + boolean hasTop = (dialogxSafetyMode & 0x1) != 0; + boolean hasLeft = (dialogxSafetyMode & 0x2) != 0; + boolean hasBottom = (dialogxSafetyMode & 0x4) != 0; + boolean hasRight = (dialogxSafetyMode & 0x8) != 0; + log(" KONGZUE DEBUG DIALOGX: dialogXSafetyArea" + dialogXSafetyArea + " hasLeft=" + hasLeft + "hasTop=" + hasTop + " hasRight=" + hasRight + " hasBottom=" + hasBottom); + dialogXSafetyArea.setPadding(hasLeft ? start : 0, hasTop ? top : 0, hasRight ? end : 0, hasBottom ? bottom : 0); + if (hasTop) { + top = 0; + } + if (hasLeft) { + start = 0; + } + if (hasRight) { + end = 0; + } + if (hasBottom) { + bottom = 0; + } + } else { + ViewGroup bkgView = findViewById(R.id.bkg); + if (!((DialogXBaseBottomDialog) getParentDialog()).isBottomNonSafetyAreaBySelf() && bkgView != null) { + log(" KONGZUE DEBUG DIALOGX: bkgView.setPadding b=" + bottom); + bkgView.setPadding(0, 0, 0, bottom); + } + bottom = 0; + } + } + if (isAutoUnsafePlacePadding()) { + log(" KONGZUE DEBUG DIALOGX: root.setPadding t=" + top + " b=" + bottom); + setPadding(start, top, end, bottom); + } + } + + private boolean isAlignBottomDialog(BaseDialog parentDialog) { + return getParentDialog() instanceof DialogXBaseBottomDialog || findViewWithTag("DialogXSafetyArea") instanceof DialogXSafetyModeInterface; + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + log("#dispatchKeyEvent: KeyCode=" + event.getKeyCode()); + if (isAttachedToWindow() && event.getAction() == KeyEvent.ACTION_UP && event.getKeyCode() == KeyEvent.KEYCODE_BACK && interceptBack) { + if (onBackPressedListener != null && !parentDialog.get().isHide()) { + return onBackPressedListener.onBackPressed(); + } + } + return super.dispatchKeyEvent(event); + } + + boolean touch; + float touchDownX, touchDownY; + + @Override + public boolean onTouchEvent(MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + touch = true; + touchDownX = event.getX(); + touchDownY = event.getY(); + break; + case MotionEvent.ACTION_UP: + if (touch && findFocus() != this && getParentDialog() != null) { + float deltaTouchOffset = getParentDialog().dip2px(5); + if (Math.abs(event.getX() - touchDownX) <= deltaTouchOffset && Math.abs(event.getY() - touchDownY) <= deltaTouchOffset) { + callOnClick(); + } + } + break; + } + if (getParentDialog() instanceof NoTouchInterface) { + return super.onTouchEvent(event); + } + InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + return super.onTouchEvent(event); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (!isInEditMode()) { + if (getParentDialog() == null || getParentDialog().getOwnActivity() == null) return; + if (onLifecycleCallBack != null) { + onLifecycleCallBack.onShow(); + } + isLightMode = (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_NO; + if (focusable) { + requestFocus(); + } + } + } + + @Override + protected void onDetachedFromWindow() { + if (onLifecycleCallBack != null) { + onLifecycleCallBack.onDismiss(); + } + if (fitSystemBarUtils != null) { + fitSystemBarUtils.recycle(); + } + fitSystemBarUtils = null; + onSafeInsetsChangeListener = null; + super.onDetachedFromWindow(); + } + + @Override + public boolean performClick() { + if (!isEnabled()) return false; + return super.performClick(); + } + + @Override + public boolean callOnClick() { + if (!isEnabled()) return false; + return super.callOnClick(); + } + + public DialogXBaseRelativeLayout setOnLifecycleCallBack(OnLifecycleCallBack onLifecycleCallBack) { + this.onLifecycleCallBack = onLifecycleCallBack; + return this; + } + + public float getSafeHeight() { + return getMeasuredHeight() - unsafePlace.bottom - unsafePlace.top; + } + + private WeakReference requestFocusView; + + public void bindFocusView(View view) { + if (view != this) { + requestFocusView = new WeakReference<>(view); + } + } + + @Override + public boolean requestFocus(int direction, Rect previouslyFocusedRect) { + if (getParentDialog() != null && getParentDialog() instanceof NoTouchInterface) { + return false; + } + if (direction == View.FOCUS_DOWN && requestFocusView != null && requestFocusView.get() != null && requestFocusView.get() != this) { + return requestFocusView.get().requestFocus(); + } + View findFocusView = findFocus(); + if (findFocusView != null && findFocusView != this) { + findFocusView.requestFocus(); + return true; + } + return super.requestFocus(direction, previouslyFocusedRect); + } + + public void requestFocusOnResume() { + View findFocusView = findFocus(); + if (findFocusView != null && findFocusView != this) { + findFocusView.requestFocus(); + return; + } + requestFocus(); + } + + public abstract static class OnLifecycleCallBack { + public void onShow() { + } + + public abstract void onDismiss(); + } + + protected Rect unsafePlace = new Rect(); + + public DialogXBaseRelativeLayout setOnBackPressedListener(PrivateBackPressedListener onBackPressedListener) { + this.onBackPressedListener = onBackPressedListener; + return this; + } + + public OnSafeInsetsChangeListener getOnSafeInsetsChangeListener() { + return onSafeInsetsChangeListener; + } + + public DialogXBaseRelativeLayout setOnSafeInsetsChangeListener(OnSafeInsetsChangeListener onSafeInsetsChangeListener) { + this.onSafeInsetsChangeListener = onSafeInsetsChangeListener; + return this; + } + + public boolean isAutoUnsafePlacePadding() { + return autoUnsafePlacePadding; + } + + public Rect getUnsafePlace() { + return unsafePlace; + } + + public DialogXBaseRelativeLayout setAutoUnsafePlacePadding(boolean autoUnsafePlacePadding) { + this.autoUnsafePlacePadding = autoUnsafePlacePadding; + return this; + } + + public BaseDialog getParentDialog() { + return parentDialog == null ? null : parentDialog.get(); + } + + public DialogXBaseRelativeLayout setParentDialog(BaseDialog parentDialog) { + this.parentDialog = new WeakReference<>(parentDialog); + if (parentDialog != null && parentDialog.getDialogImplMode() != DialogX.IMPL_MODE.VIEW) { + setFitsSystemWindows(true); + } + if (unsafePlace != null) { + log("KONGZUE DEBUG DIALOGX: setParentDialog()=" + getParentDialog()); + setUnsafePadding(unsafePlace.left, unsafePlace.top, unsafePlace.right, unsafePlace.bottom); + } else { + log("KONGZUE DEBUG DIALOGX: setParentDialog() unsafePlace is null"); + } + return this; + } + + boolean isLightMode = true; + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + boolean newLightStatus = ((newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_NO); + if (isLightMode != newLightStatus && DialogX.globalTheme == DialogX.THEME.AUTO && getParentDialog() != null) { + getParentDialog().restartDialog(); + } + } + + float nowBkgAlphaValue; + + public DialogXBaseRelativeLayout setBkgAlpha(float alpha) { + nowBkgAlphaValue = alpha; + if (getBackground() != null) getBackground().mutate().setAlpha((int) (alpha * 255)); + return this; + } + + @Override + public void setBackground(Drawable background) { + background.setAlpha((int) (nowBkgAlphaValue * 255)); + super.setBackground(background); + } + + @Override + public void setBackgroundColor(int color) { + setBackground(new ColorDrawable(color)); + } + + public boolean isBaseFocusable() { + return focusable; + } + + public boolean isInterceptBack() { + return interceptBack; + } + + public DialogXBaseRelativeLayout setInterceptBack(boolean interceptBack) { + this.interceptBack = interceptBack; + return this; + } + + @Override + public void setVisibility(int visibility) { + if (visibility == GONE && getAlpha() == 0f) { + setAlpha(0.01f); + } + super.setVisibility(visibility); + } + + public interface PrivateBackPressedListener { + boolean onBackPressed(); + } + + int[] extraPadding = new int[4]; + + public void setRootPadding(int left, int top, int right, int bottom) { + extraPadding[0] = left; + extraPadding[1] = top; + extraPadding[2] = right; + extraPadding[3] = bottom; + } + + public int getRootPaddingLeft() { + return extraPadding[0]; + } + + public int getRootPaddingTop() { + return extraPadding[1]; + } + + public int getRootPaddingRight() { + return extraPadding[2]; + } + + public int getRootPaddingBottom() { + return extraPadding[3]; + } + + public int getUseAreaWidth() { + return getWidth() - getRootPaddingRight(); + } + + public int getUseAreaHeight() { + return getHeight() - getRootPaddingBottom(); + } + + protected void log(String s) { + if (debugMode && DialogX.DEBUGMODE) { + Log.e(">>>", s); + } + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/ExtendChildLayoutParamsFrameLayout.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/ExtendChildLayoutParamsFrameLayout.java new file mode 100644 index 0000000..9d6773f --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/ExtendChildLayoutParamsFrameLayout.java @@ -0,0 +1,31 @@ +package com.kongzue.dialogx.util.views; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.RelativeLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class ExtendChildLayoutParamsFrameLayout extends FrameLayout { + public ExtendChildLayoutParamsFrameLayout(@NonNull Context context) { + super(context); + } + + public ExtendChildLayoutParamsFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public ExtendChildLayoutParamsFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + setLayoutParams(new RelativeLayout.LayoutParams(params.width, params.height)); + super.addView(child, index, params); + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/FitSystemBarUtils.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/FitSystemBarUtils.java new file mode 100644 index 0000000..38d5ebf --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/FitSystemBarUtils.java @@ -0,0 +1,637 @@ +package com.kongzue.dialogx.util.views; + +import static androidx.core.view.WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE; + +import android.app.Activity; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Rect; +import android.os.Build; +import android.util.Log; +import android.view.Surface; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.WindowInsets; +import android.view.WindowInsetsController; +import android.view.WindowManager; + +import androidx.annotation.NonNull; +import androidx.core.graphics.Insets; +import androidx.core.view.DisplayCutoutCompat; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsAnimationCompat; +import androidx.core.view.WindowInsetsCompat; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.interfaces.BaseDialog; + +import java.util.List; + +public class FitSystemBarUtils { + + //当前是否正在平滑填充中 + private boolean inSmoothingPadding = false; + + public boolean isInSmoothingPadding() { + return inSmoothingPadding; + } + + //是否需要处理挖孔屏,刘海屏 + public boolean safeCutOutPadding = true; + + //是否需要平滑填充,仅在android R及以上生效,默认开启 + public boolean smoothPadding = true; + + private View contentView; + + private CallBack callBack; + + private BaseDialog dialog; + + /** + * 绑定到View + * + * @param view 指定View + */ + public static FitSystemBarUtils attachView(View view) { + return attachView(view, new CallBack() { + @Override + public boolean isEnable(Orientation orientation) { + return true; + } + + @Override + public void unsafeRect(int start, int top, int end, int bottom) { + + } + + @Override + public int initialPadding(Orientation orientation) { + return 0; + } + }); + } + + /** + * 绑定到View + * + * @param view 指定View + * @param callBack 用于判断是否需要某条边的padding,会非常频繁的调用,所以不要在里面做需要耗时的操作,以免卡UI线程 + */ + public static FitSystemBarUtils attachView(View view, CallBack callBack) { + return new FitSystemBarUtils(view, callBack); + } + + + /** + * 绑定到View + * + * @param view 指定View + * @param start 是否需要paddingStart + * @param top 是否需要paddingTop + * @param end 是否需要paddingEnd + * @param bottom 是否需要paddingBottom + */ + public static FitSystemBarUtils attachView( + View view, + boolean start, + boolean top, + boolean end, + boolean bottom + ) { + return attachView(view, new CallBack() { + @Override + public boolean isEnable(Orientation orientation) { + switch (orientation) { + case Start: + return start; + case Top: + return top; + case End: + return end; + case Bottom: + return bottom; + } + return false; + } + + @Override + public void unsafeRect(int start, int top, int end, int bottom) { + + } + + @Override + public int initialPadding(Orientation orientation) { + return 0; + } + }); + } + + private FitSystemBarUtils() { + } + + public FitSystemBarUtils(View view, CallBack callBack) { +// view.setFitsSystemWindows(true); + contentView = view; + this.callBack = callBack; + if (view instanceof DialogXBaseRelativeLayout) { + dialog = ((DialogXBaseRelativeLayout) view).getParentDialog(); + } + applyWindowInsets(); + } + + View.OnLayoutChangeListener rootViewLayoutChangeListener; + + public void applyWindowInsets() { +// view.fitsSystemWindows = true + //创建原始padding的快照 + RelativePadding initialPadding = new RelativePadding( + ViewCompat.getPaddingStart(contentView), + contentView.getPaddingTop(), + ViewCompat.getPaddingEnd(contentView), + contentView.getPaddingBottom() + ); + //不带平滑变化的 + ViewCompat.setOnApplyWindowInsetsListener(contentView, (view, insets) -> { + if (inSmoothingPadding) return insets; + formatInsets(insets, new RelativePadding(initialPadding)); + return insets; + }); + //带平滑变化的,但是支支持android R及以上 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + log("FitSystemBarUtils: setWindowInsetsAnimationCallback"); + ViewCompat.setWindowInsetsAnimationCallback(contentView, + new WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) { + + @NonNull + @Override + public WindowInsetsCompat onProgress( + @NonNull WindowInsetsCompat insets, + @NonNull List runningAnimations + ) { + log("FitSystemBarUtils: setWindowInsetsAnimationCallback#onProgress: " + insets); + if (smoothPadding) { + formatInsets(insets, new RelativePadding(initialPadding)); + } + return insets; + } + + @Override + public void onEnd(@NonNull WindowInsetsAnimationCompat animation) { + log("FitSystemBarUtils: setWindowInsetsAnimationCallback#onEnd "); + inSmoothingPadding = false; + super.onEnd(animation); + } + + @Override + public void onPrepare(@NonNull WindowInsetsAnimationCompat animation) { + inSmoothingPadding = smoothPadding; + super.onPrepare(animation); + } + }); + } + + if (ViewCompat.isAttachedToWindow(contentView)) { + log("FitSystemBarUtils: AttachedToWindow ok"); + ViewCompat.requestApplyInsets(contentView); + } else { + log("FitSystemBarUtils: wait AttachedToWindow"); + contentView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View view) { + view.removeOnAttachStateChangeListener(this); + + log("FitSystemBarUtils: onViewAttachedToWindow"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH && (Build.VERSION.SDK_INT < Build.VERSION_CODES.R || getAppTargetSDKVersion() < Build.VERSION_CODES.R)) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + //修复<=API29的部分设备上存在的非安全区不回调的问题 + View parentView = (View) view.getParent(); + if (rootViewLayoutChangeListener != null) { + parentView.removeOnLayoutChangeListener(rootViewLayoutChangeListener); + } + rootViewLayoutChangeListener = new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { + WindowInsets windowInsets = v.getRootView().getRootWindowInsets(); + if (windowInsets != null) { + log(" FitSystemBarUtils: RootView get Insets"); + formatInsets(WindowInsetsCompat.toWindowInsetsCompat(windowInsets), new RelativePadding(initialPadding)); + } else { + log(" FitSystemBarUtils: RootView not get Insets"); + } + } + }; + parentView.addOnLayoutChangeListener(rootViewLayoutChangeListener); + parentView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + } + + @Override + public void onViewDetachedFromWindow(View v) { + parentView.removeOnLayoutChangeListener(rootViewLayoutChangeListener); + } + }); + } else { + View parentView = (View) view.getParent(); + ViewTreeObserver.OnPreDrawListener preDrawListener = new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + // 获取状态栏高度 + Rect insets = new Rect(); + parentView.getWindowVisibleDisplayFrame(insets); + int statusBarHeight = insets.top; + + // 获取导航栏高度 + int navigationBarHeight = 0; + Resources resources = parentView.getResources(); + int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android"); + if (resourceId > 0) { + navigationBarHeight = resources.getDimensionPixelSize(resourceId); + } + + log("FitSystemBarUtils: below Android M use support mode: statusBarHeight=" + statusBarHeight + ", navigationBarHeight=" + navigationBarHeight); + int deviceOrientation = checkOrientationAndStatusBarSide(); + log(" FitSystemBarUtils: deviceOrientation = " + deviceOrientation); + switch (deviceOrientation) { + case 1: + case -1: + initialPadding.end = navigationBarHeight; + initialPadding.start = 0; + break; + default: + initialPadding.top = statusBarHeight; + initialPadding.bottom = navigationBarHeight; + break; + } + + applyCallBack(initialPadding); + + parentView.getViewTreeObserver().removeOnPreDrawListener(this); + + return true; + } + }; + parentView.getViewTreeObserver().addOnPreDrawListener(preDrawListener); + + } + } + + ViewCompat.requestApplyInsets(view); + } + + @Override + public void onViewDetachedFromWindow(View view) { + + } + }); + } + } + + RelativePadding relativePaddingCache; + + /** + * 针对不同版本处理Insets + */ + private void formatInsets(WindowInsetsCompat insetsCompat, RelativePadding initialPadding) { + if (contentView == null || insetsCompat == null || initialPadding == null) return; + + relativePaddingCache = initialPadding; + int cutoutPaddingLeft = 0; + int cutoutPaddingTop = 0; + int cutoutPaddingRight = 0; + int cutoutPaddingBottom = 0; + + int systemWindowInsetLeft = 0; + int systemWindowInsetTop = 0; + int systemWindowInsetRight = 0; + int systemWindowInsetBottom = 0; + + if (safeCutOutPadding) { + DisplayCutoutCompat displayCutout = insetsCompat.getDisplayCutout(); + if (null != displayCutout) { + cutoutPaddingTop = displayCutout.getSafeInsetTop(); + cutoutPaddingLeft = displayCutout.getSafeInsetLeft(); + cutoutPaddingRight = displayCutout.getSafeInsetRight(); + cutoutPaddingBottom = displayCutout.getSafeInsetRight(); + } + } + Insets systemBars = insetsCompat.getInsets( + WindowInsetsCompat.Type.ime() | WindowInsetsCompat.Type.systemBars() + ); + systemWindowInsetLeft = systemBars.left; + systemWindowInsetRight = systemBars.right; + + //对api低于30的设备,做额外判断,api 30+的不需要这个 + int suv = contentView.getRootView().getWindowSystemUiVisibility(); + boolean statusBar = true; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + statusBar = (suv & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0; + } + boolean naviBar = true; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + naviBar = (suv & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; + } + if (naviBar && (insetsCompat.isVisible(WindowInsetsCompat.Type.ime()) + || insetsCompat.isVisible(WindowInsetsCompat.Type.navigationBars())) + ) { + systemWindowInsetBottom = systemBars.bottom; + } + if (statusBar && insetsCompat.isVisible(WindowInsetsCompat.Type.statusBars())) { + systemWindowInsetTop = systemBars.top; + } + if (isWrongInsets(systemBars)) { + log(" FitSystemBarUtils: isWrongInsets try special mode..."); + int deviceOrientation = checkOrientationAndStatusBarSide(); + log(" FitSystemBarUtils: deviceOrientation = " + deviceOrientation); + switch (deviceOrientation) { + case 1: + initialPadding.end = getStatusBarHeight(); + initialPadding.start = getNavigationBarHeight(); + break; + default: + initialPadding.top = getStatusBarHeight(); + initialPadding.bottom = getNavigationBarHeight(); + break; + } + addListenerWhenImeHeightChanged(); + } else { + specialMode = false; + if (callBack.isEnable(Orientation.Top)) { + initialPadding.top += Math.max(systemWindowInsetTop, cutoutPaddingTop); + } + if (callBack.isEnable(Orientation.Bottom)) { + initialPadding.bottom += Math.max(systemWindowInsetBottom, cutoutPaddingBottom); + } + + boolean isRtl = + ViewCompat.getLayoutDirection(contentView) == ViewCompat.LAYOUT_DIRECTION_RTL; + if (callBack.isEnable(Orientation.Start)) { + if (isRtl) { + initialPadding.start += Math.max(systemWindowInsetRight, cutoutPaddingRight); + } else { + initialPadding.start += Math.max(systemWindowInsetLeft, cutoutPaddingLeft); + } + } + if (callBack.isEnable(Orientation.End)) { + if (isRtl) { + initialPadding.end += Math.max(systemWindowInsetLeft, cutoutPaddingLeft); + } else { + initialPadding.end += Math.max(systemWindowInsetRight, cutoutPaddingRight); + } + } + } + + applyCallBack(initialPadding); + } + + private void applyCallBack() { + if (relativePaddingCache != null) { + applyCallBack(relativePaddingCache); + } + } + + private void applyCallBack(RelativePadding initialPadding) { + if (callBack == null) { + return; + } + //加上用户自定义的 + initialPadding.start += callBack.initialPadding(Orientation.Start); + initialPadding.top += callBack.initialPadding(Orientation.Top); + initialPadding.end += callBack.initialPadding(Orientation.End); + initialPadding.bottom += callBack.initialPadding(Orientation.Bottom); + + initialPadding.applyToView(contentView); + //四边 非安全区 传递回去 + log(" KONGZUE DEBUG DIALOGX FitSystemBarUtils callBack: left=" + initialPadding.start + " top=" + initialPadding.top + + " right=" + initialPadding.end + " bottom=" + initialPadding.bottom + " specialMode=" + specialMode + + " specialModeImeHeight=" + specialModeImeHeight + ); + callBack.unsafeRect( + initialPadding.start, + initialPadding.top, + initialPadding.end, + initialPadding.bottom + (specialMode ? specialModeImeHeight : 0) + ); + } + + private boolean isWrongInsets(Insets systemBars) { + return systemBars.top == 0 && systemBars.bottom == 0 && systemBars.left == 0 && systemBars.right == 0; + } + + public interface CallBack { + + //是否启用指定边的insets + boolean isEnable(Orientation orientation); + + //非安全区 + void unsafeRect(int start, int top, int end, int bottom); + + //用户自定义的padding + int initialPadding(Orientation orientation); + } + + public static class RelativePadding { + int start; + int top; + int end; + int bottom; + + public RelativePadding(int start, int top, int end, int bottom) { + this.start = start; + this.top = top; + this.end = end; + this.bottom = bottom; + } + + public RelativePadding(RelativePadding other) { + start = other.start; + top = other.top; + end = other.end; + bottom = other.bottom; + } + + /** + * Applies this relative padding to the view. + */ + public void applyToView(View view) { + if (view instanceof DialogXBaseRelativeLayout) { + //((DialogXBaseRelativeLayout) view).setUnsafePadding(start, top, end, bottom); + } else { + ViewCompat.setPaddingRelative(view, start, top, end, bottom); + } + } + } + + enum Orientation { + Start, Top, End, Bottom + } + + protected void log(String s) { + if (DialogXBaseRelativeLayout.debugMode && DialogX.DEBUGMODE) { + Log.e(">>>", s); + } + } + + private int getStatusBarHeight() { + if (isFullScreen() || getDecorView() == null) { + return 0; + } + WindowInsetsController insetsController = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) ? getDecorView().getWindowInsetsController() : null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && insetsController != null && (insetsController.getSystemBarsBehavior() & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) == 0) { + return 0; + } + Resources res; + if (contentView == null || contentView.getContext() == null) { + res = Resources.getSystem(); + } else { + res = contentView.getContext().getResources(); + } + int result = 0; + int resourceId = res.getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + result = res.getDimensionPixelSize(resourceId); + } + return result; + } + + private int getNavigationBarHeight() { + if (isFullScreen() || getDecorView() == null) { + return 0; + } + WindowInsetsController insetsController = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) ? getDecorView().getWindowInsetsController() : null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && insetsController != null && (insetsController.getSystemBarsBehavior() & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) == 0) { + log("getNavigationBarHeight =0"); + return 0; + } + Resources res; + if (contentView == null || contentView.getContext() == null) { + res = Resources.getSystem(); + } else { + res = contentView.getContext().getResources(); + } + int result = 0; + int resourceId = res.getIdentifier("navigation_bar_height", "dimen", "android"); + if (resourceId > 0) { + result = res.getDimensionPixelSize(resourceId); + } + return result; + } + + private boolean isFullScreen() { + Activity activity = getActivity(); + if (activity == null) { + return false; + } + // 通过检查窗口标志来判断 + int flags = activity.getWindow().getAttributes().flags; + if ((flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0) { + return true; + } + // 通过检查系统 UI 标志来判断 + int uiOptions = activity.getWindow().getDecorView().getSystemUiVisibility(); + if ((uiOptions & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0) { + return true; + } + return false; + } + + private int checkOrientationAndStatusBarSide() { + Activity activity = getActivity(); + if (activity == null) { + return 0; + } + // 判断是否为横屏 + if (activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { + // 判断状态栏位置 + int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); + switch (rotation) { + case Surface.ROTATION_90: + // 设备旋转了 90 度,状态栏在左侧 + return -1; + case Surface.ROTATION_270: + // 设备旋转了 270 度,状态栏在右侧 + return 1; + default: + // 其他情况,不应当发生在横屏状态 + return 0; + } + } + return 0; + } + + private int specialModeImeHeight; + private boolean specialMode; + private ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener; + + private View getDecorView() { + Activity activity = getActivity(); + if (activity == null) { + return null; + } + return activity.getWindow().getDecorView(); + } + + private void addListenerWhenImeHeightChanged() { + specialMode = true; + View decorView = getDecorView(); + if (decorView == null) return; + if (onGlobalLayoutListener != null) { + decorView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener); + } + decorView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + Rect r = new Rect(); + decorView.getWindowVisibleDisplayFrame(r); + int screenHeight = decorView.getHeight(); + WindowInsetsController insetsController = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) ? getDecorView().getWindowInsetsController() : null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && insetsController != null && (insetsController.getSystemBarsBehavior() & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) == 0) { + r.bottom = screenHeight; + } + int keypadHeight = screenHeight - r.bottom; + if (keypadHeight != specialModeImeHeight) { + specialModeImeHeight = keypadHeight; + log(" FitSystemBarUtils: specialModeImeHeight=" + specialModeImeHeight); + applyCallBack(); + } + } + }); + } + + private int getAppTargetSDKVersion() { + try { + Context context = BaseDialog.getApplicationContext(); + if (context == null) { + return -1; + } + PackageManager pm = context.getPackageManager(); + ApplicationInfo applicationInfo = pm.getApplicationInfo(context.getPackageName(), 0); + int targetSdkVersion = applicationInfo.targetSdkVersion; + return targetSdkVersion; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return -1; + } + + private Activity getActivity() { + if (dialog == null) return BaseDialog.getTopActivity(); + return dialog.getOwnActivity(); + } + + public void recycle() { + View decorView = getDecorView(); + if (decorView != null && onGlobalLayoutListener != null) { + decorView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener); + } + onGlobalLayoutListener = null; + callBack = null; + contentView = null; + dialog = null; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/MaxLinearLayout.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/MaxLinearLayout.java new file mode 100644 index 0000000..dc37368 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/MaxLinearLayout.java @@ -0,0 +1,111 @@ +package com.kongzue.dialogx.util.views; + +import static android.view.View.MeasureSpec.EXACTLY; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; + +import androidx.annotation.Nullable; + +import com.kongzue.dialogx.R; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/8/26 11:01 + */ +public class MaxLinearLayout extends LinearLayout { + + private int maxWidth; + private int maxHeight; + private int minWidth; + private int minHeight; + + public MaxLinearLayout(Context context) { + super(context); + init(context, null); + } + + public MaxLinearLayout(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public MaxLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + private void init(Context context, AttributeSet attrs) { + if (attrs != null) { + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DialogXMaxLayout); + maxWidth = a.getDimensionPixelSize(R.styleable.DialogXMaxLayout_maxLayoutWidth, 0); + maxHeight = a.getDimensionPixelSize(R.styleable.DialogXMaxLayout_maxLayoutHeight, 0); + minWidth = a.getDimensionPixelSize(R.styleable.DialogXMaxLayout_minLayoutWidth, 0); + minHeight = a.getDimensionPixelSize(R.styleable.DialogXMaxLayout_minLayoutHeight, 0); + + a.recycle(); + } + minWidth = minWidth == 0 ? getMinimumWidth() : minWidth; + minHeight = minHeight == 0 ? getMinimumHeight() : minHeight; + } + + public MaxLinearLayout setMaxHeight(int maxHeight) { + this.maxHeight = maxHeight; + return this; + } + + public MaxLinearLayout setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + return this; + } + + private int preWidth = -1; + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + + if (preWidth == -1 && widthSize != 0) { + preWidth = widthSize; + } + if (heightSize > maxHeight && maxHeight != 0) { + heightSize = maxHeight; + } + if (widthSize > maxWidth && maxWidth != 0) { + widthSize = maxWidth; + } + + int maxHeightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode); + int maxWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode); + super.onMeasure(maxWidthMeasureSpec, maxHeightMeasureSpec); + } + + public int dip2px(float dpValue) { + final float scale = getResources().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + @Override + public void setMinimumHeight(int minHeight) { + this.minHeight = minHeight; + super.setMinimumHeight(minHeight); + } + + @Override + public void setMinimumWidth(int minWidth) { + this.minWidth = minWidth; + super.setMinimumWidth(minWidth); + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/MaxRelativeLayout.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/MaxRelativeLayout.java new file mode 100644 index 0000000..5766b03 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/MaxRelativeLayout.java @@ -0,0 +1,223 @@ +package com.kongzue.dialogx.util.views; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.os.Build; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.widget.RelativeLayout; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.DialogXSafetyModeInterface; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2019/9/24 17:34 + */ +public class MaxRelativeLayout extends RelativeLayout implements DialogXSafetyModeInterface { + + private int maxWidth; + private int maxHeight; + private int minWidth; + private int minHeight; + private boolean lockWidth; + private boolean interceptTouch = true; + private View contentView; + private int dialogXSafetyMode; + + public MaxRelativeLayout(Context context) { + super(context); + init(context, null); + } + + public MaxRelativeLayout(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public MaxRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + private float startAnimValue = 0, endAnimValue = 0; + + private void init(Context context, AttributeSet attrs) { + if (attrs != null) { + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DialogXMaxLayout); + maxWidth = a.getDimensionPixelSize(R.styleable.DialogXMaxLayout_maxLayoutWidth, 0); + maxHeight = a.getDimensionPixelSize(R.styleable.DialogXMaxLayout_maxLayoutHeight, 0); + minWidth = a.getDimensionPixelSize(R.styleable.DialogXMaxLayout_minLayoutWidth, 0); + minHeight = a.getDimensionPixelSize(R.styleable.DialogXMaxLayout_minLayoutHeight, 0); + lockWidth = a.getBoolean(R.styleable.DialogXMaxLayout_lockWidth, false); + interceptTouch = a.getBoolean(R.styleable.DialogXMaxLayout_interceptTouch, true); + dialogXSafetyMode = a.getInt(R.styleable.DialogXMaxLayout_dialogXSafetyMode, 0); + a.recycle(); + } + minWidth = minWidth == 0 ? getMinimumWidth() : minWidth; + minHeight = minHeight == 0 ? getMinimumHeight() : minHeight; + + if (!isInEditMode()) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + animate().setUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float progress = (float) animation.getAnimatedValue(); + long value = (long) (startAnimValue + (endAnimValue - startAnimValue) * progress); + if (onYChangedListener != null) onYChangedListener.y(value); + } + }); + } + } + } + + public MaxRelativeLayout setMaxHeight(int maxHeight) { + if (maxHeight > 0) this.maxHeight = maxHeight; + return this; + } + + public MaxRelativeLayout setMaxWidth(int maxWidth) { + if (maxWidth > 0) this.maxWidth = maxWidth; + return this; + } + + public void setMinHeight(int minHeight) { + if (minHeight > 0) this.minHeight = minHeight; + } + + public void setMinWidth(int minWidth) { + if (minWidth > 0) this.minWidth = minWidth; + } + + private int preWidth = -1; + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + + if (preWidth == -1 && widthSize != 0) { + preWidth = widthSize; + } + if (lockWidth) { + maxWidth = Math.min(maxWidth, Math.min(widthSize, preWidth)); + } + if (heightSize > maxHeight && maxHeight != 0) { + heightSize = maxHeight + getPaddingBottom() + getPaddingTop(); + } + if (widthSize > maxWidth && maxWidth != 0) { + widthSize = maxWidth + getPaddingLeft() + getPaddingRight(); + } + + int maxHeightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode); + int maxWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode); + super.onMeasure(maxWidthMeasureSpec, maxHeightMeasureSpec); + } + + public int dip2px(float dpValue) { + final float scale = getResources().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + public boolean isLockWidth() { + return lockWidth; + } + + public MaxRelativeLayout setLockWidth(boolean lockWidth) { + this.lockWidth = lockWidth; + return this; + } + + private OnYChanged onYChangedListener; + + int navBarHeight; + Paint navBarPaint; + + public void setNavBarHeight(int height) { + navBarHeight = height; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (navBarHeight != 0 && DialogX.bottomDialogNavbarColor != 0) { + if (navBarPaint == null) { + navBarPaint = new Paint(); + navBarPaint.setColor(DialogX.bottomDialogNavbarColor); + } + canvas.drawRect(0, getHeight() - navBarHeight, getWidth(), getHeight(), navBarPaint); + } + } + + public void setContentView(View contentView) { + this.contentView = contentView; + } + + public interface OnYChanged { + void y(float y); + } + + @Override + public void setY(float y) { + super.setY(y); + } + + public OnYChanged getOnYChanged() { + return onYChangedListener; + } + + public MaxRelativeLayout setOnYChanged(OnYChanged onYChanged) { + this.onYChangedListener = onYChanged; + return this; + } + + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY); + if (onYChangedListener != null) onYChangedListener.y(translationY); + } + + private OnTouchListener onTouchListener; + + @Override + public void setOnTouchListener(OnTouchListener l) { + onTouchListener = l; + } + + boolean reInterceptTouch; + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (onTouchListener != null) { + reInterceptTouch = onTouchListener.onTouch(this, ev); + } + return super.dispatchTouchEvent(ev); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + return reInterceptTouch; + } + + public int getDialogXSafetyMode() { + return dialogXSafetyMode; + } + + public MaxRelativeLayout setDialogXSafetyMode(int dialogXSafetyMode) { + this.dialogXSafetyMode = dialogXSafetyMode; + return this; + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/NoArticulatedProgressView.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/NoArticulatedProgressView.java new file mode 100644 index 0000000..5172e8a --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/NoArticulatedProgressView.java @@ -0,0 +1,452 @@ +package com.kongzue.dialogx.util.views; + +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.HapticFeedbackConstants; +import android.view.View; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.LinearInterpolator; + +import androidx.annotation.Nullable; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.ProgressViewInterface; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/3/14 20:58 + */ +public class NoArticulatedProgressView extends View implements ProgressViewInterface { + public static final int STATUS_LOADING = 0; + public static final int STATUS_SUCCESS = 1; + public static final int STATUS_WARNING = 2; + public static final int STATUS_ERROR = 3; + public static final int STATUS_PROGRESSING = 4; + + private int status = STATUS_LOADING; + + private int width = dip2px(2); + private int color = Color.WHITE; + + public NoArticulatedProgressView(Context context) { + super(context); + init(null); + } + + public NoArticulatedProgressView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(attrs); + } + + public NoArticulatedProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(attrs); + } + + public NoArticulatedProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(attrs); + } + + private ValueAnimator rotateAnimator; + private ValueAnimator followAnimator; + + private float currentRotateDegrees; + private float followRotateDegrees; + + //跟随点度数做正弦值变化,halfSweepAMinValue为最低相较目标点度数差值,halfSweepAMaxValue为最大相较目标点度数差值 + private float halfSweepAMaxValue = 180; + private float halfSweepAMinValue = 80; + //正弦函数的半径 + private float halfSweepA; + + Paint mPaint = new Paint(); + + private boolean isInited = false; + + private void init(AttributeSet attrs) { + synchronized (NoArticulatedProgressView.class) { + if (isInited) { + return; + } + isInited = true; + if (attrs != null) { + TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressView); + width = a.getDimensionPixelSize(R.styleable.ProgressView_progressStrokeWidth, dip2px(2)); + color = a.getDimensionPixelSize(R.styleable.ProgressView_progressStrokeColor, color); + + a.recycle(); + } + + mPaint.setAntiAlias(true); + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setStrokeWidth(width); + mPaint.setStrokeCap(Paint.Cap.ROUND); + mPaint.setColor(color); + + if (!isInEditMode()) { + halfSweepA = (halfSweepAMaxValue - halfSweepAMinValue) / 2; + + rotateAnimator = ValueAnimator.ofFloat(0, 365); + rotateAnimator.setDuration(1000); + rotateAnimator.setInterpolator(new LinearInterpolator()); + rotateAnimator.setRepeatCount(-1); + rotateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + currentRotateDegrees = (float) animation.getAnimatedValue(); + invalidate(); + } + }); + + followAnimator = ValueAnimator.ofFloat(0, 365); + followAnimator.setDuration(1500); + followAnimator.setInterpolator(new LinearInterpolator()); + followAnimator.setRepeatCount(-1); + followAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + followRotateDegrees = (float) animation.getAnimatedValue(); + } + }); + + followAnimator.start(); + rotateAnimator.start(); + } + } + } + + //旋转圆的中心坐标 + private float mCenterX; + private float mCenterY; + //半径 + private float mRadius = 100; + private RectF oval; + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + mCenterX = w * 1f / 2; + mCenterY = h * 1f / 2; + mRadius = Math.min(getWidth(), getHeight()) / 2 - width / 2; + oval = new RectF(mCenterX - mRadius, mCenterY - mRadius, mCenterX + mRadius, mCenterY + mRadius); + } + + protected float oldAnimAngle; + private int successStep = 0; + + @Override + protected void onDraw(Canvas canvas) { + if (isInEditMode()) { + canvas.drawArc(oval, 0, 365, false, mPaint); + return; + } + if (noShowLoading) { + canvas.drawArc(oval, 0, 365, false, mPaint); + successStep = 2; + drawDoneMark(status, canvas); + return; + } + + float sweepAngle = (float) (halfSweepA * Math.sin(Math.toRadians(followRotateDegrees))) + halfSweepA + halfSweepAMinValue / 2; + switch (status) { + case STATUS_LOADING: + canvas.drawArc(oval, currentRotateDegrees, -sweepAngle, false, mPaint); + break; + case STATUS_SUCCESS: + case STATUS_WARNING: + case STATUS_ERROR: + canvas.drawArc(oval, 0, 360, false, mPaint); + drawDoneMark(status, canvas); + break; + case STATUS_PROGRESSING: + canvas.drawArc(oval, -90, currentRotateDegrees, false, mPaint); + if (waitProgressingRunnable != null) { + waitProgressingRunnable.run(); + waitProgressingRunnable = null; + } + break; + } + } + + private void drawDoneMark(int status, Canvas canvas) { + if (rotateAnimator.getInterpolator() != interpolator) { + rotateAnimator.setInterpolator(interpolator); + } + if (tickShowRunnable != null) { + tickShowRunnable.run(); + if (DialogX.useHaptic)performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + tickShowRunnable = null; + } + switch (status) { + case STATUS_SUCCESS: + showSuccessTick(canvas); + break; + case STATUS_WARNING: + showWarningTick(canvas); + break; + case STATUS_ERROR: + showErrorTick(canvas); + break; + } + } + + private int line1X = 0; + private int line1Y = 0; + private int line2X = 0; + private int line2Y = 0; + + private int tickStep = 0; + + //绘制对号 + private void showSuccessTick(Canvas canvas) { + int tickLeftPoint = (int) (mCenterX - mRadius * 1 / 2); + int tickTurnLeftPoint = (int) (mCenterX - mRadius / 10); + int tickRightPoint = (int) (mRadius * 0.99f); + int speed = 2; + switch (tickStep) { + case 0: + if (tickLeftPoint + line1X < tickTurnLeftPoint) { + line1X = line1X + speed; + line1Y = line1Y + speed; + } else { + line2X = line1X; + line2Y = line1Y; + tickStep = 1; + } + break; + case 1: + if (line2X < tickRightPoint) { + line2X = line2X + 4; + line2Y = line2Y - 5; + } + break; + } + canvas.drawLine(tickLeftPoint, mCenterY, tickLeftPoint + line1X, mCenterY + line1Y, mPaint); + canvas.drawLine(tickLeftPoint + line1X, mCenterY + line1Y, tickLeftPoint + line2X, mCenterY + line2Y, mPaint); + + postInvalidateDelayed(1); + } + + //绘制感叹号 + private void showWarningTick(Canvas canvas) { + int tickLeftPoint = (int) mCenterX; + int line1StartY = (int) (mCenterY - mRadius * 1 / 2); + int line1EndY = (int) (mCenterY + mRadius * 1 / 8); + int line2StartY = (int) (mCenterY + mRadius * 3 / 7); + int speed = 4; + switch (tickStep) { + case 0: + if (line1Y < line1EndY - line1StartY) { + line1Y = line1Y + speed; + } else { + line1Y = line1EndY - line1StartY; + tickStep = 1; + } + break; + case 1: + if (line2Y != line2StartY) { + canvas.drawLine(tickLeftPoint, line2StartY, tickLeftPoint, line2StartY + 1, mPaint); + } + break; + } + canvas.drawLine(tickLeftPoint, line1StartY, tickLeftPoint, line1StartY + line1Y, mPaint); + postInvalidateDelayed(tickStep == 1 ? 100 : 1); + } + + //绘制错误符号 + private void showErrorTick(Canvas canvas) { + int tickLeftPoint = (int) (mCenterX - mRadius * 4 / 10); + int tickRightPoint = (int) (mCenterX + mRadius * 4 / 10); + int tickTopPoint = (int) (mCenterY - mRadius * 4 / 10); + int speed = 4; + + switch (tickStep) { + case 0: + if (tickRightPoint - line1X > tickLeftPoint) { + line1X = line1X + speed; + line1Y = line1Y + speed; + } else { + tickStep = 1; + canvas.drawLine(tickRightPoint, tickTopPoint, tickRightPoint - line1X, tickTopPoint + line1Y, mPaint); + postInvalidateDelayed(150); + return; + } + break; + case 1: + if (tickLeftPoint + line2X < tickRightPoint) { + line2X = line2X + speed; + line2Y = line2Y + speed; + } + canvas.drawLine(tickLeftPoint, tickTopPoint, tickLeftPoint + line2X, tickTopPoint + line2Y, mPaint); + break; + } + canvas.drawLine(tickRightPoint, tickTopPoint, tickRightPoint - line1X, tickTopPoint + line1Y, mPaint); + postInvalidateDelayed(1); + } + + private TimeInterpolator interpolator; + private Runnable waitProgressingRunnable; + + public void success() { + if (status == STATUS_PROGRESSING) { + progress(1f); + waitProgressingRunnable = new Runnable() { + @Override + public void run() { + tickStep = 0; + successStep = 2; + interpolator = new AccelerateDecelerateInterpolator(); + status = STATUS_SUCCESS; + } + }; + return; + } + tickStep = 0; + interpolator = new AccelerateDecelerateInterpolator(); + status = STATUS_SUCCESS; + invalidate(); + } + + public void warning() { + if (status == STATUS_PROGRESSING) { + progress(1f); + waitProgressingRunnable = new Runnable() { + @Override + public void run() { + tickStep = 0; + successStep = 2; + interpolator = new DecelerateInterpolator(2); + status = STATUS_WARNING; + } + }; + return; + } + tickStep = 0; + interpolator = new DecelerateInterpolator(2); + status = STATUS_WARNING; + invalidate(); + } + + public void error() { + if (status == STATUS_PROGRESSING) { + progress(1f); + waitProgressingRunnable = new Runnable() { + @Override + public void run() { + tickStep = 0; + successStep = 2; + interpolator = new DecelerateInterpolator(2); + status = STATUS_ERROR; + } + }; + return; + } + tickStep = 0; + interpolator = new DecelerateInterpolator(2); + status = STATUS_ERROR; + invalidate(); + } + + public void progress(float progress) { + if (rotateAnimator != null) rotateAnimator.cancel(); + if (followAnimator != null) followAnimator.cancel(); + if (status != STATUS_PROGRESSING) { + currentRotateDegrees = 0; + } + rotateAnimator = ValueAnimator.ofFloat(currentRotateDegrees, 365 * progress); + rotateAnimator.setDuration(1000); + rotateAnimator.setInterpolator(new DecelerateInterpolator(2)); + rotateAnimator.setRepeatCount(0); + rotateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + currentRotateDegrees = (float) animation.getAnimatedValue(); + invalidate(); + } + }); + rotateAnimator.start(); + status = STATUS_PROGRESSING; + } + + private Runnable tickShowRunnable; + + public NoArticulatedProgressView whenShowTick(Runnable runnable) { + tickShowRunnable = runnable; + return this; + } + + public void loading() { + noShowLoading = false; + oldAnimAngle = 0; + successStep = 0; + line1X = 0; + line1Y = 0; + line2X = 0; + line2Y = 0; + status = STATUS_LOADING; + if (rotateAnimator != null) rotateAnimator.cancel(); + if (followAnimator != null) followAnimator.cancel(); + isInited = false; + init(null); + } + + public int getStatus() { + return status; + } + + @Override + protected void onDetachedFromWindow() { + if (rotateAnimator != null) { + rotateAnimator.cancel(); + } + if (followAnimator != null) { + followAnimator.cancel(); + } + super.onDetachedFromWindow(); + } + + public int getStrokeWidth() { + return width; + } + + public NoArticulatedProgressView setStrokeWidth(int width) { + this.width = width; + if (mPaint != null) mPaint.setStrokeWidth(width); + return this; + } + + public int getColor() { + return color; + } + + public NoArticulatedProgressView setColor(int color) { + this.color = color; + if (mPaint != null) mPaint.setColor(color); + return this; + } + + private boolean noShowLoading; + + public void noLoading() { + noShowLoading = true; + } + + private int dip2px(float dpValue) { + final float scale = Resources.getSystem().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/PopMenuListView.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/PopMenuListView.java new file mode 100644 index 0000000..319aaf6 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/PopMenuListView.java @@ -0,0 +1,66 @@ +package com.kongzue.dialogx.util.views; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.BaseAdapter; +import android.widget.ListView; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2021/8/18 17:48 + */ +public class PopMenuListView extends ListView { + + private float maxHeight = -1; + + public PopMenuListView(Context context) { + super(context); + } + + public PopMenuListView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public PopMenuListView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public boolean isCanScroll() { + boolean canScroll; + int count = getCount(); + int firstVisiblePosition = getFirstVisiblePosition(); + int lastVisiblePosition = getLastVisiblePosition(); + canScroll = firstVisiblePosition != 0 || count != lastVisiblePosition + 1; + return canScroll; + } + + public float getMaxHeight() { + return maxHeight; + } + + public PopMenuListView setMaxHeight(float maxHeight) { + this.maxHeight = maxHeight; + return this; + } + + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int specSize = MeasureSpec.getSize(heightMeasureSpec); + if (maxHeight <= specSize && maxHeight > -1) { + heightMeasureSpec = MeasureSpec.makeMeasureSpec(Float.valueOf(maxHeight).intValue(), MeasureSpec.AT_MOST); + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + protected void layoutChildren() { + try { + super.layoutChildren(); + } catch (IllegalStateException e) { + ((BaseAdapter) getAdapter()).notifyDataSetChanged(); + super.layoutChildren(); + } + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/ProgressView.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/ProgressView.java new file mode 100644 index 0000000..8d2e8f3 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/ProgressView.java @@ -0,0 +1,540 @@ +package com.kongzue.dialogx.util.views; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Display; +import android.view.HapticFeedbackConstants; +import android.view.View; +import android.view.WindowManager; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; + +import androidx.annotation.Nullable; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogx.R; +import com.kongzue.dialogx.interfaces.ProgressViewInterface; +import com.kongzue.dialogx.util.DialogXValueAnimator; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/27 16:16 + * @license: Apache License 2.0 + */ +public class ProgressView extends View implements ProgressViewInterface { + + //提示动画持续时间 + public static long TIP_ANIMATOR_DURATION = 300; + //进度动画中的逐渐跟随动画时长 + public static long PROGRESSING_ANIMATOR_DURATION = 1000; + + public static final int STATUS_LOADING = 0; + public static final int STATUS_SUCCESS = 1; + public static final int STATUS_WARNING = 2; + public static final int STATUS_ERROR = 3; + public static final int STATUS_PROGRESSING = 4; + + private int status = STATUS_LOADING; + + private int width = dip2px(2); + private int color = Color.WHITE; + + public ProgressView(Context context) { + super(context); + init(null); + } + + public ProgressView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(attrs); + } + + public ProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(attrs); + } + + private DialogXValueAnimator rotateAnimator; + private DialogXValueAnimator followAnimator; + + private float currentRotateDegrees; + private float followRotateDegrees; + + //跟随点度数做正弦值变化,halfSweepAMinValue为最低相较目标点度数差值,halfSweepAMaxValue为最大相较目标点度数差值 + private float halfSweepAMaxValue = 180; + private float halfSweepAMinValue = 80; + //正弦函数的半径 + private float halfSweepA; + + Paint mPaint = new Paint(); + + private boolean isInited = false; + + private void init(AttributeSet attrs) { + synchronized (ProgressView.class) { + if (isInited) { + return; + } + isInited = true; + if (attrs != null) { + TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressView); + width = a.getDimensionPixelSize(R.styleable.ProgressView_progressStrokeWidth, dip2px(2)); + color = a.getDimensionPixelSize(R.styleable.ProgressView_progressStrokeColor, color); + + a.recycle(); + } + + mPaint.setAntiAlias(true); + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setStrokeWidth(width); + mPaint.setStrokeCap(Paint.Cap.ROUND); + mPaint.setColor(color); + + if (!isInEditMode()) { + int refreshInterval = (int) calculateMillisPerFrame(getContext()); + + halfSweepA = (halfSweepAMaxValue - halfSweepAMinValue) / 2; + + rotateAnimator = DialogXValueAnimator.ofFloat(0, 365); + rotateAnimator.setDuration(1000); + rotateAnimator.setInterpolator(new LinearInterpolator()); + rotateAnimator.setRepeatCount(-1); + rotateAnimator.setRefreshInterval(refreshInterval); + rotateAnimator.addUpdateListener(new DialogXValueAnimator.ValueUpdateListener() { + @Override + public void onValueUpdate(float animatedValue) { + if (!isAttachedToWindow()){ + return; + } + currentRotateDegrees = animatedValue; + invalidate(); + } + }); + + followAnimator = DialogXValueAnimator.ofFloat(0, 365); + followAnimator.setDuration(1500); + followAnimator.setRefreshInterval(refreshInterval); + followAnimator.setInterpolator(new LinearInterpolator()); + followAnimator.setRepeatCount(-1); + followAnimator.addUpdateListener(new DialogXValueAnimator.ValueUpdateListener() { + @Override + public void onValueUpdate(float animatedValue) { + followRotateDegrees = animatedValue; + } + }); + + followAnimator.start(); + rotateAnimator.start(); + } + } + } + + //旋转圆的中心坐标 + private float mCenterX; + private float mCenterY; + //半径 + private float mRadius = 100; + private RectF oval; + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + mCenterX = w * 1f / 2; + mCenterY = h * 1f / 2; + mRadius = Math.min(getWidth(), getHeight()) / 2 - width / 2; + oval = new RectF(mCenterX - mRadius, mCenterY - mRadius, mCenterX + mRadius, mCenterY + mRadius); + } + + private int successStep = 0; + private float nowLoadingProgressValue; + private float nowLoadingProgressEndAngle; + private float changeStatusAngle; + + @Override + protected void onDraw(Canvas canvas) { + if (isInEditMode()) { + canvas.drawArc(oval, 0, 365, false, mPaint); + return; + } + if (noShowLoading) { + canvas.drawArc(oval, 0, 365, false, mPaint); + successStep = 2; + drawDoneMark(status, canvas); + return; + } + switch (status) { + case STATUS_LOADING: + float sweepAngle = (float) (halfSweepA * Math.sin(Math.toRadians(followRotateDegrees))) + halfSweepA + halfSweepAMinValue / 2; + nowLoadingProgressValue = currentRotateDegrees - sweepAngle; + if (nowLoadingProgressValue < 0) { + nowLoadingProgressValue = 360 + nowLoadingProgressValue; + } + nowLoadingProgressEndAngle = sweepAngle; + changeStatusAngle = sweepAngle < 0 ? 360 - sweepAngle : sweepAngle; + canvas.drawArc(oval, currentRotateDegrees, -sweepAngle, false, mPaint); + break; + case STATUS_SUCCESS: + case STATUS_WARNING: + case STATUS_ERROR: + switch (successStep) { + case 0: + nowLoadingProgressEndAngle = nowLoadingProgressEndAngle + 5; + canvas.drawArc(oval, nowLoadingProgressValue, nowLoadingProgressEndAngle, false, mPaint); + + if (nowLoadingProgressEndAngle - (360 - changeStatusAngle) >= nowLoadingProgressValue) { + successStep = 1; + if (waitArticulationAnimationRunnable != null) { + waitArticulationAnimationRunnable.run(); + waitArticulationAnimationRunnable = null; + } + } + break; + case 1: + canvas.drawArc(oval, 0, 360, false, mPaint); + drawDoneMark(status, canvas); + break; + } + break; + case STATUS_PROGRESSING: + switch (successStep) { + case 0: + canvas.drawArc(oval, -90, currentRotateDegrees, false, mPaint); + if (currentRotateDegrees == 365) { + successStep = 1; + if (waitArticulationAnimationRunnable != null) { + waitArticulationAnimationRunnable.run(); + waitArticulationAnimationRunnable = null; + } + } + break; + case 1: + canvas.drawArc(oval, 0, 360, false, mPaint); + drawDoneMark(status, canvas); + break; + } + } + } + + private void drawDoneMark(int status, Canvas canvas) { + if (rotateAnimator.getInterpolator() != interpolator) { + rotateAnimator.setInterpolator(interpolator); + } + if (tickShowRunnable != null) { + tickShowRunnable.run(); + tickShowRunnable = null; + + if (DialogX.useHaptic) { + switch (status) { + case STATUS_SUCCESS: + performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + break; + case STATUS_WARNING: + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + } + }, (long) (TIP_ANIMATOR_DURATION * 0.8f)); + break; + case STATUS_ERROR: + performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + } + }, (long) (TIP_ANIMATOR_DURATION * 0.5f)); + break; + } + } + } + switch (status) { + case STATUS_SUCCESS: + showSuccessTick(canvas); + break; + case STATUS_WARNING: + showWarningTick(canvas); + break; + case STATUS_ERROR: + showErrorTick(canvas); + break; + } + } + + private int line1X = 0; + private int line1Y = 0; + private int line2X = 0; + private int line2Y = 0; + + private ValueAnimator tickAnimator; + private float tickAnimatorValue; + + //绘制对号 + private void showSuccessTick(Canvas canvas) { + int verticalAxisOffset = (int) (mRadius / 20); //纵轴向下偏移量 + int tickTurnLeftPoint = (int) (mCenterX - mRadius / 10 - verticalAxisOffset); //转折点 + int startX = (int) (mCenterX - mRadius / 2); + int startY = (int) (mCenterY + verticalAxisOffset); + int endX = (int) (mCenterX + mRadius / 2); + int tickAnimatorX = (int) (startX + ((endX - startX) * tickAnimatorValue)); + + Path path = new Path(); + path.moveTo(startX, startY); + if (tickAnimatorX < tickTurnLeftPoint) { + line1X = tickAnimatorX; + line1Y = (int) (startY + (tickAnimatorX - startX)); + path.lineTo(line1X, line1Y); + } else { + line1X = tickTurnLeftPoint; + line1Y = (int) (startY + (line1X - startX)); + path.lineTo(line1X, line1Y); + + line2X = tickAnimatorX; + line2Y = line1Y - (tickAnimatorX - line1X); + path.lineTo(line2X, line2Y); + } + canvas.drawPath(path, mPaint); + } + + //绘制感叹号 + private void showWarningTick(Canvas canvas) { + int x = (int) mCenterX; + int startY = (int) (mCenterY - mRadius * 1 / 2); + int endY = (int) (mCenterY + mRadius * 1 / 8); + int line2Y = (int) (mCenterY + mRadius * 3 / 7); + + if (tickAnimatorValue < 0.9f) { + canvas.drawLine(x, startY, x, startY + (endY - startY) * tickAnimatorValue, mPaint); + } else { + canvas.drawLine(x, startY, x, endY, mPaint); + canvas.drawLine(x, line2Y, x, line2Y + 1, mPaint); + } + } + + //绘制错误符号 + private void showErrorTick(Canvas canvas) { + int start = (int) (mCenterY - mRadius * 4 / 10); + int end = (int) (mCenterX + mRadius * 4 / 10); + + if (tickAnimatorValue < 0.5f) { + line1X = (int) (start + (tickAnimatorValue * 2) * (end - start)); + line1Y = (int) (start + (tickAnimatorValue * 2) * (end - start)); + canvas.drawLine(start, start, line1X, line1Y, mPaint); + } else { + line1X = (int) (start + (tickAnimatorValue * 2) * (end - start)); + line1Y = (int) (start + (tickAnimatorValue * 2) * (end - start)); + canvas.drawLine(start, start, end, end, mPaint); + + line2X = (int) (end - ((tickAnimatorValue - 0.5f) * 2) * (end - start)); + line2Y = (int) (start + ((tickAnimatorValue - 0.5f) * 2) * (end - start)); + canvas.drawLine(end, start, line2X, line2Y, mPaint); + } + } + + private Interpolator interpolator; + + public void success() { + if (status == STATUS_PROGRESSING) { + progress(1f); + waitArticulationAnimationRunnable = new Runnable() { + @Override + public void run() { + initTipAnimator(STATUS_SUCCESS, new AccelerateDecelerateInterpolator()); + } + }; + return; + } + initTipAnimator(STATUS_SUCCESS, new AccelerateDecelerateInterpolator()); + } + + public void warning() { + if (status == STATUS_PROGRESSING) { + progress(1f); + waitArticulationAnimationRunnable = new Runnable() { + @Override + public void run() { + initTipAnimator(STATUS_WARNING, new AccelerateInterpolator(2f)); + } + }; + return; + } + initTipAnimator(STATUS_WARNING, new AccelerateInterpolator(2f)); + } + + public void error() { + if (status == STATUS_PROGRESSING) { + progress(1f); + waitArticulationAnimationRunnable = new Runnable() { + @Override + public void run() { + initTipAnimator(STATUS_ERROR, new DecelerateInterpolator(2)); + } + }; + return; + } + initTipAnimator(STATUS_ERROR, new DecelerateInterpolator(2)); + } + + Runnable waitArticulationAnimationRunnable; //等待衔接完成后再执行 + + private void initTipAnimator(int s, Interpolator i) { + interpolator = i; + status = s; + if (successStep == 0) { + waitArticulationAnimationRunnable = new Runnable() { + @Override + public void run() { + initTipAnimator(status, interpolator); + } + }; + return; + } + + if (tickAnimator != null) { + tickAnimator.cancel(); + tickAnimator = null; + } + tickAnimatorValue = 0; + tickAnimator = ValueAnimator.ofFloat(0f, 1f); + tickAnimator.setDuration(TIP_ANIMATOR_DURATION); + tickAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + tickAnimatorValue = (float) animation.getAnimatedValue(); + invalidate(); + } + }); + tickAnimator.start(); + } + + public void progress(float progress) { + if (rotateAnimator != null) rotateAnimator.cancel(); + if (followAnimator != null) followAnimator.cancel(); + if (status != STATUS_PROGRESSING) { + currentRotateDegrees = 0; + } + noShowLoading = false; + status = STATUS_PROGRESSING; + rotateAnimator = DialogXValueAnimator.ofFloat(currentRotateDegrees, 365 * progress); + rotateAnimator.setDuration(PROGRESSING_ANIMATOR_DURATION); + rotateAnimator.setInterpolator(new DecelerateInterpolator(2)); + rotateAnimator.setRepeatCount(0); + rotateAnimator.addUpdateListener(new DialogXValueAnimator.ValueUpdateListener() { + @Override + public void onValueUpdate(float animatedValue) { + currentRotateDegrees = animatedValue; + invalidate(); + } + }); + rotateAnimator.start(); + } + + private Runnable tickShowRunnable; + + public ProgressView whenShowTick(Runnable runnable) { + tickShowRunnable = runnable; + return this; + } + + public void loading() { + noShowLoading = false; + successStep = 0; + line1X = 0; + line1Y = 0; + line2X = 0; + line2Y = 0; + status = STATUS_LOADING; + if (rotateAnimator != null) rotateAnimator.cancel(); + if (followAnimator != null) followAnimator.cancel(); + isInited = false; + init(null); + } + + public int getStatus() { + return status; + } + + @Override + protected void onDetachedFromWindow() { + if (rotateAnimator != null) { + rotateAnimator.cancel(); + } + if (followAnimator != null) { + followAnimator.cancel(); + } + super.onDetachedFromWindow(); + } + + public int getStrokeWidth() { + return width; + } + + public ProgressView setStrokeWidth(int width) { + this.width = width; + if (mPaint != null) mPaint.setStrokeWidth(width); + return this; + } + + public int getColor() { + return color; + } + + public ProgressView setColor(int color) { + this.color = color; + if (mPaint != null) mPaint.setColor(color); + return this; + } + + private boolean noShowLoading; + + public void noLoading() { + noShowLoading = true; + } + + private int dip2px(float dpValue) { + final float scale = Resources.getSystem().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + private float getRefreshRate(Context context) { + float refreshRate = 60.0f; // 默认值 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + Display display = context.getSystemService(WindowManager.class).getDefaultDisplay(); + Display.Mode mode = display.getMode(); + refreshRate = mode.getRefreshRate(); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); + refreshRate = display.getRefreshRate(); + } + + return refreshRate; + } + + private long calculateMillisPerFrame(Context context) { + float refreshRate = getRefreshRate(context); + + if (refreshRate > 0) { + return (long) (1000.0 / refreshRate); + } else { + return 16; // 获取刷新率失败 + } + } +} diff --git a/DialogX/src/main/java/com/kongzue/dialogx/util/views/RoundView.java b/DialogX/src/main/java/com/kongzue/dialogx/util/views/RoundView.java new file mode 100644 index 0000000..c457993 --- /dev/null +++ b/DialogX/src/main/java/com/kongzue/dialogx/util/views/RoundView.java @@ -0,0 +1,56 @@ +package com.kongzue.dialogx.util.views; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Path; +import android.graphics.Rect; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.widget.RelativeLayout; + +public class RoundView extends RelativeLayout { + + private float mRadius = 0; + private Path mBoundPath = null; + + public RoundView(Context context) { + this(context, null); + } + + public RoundView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public RoundView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setWillNotDraw(false); + mRadius = 50; + } + + public void setRadius(float radius) { + if (mRadius == radius) + return; + this.mRadius = radius; + postInvalidate(); + } + + public float getRadius() { + return mRadius; + } + + public void draw(Canvas canvas) { + Rect rect = new Rect(); + getLocalVisibleRect(rect); + mBoundPath = caculateRoundRectPath(rect); + canvas.clipPath(mBoundPath); + super.draw(canvas); + } + + private Path caculateRoundRectPath(Rect r) { + Path path = new Path(); + float radius = getRadius(); + float elevation = 0; + path.addRoundRect(new RectF(r.left + elevation, r.top + elevation, r.right - elevation, r.bottom - elevation), radius, radius, Path.Direction.CW); + return path; + } +} \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_alpha_enter.xml b/DialogX/src/main/res/anim/anim_dialogx_alpha_enter.xml new file mode 100644 index 0000000..a5e3c05 --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_alpha_enter.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_bottom_enter.xml b/DialogX/src/main/res/anim/anim_dialogx_bottom_enter.xml new file mode 100644 index 0000000..815d566 --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_bottom_enter.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_bottom_exit.xml b/DialogX/src/main/res/anim/anim_dialogx_bottom_exit.xml new file mode 100644 index 0000000..d60af3f --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_bottom_exit.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_default_alpha_enter.xml b/DialogX/src/main/res/anim/anim_dialogx_default_alpha_enter.xml new file mode 100644 index 0000000..f42dab9 --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_default_alpha_enter.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_default_enter.xml b/DialogX/src/main/res/anim/anim_dialogx_default_enter.xml new file mode 100644 index 0000000..6d3cae4 --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_default_enter.xml @@ -0,0 +1,18 @@ + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_default_exit.xml b/DialogX/src/main/res/anim/anim_dialogx_default_exit.xml new file mode 100644 index 0000000..9b62ada --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_default_exit.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_left_enter.xml b/DialogX/src/main/res/anim/anim_dialogx_left_enter.xml new file mode 100644 index 0000000..d3b1c6a --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_left_enter.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_left_exit.xml b/DialogX/src/main/res/anim/anim_dialogx_left_exit.xml new file mode 100644 index 0000000..60fe2ab --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_left_exit.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_notification_enter.xml b/DialogX/src/main/res/anim/anim_dialogx_notification_enter.xml new file mode 100644 index 0000000..2c77041 --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_notification_enter.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_notification_exit.xml b/DialogX/src/main/res/anim/anim_dialogx_notification_exit.xml new file mode 100644 index 0000000..93e7244 --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_notification_exit.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_right_enter.xml b/DialogX/src/main/res/anim/anim_dialogx_right_enter.xml new file mode 100644 index 0000000..3746a76 --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_right_enter.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_right_exit.xml b/DialogX/src/main/res/anim/anim_dialogx_right_exit.xml new file mode 100644 index 0000000..be93c12 --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_right_exit.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_top_enter.xml b/DialogX/src/main/res/anim/anim_dialogx_top_enter.xml new file mode 100644 index 0000000..95efebb --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_top_enter.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/anim/anim_dialogx_top_exit.xml b/DialogX/src/main/res/anim/anim_dialogx_top_exit.xml new file mode 100644 index 0000000..771dbfa --- /dev/null +++ b/DialogX/src/main/res/anim/anim_dialogx_top_exit.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable-v21/button_dialogx_material_light.xml b/DialogX/src/main/res/drawable-v21/button_dialogx_material_light.xml new file mode 100644 index 0000000..65e5908 --- /dev/null +++ b/DialogX/src/main/res/drawable-v21/button_dialogx_material_light.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable-v21/button_dialogx_material_night.xml b/DialogX/src/main/res/drawable-v21/button_dialogx_material_night.xml new file mode 100644 index 0000000..e2f8fb7 --- /dev/null +++ b/DialogX/src/main/res/drawable-v21/button_dialogx_material_night.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/button_dialogx_material_light.xml b/DialogX/src/main/res/drawable/button_dialogx_material_light.xml new file mode 100644 index 0000000..8cb8c1d --- /dev/null +++ b/DialogX/src/main/res/drawable/button_dialogx_material_light.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/button_dialogx_material_night.xml b/DialogX/src/main/res/drawable/button_dialogx_material_night.xml new file mode 100644 index 0000000..5157816 --- /dev/null +++ b/DialogX/src/main/res/drawable/button_dialogx_material_night.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_defalut_edittxt_cursor.xml b/DialogX/src/main/res/drawable/rect_dialogx_defalut_edittxt_cursor.xml new file mode 100644 index 0000000..6bdd322 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_defalut_edittxt_cursor.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_low_api_material_button_press.xml b/DialogX/src/main/res/drawable/rect_dialogx_low_api_material_button_press.xml new file mode 100644 index 0000000..07c0254 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_low_api_material_button_press.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_low_api_material_button_press_night.xml b/DialogX/src/main/res/drawable/rect_dialogx_low_api_material_button_press_night.xml new file mode 100644 index 0000000..d41f3a9 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_low_api_material_button_press_night.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_bkg_light.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_bkg_light.xml new file mode 100644 index 0000000..505db92 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_bkg_light.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_bkg_night.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_bkg_night.xml new file mode 100644 index 0000000..a90496c --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_bkg_night.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_bottom_bkg_light.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_bottom_bkg_light.xml new file mode 100644 index 0000000..e500950 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_bottom_bkg_light.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_bottom_bkg_night.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_bottom_bkg_night.xml new file mode 100644 index 0000000..edc0eaf --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_bottom_bkg_night.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_button_light_forword.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_button_light_forword.xml new file mode 100644 index 0000000..261c420 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_button_light_forword.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_button_night_forword.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_button_night_forword.xml new file mode 100644 index 0000000..518437f --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_button_night_forword.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_dialogtap.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_dialogtap.xml new file mode 100644 index 0000000..07c0254 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_dialogtap.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_dialogtap_night.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_dialogtap_night.xml new file mode 100644 index 0000000..d41f3a9 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_dialogtap_night.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_menu_split_divider.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_menu_split_divider.xml new file mode 100644 index 0000000..aa97988 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_menu_split_divider.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_menu_split_divider_night.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_menu_split_divider_night.xml new file mode 100644 index 0000000..29b7e21 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_menu_split_divider_night.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_popnotification_bkg.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_popnotification_bkg.xml new file mode 100644 index 0000000..db95930 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_popnotification_bkg.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_popnotification_bkg_night.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_popnotification_bkg_night.xml new file mode 100644 index 0000000..e367970 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_popnotification_bkg_night.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_poptip_bkg.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_poptip_bkg.xml new file mode 100644 index 0000000..db95930 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_poptip_bkg.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_poptip_bkg_night.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_poptip_bkg_night.xml new file mode 100644 index 0000000..e367970 --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_poptip_bkg_night.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/DialogX/src/main/res/drawable/rect_dialogx_material_wait_bkg.xml b/DialogX/src/main/res/drawable/rect_dialogx_material_wait_bkg.xml new file mode 100644 index 0000000..96091ee --- /dev/null +++ b/DialogX/src/main/res/drawable/rect_dialogx_material_wait_bkg.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/DialogX/src/main/res/layout/item_dialogx_material_bottom_menu_normal_text.xml b/DialogX/src/main/res/layout/item_dialogx_material_bottom_menu_normal_text.xml new file mode 100644 index 0000000..b6aaab6 --- /dev/null +++ b/DialogX/src/main/res/layout/item_dialogx_material_bottom_menu_normal_text.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/item_dialogx_material_context_menu_normal_text.xml b/DialogX/src/main/res/layout/item_dialogx_material_context_menu_normal_text.xml new file mode 100644 index 0000000..7fad54e --- /dev/null +++ b/DialogX/src/main/res/layout/item_dialogx_material_context_menu_normal_text.xml @@ -0,0 +1,34 @@ + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_bottom_material.xml b/DialogX/src/main/res/layout/layout_dialogx_bottom_material.xml new file mode 100644 index 0000000..99fd5ef --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_bottom_material.xml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_bottom_material_dark.xml b/DialogX/src/main/res/layout/layout_dialogx_bottom_material_dark.xml new file mode 100644 index 0000000..3b74ade --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_bottom_material_dark.xml @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_custom.xml b/DialogX/src/main/res/layout/layout_dialogx_custom.xml new file mode 100644 index 0000000..ac85bb7 --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_custom.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_empty.xml b/DialogX/src/main/res/layout/layout_dialogx_empty.xml new file mode 100644 index 0000000..24f3d6b --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_empty.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_fullscreen.xml b/DialogX/src/main/res/layout/layout_dialogx_fullscreen.xml new file mode 100644 index 0000000..6366b9c --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_fullscreen.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/DialogX/src/main/res/layout/layout_dialogx_fullscreen_dark.xml b/DialogX/src/main/res/layout/layout_dialogx_fullscreen_dark.xml new file mode 100644 index 0000000..ea065c0 --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_fullscreen_dark.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/DialogX/src/main/res/layout/layout_dialogx_material.xml b/DialogX/src/main/res/layout/layout_dialogx_material.xml new file mode 100644 index 0000000..3df864f --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_material.xml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_material_dark.xml b/DialogX/src/main/res/layout/layout_dialogx_material_dark.xml new file mode 100644 index 0000000..ce3c4b3 --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_material_dark.xml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_popmenu_material.xml b/DialogX/src/main/res/layout/layout_dialogx_popmenu_material.xml new file mode 100644 index 0000000..241e249 --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_popmenu_material.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_popmenu_material_dark.xml b/DialogX/src/main/res/layout/layout_dialogx_popmenu_material_dark.xml new file mode 100644 index 0000000..297cc75 --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_popmenu_material_dark.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_popnotification_material.xml b/DialogX/src/main/res/layout/layout_dialogx_popnotification_material.xml new file mode 100644 index 0000000..7c1f0e1 --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_popnotification_material.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_popnotification_material_dark.xml b/DialogX/src/main/res/layout/layout_dialogx_popnotification_material_dark.xml new file mode 100644 index 0000000..e76fce1 --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_popnotification_material_dark.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_poptip_material.xml b/DialogX/src/main/res/layout/layout_dialogx_poptip_material.xml new file mode 100644 index 0000000..365e34d --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_poptip_material.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_poptip_material_dark.xml b/DialogX/src/main/res/layout/layout_dialogx_poptip_material_dark.xml new file mode 100644 index 0000000..0fc708e --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_poptip_material_dark.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/layout/layout_dialogx_wait.xml b/DialogX/src/main/res/layout/layout_dialogx_wait.xml new file mode 100644 index 0000000..b9ac007 --- /dev/null +++ b/DialogX/src/main/res/layout/layout_dialogx_wait.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/mipmap-xxhdpi/ico_dialogx_error.png b/DialogX/src/main/res/mipmap-xxhdpi/ico_dialogx_error.png new file mode 100644 index 0000000..73f17eb Binary files /dev/null and b/DialogX/src/main/res/mipmap-xxhdpi/ico_dialogx_error.png differ diff --git a/DialogX/src/main/res/mipmap-xxhdpi/ico_dialogx_success.png b/DialogX/src/main/res/mipmap-xxhdpi/ico_dialogx_success.png new file mode 100644 index 0000000..76e9ca7 Binary files /dev/null and b/DialogX/src/main/res/mipmap-xxhdpi/ico_dialogx_success.png differ diff --git a/DialogX/src/main/res/mipmap-xxhdpi/ico_dialogx_warning.png b/DialogX/src/main/res/mipmap-xxhdpi/ico_dialogx_warning.png new file mode 100644 index 0000000..b50b115 Binary files /dev/null and b/DialogX/src/main/res/mipmap-xxhdpi/ico_dialogx_warning.png differ diff --git a/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_multi_selection.png b/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_multi_selection.png new file mode 100644 index 0000000..feebea1 Binary files /dev/null and b/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_multi_selection.png differ diff --git a/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_non_multi_select.png b/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_non_multi_select.png new file mode 100644 index 0000000..2b23ee6 Binary files /dev/null and b/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_non_multi_select.png differ diff --git a/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_non_select.png b/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_non_select.png new file mode 100644 index 0000000..005c878 Binary files /dev/null and b/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_non_select.png differ diff --git a/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_selection.png b/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_selection.png new file mode 100644 index 0000000..fd1d9d6 Binary files /dev/null and b/DialogX/src/main/res/mipmap-xxhdpi/img_dialogx_bottom_menu_material_item_selection.png differ diff --git a/DialogX/src/main/res/mipmap-xxhdpi/img_drawable_down.png b/DialogX/src/main/res/mipmap-xxhdpi/img_drawable_down.png new file mode 100644 index 0000000..42057f6 Binary files /dev/null and b/DialogX/src/main/res/mipmap-xxhdpi/img_drawable_down.png differ diff --git a/DialogX/src/main/res/values-v21/styles.xml b/DialogX/src/main/res/values-v21/styles.xml new file mode 100644 index 0000000..30ee2b0 --- /dev/null +++ b/DialogX/src/main/res/values-v21/styles.xml @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/values/attrs.xml b/DialogX/src/main/res/values/attrs.xml new file mode 100644 index 0000000..dfc2a32 --- /dev/null +++ b/DialogX/src/main/res/values/attrs.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/values/colors.xml b/DialogX/src/main/res/values/colors.xml new file mode 100644 index 0000000..bc44d77 --- /dev/null +++ b/DialogX/src/main/res/values/colors.xml @@ -0,0 +1,42 @@ + + + #2196F3 + + #353535 + #000 + #E6000000 + #CC000000 + #BF000000 + #B3000000 + #99000000 + #80000000 + #66000000 + #4D000000 + #40000000 + #33000000 + #1A000000 + #0D000000 + #00000000 + + #fff + #E6ffffff + #CCffffff + #BFffffff + #B3ffffff + #99ffffff + #80ffffff + #66ffffff + #4Dffffff + #40ffffff + #33ffffff + #1Affffff + #0DFFFFFF + + #2196F3 + + #343434 + #F5F6F7 + #232323 + + #86B3D6 + \ No newline at end of file diff --git a/DialogX/src/main/res/values/ids.xml b/DialogX/src/main/res/values/ids.xml new file mode 100644 index 0000000..50fa4bf --- /dev/null +++ b/DialogX/src/main/res/values/ids.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/DialogX/src/main/res/values/styles.xml b/DialogX/src/main/res/values/styles.xml new file mode 100644 index 0000000..8a15e21 --- /dev/null +++ b/DialogX/src/main/res/values/styles.xml @@ -0,0 +1,37 @@ + + + + + + + + \ No newline at end of file diff --git a/DialogXInterface/.gitignore b/DialogXInterface/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/DialogXInterface/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/DialogXInterface/build.gradle b/DialogXInterface/build.gradle new file mode 100644 index 0000000..2ef58bd --- /dev/null +++ b/DialogXInterface/build.gradle @@ -0,0 +1,8 @@ +apply plugin: 'java-library' + +dependencies { + compileOnly files('libs\\android.jar') +} + +sourceCompatibility = "1.8" +targetCompatibility = "1.8" \ No newline at end of file diff --git a/DialogXInterface/libs/android.jar b/DialogXInterface/libs/android.jar new file mode 100644 index 0000000..5e69644 Binary files /dev/null and b/DialogXInterface/libs/android.jar differ diff --git a/DialogXInterface/src/main/java/com/kongzue/dialogx/interfaces/DialogXStyle.java b/DialogXInterface/src/main/java/com/kongzue/dialogx/interfaces/DialogXStyle.java new file mode 100644 index 0000000..3fa4243 --- /dev/null +++ b/DialogXInterface/src/main/java/com/kongzue/dialogx/interfaces/DialogXStyle.java @@ -0,0 +1,627 @@ +package com.kongzue.dialogx.interfaces; + +import android.content.Context; + +public abstract class DialogXStyle { + + /** + * DialogXStyle 版本 + * 相关文档请参阅:https://github.com/kongzue/DialogX/wiki/%E8%87%AA%E5%AE%9A%E4%B9%89-DialogX-%E4%B8%BB%E9%A2%98 + */ + public static final int styleVer = 6; + + /** + * 按钮类型常量 + */ + public static final int BUTTON_OK = 1; + public static final int BUTTON_CANCEL = 2; + public static final int BUTTON_OTHER = 3; + public static final int SPACE = 4; + public static final int SPLIT = 5; + + /** + * 重写基础对话框布局资源 + *

+ * 重写 layout 方法用于设置默认的消息对话框的 layout 布局文件,其中 light 参数用于判断是否为亮色模式, + * 你可以在这里查看亮色默认主题布局文件的实现 和 暗色默认主题布局文件的实现, + * 请参照 Demo 布局的格式进行布局设计,不建议修改或去掉布局中的含有 id 的组件。 + * + * @param light 亮暗色 + * @return 布局资源 id + */ + public int layout(boolean light) { + return 0; + } + + /** + * 修改默认对话框启动动画效果 + *

+ * 你可以自定义消息对话框的启动动画效果,当你return 0时则采用默认实现, + * 要自定义动画文件,可以参考:默认对话框启动动画文件 https://github.com/kongzue/DialogX/blob/master/DialogX/src/main/res/anim/anim_dialogx_default_enter.xml + * + * @return 布局资源 id + */ + public int enterAnimResId() { + return 0; + } + + /** + * 修改默认对话框关闭动画效果 + *

+ * 你可以自定义消息对话框的关闭动画效果,当你return 0时则采用默认实现, + * 要自定义动画文件,可以参考:默认对话框启动动画文件 https://github.com/kongzue/DialogX/blob/master/DialogX/src/main/res/anim/anim_dialogx_default_exit.xml + * + * @return 布局资源 id + */ + public int exitAnimResId() { + return 0; + } + + /** + * 修改默认按钮排序(纵向) + *

+ * 在特殊情况下,你可以调整对话框的按钮布局顺序,例如,在横向时,我们一般采用“其他、间隔、取消、确定”的逻辑,要修改按钮排序顺序,你可以重写以下接口。 + *

+ * eg. new int[]{BUTTON_OTHER, SPLIT, BUTTON_CANCEL, BUTTON_OK} + * + * @return 排序数组 + */ + public int[] verticalButtonOrder() { + return new int[]{BUTTON_OTHER, SPLIT, BUTTON_CANCEL, BUTTON_OK}; + } + + /** + * 修改默认按钮排序(横向) + *

+ * 在特殊情况下,你可以调整对话框的按钮布局顺序,例如,在纵向时,我们可以调整为“确定、其他、取消”的顺序,要修改按钮排序顺序,你可以重写以下接口。 + *

+ * eg. new int[]{BUTTON_OTHER, BUTTON_CANCEL, BUTTON_OK} + * + * @return 排序数组 + */ + public int[] horizontalButtonOrder() { + return new int[]{BUTTON_OTHER, BUTTON_CANCEL, BUTTON_OK}; + } + + /** + * 按钮分割线宽度 + *

+ * 需要调整分隔线的宽度,可以重写以下接口进行设置,这些设置也可以使用 return 0不进行设置。 + * + * @return 像素值 + */ + public int splitWidthPx() { + return 1; + } + + /** + * 按钮分割线颜色 + *

+ * 需要调整分隔线的颜色,可以重写以下接口进行设置,这些设置也可以使用 return 0不进行设置。 + * + * @return 颜色资源 id + */ + public int splitColorRes(boolean light) { + return 0x1A000000; + } + + /** + * 模糊背景 + *

+ * 默认对话框支持模糊化背景,如果需要模糊的背景效果,你可以重写以下接口: + * + * @return BlurBackgroundSetting + */ + public BlurBackgroundSetting messageDialogBlurSettings() { + return null; + } + + /** + * 自定义按钮样式 + *

+ * 一些主题要求我们在显示按钮时提供不同的样式,你可以通过两个接口来进行自定义:当对话框按钮处于横向/纵向时, + * 或者当按钮显示数量为1个、2个或3个时,呈现不同的样式。 + *

+ * 例如 iOS 主题,横向且只显示一个按钮(OkButton)时,需要左下角和右下角都为圆角的按钮样式, + * 当显示多个按钮时则为仅右下角为圆角的按钮样式,如果需要基于不同的情况进行相应按钮样式调整,你可以重写以下接口: + * + * @return HorizontalButtonRes + */ + public HorizontalButtonRes overrideHorizontalButtonRes() { + return null; + } + + public VerticalButtonRes overrideVerticalButtonRes() { + return null; + } + +// /** +// * 消息对话框正文文本 UI 定义 +// * +// * @return null 代表使用默认定义 +// */ +// public UIAttributeDefinition messageDialogMessageTextViewUIAD() { +// return null; +// } +// +// /** +// * InputDialog 输入框 UI 定义 +// * +// * @return null 代表使用默认定义 +// */ +// public UIAttributeDefinition inputEditTextUIAD() { +// return null; +// } + + /** + * 修改等待/提示框的样式 + *

+ * 如果需要修改等待/提示框的样式效果,你可以重写这个接口: + * + * @return WaitTipRes + */ + public WaitTipRes overrideWaitTipRes() { + return null; + } + + /** + * 自定义底部对话框/菜单样式 + *

+ * 底部对话框是 DialogX 中第二个具有丰富功能的组件,您可以实现overrideBottomDialogRes()方法以自定义底部对话框的样式细节。 + * 同样的,当return null 时使用默认样式(Material 主题)。 + * + * @return BottomDialogRes + */ + public BottomDialogRes overrideBottomDialogRes() { + return null; + } + + /** + * 自定义 PopTip 样式 + * + * @return PopTipSettings + */ + public PopTipSettings popTipSettings() { + return null; + } + + /** + * 自定义 PopNotificationSettings 样式 + * + * @return PopNotificationSettings + */ + public PopNotificationSettings popNotificationSettings() { + return null; + } + + /** + * 自定义 PopMenu 样式 + * + * @return PopMenuSettings + */ + public PopMenuSettings popMenuSettings() { + return null; + } + + /** + * 模糊背景设置 + *

+ * 如果不需要可以return null进行默认处理。 + *

+ * 此接口需要重写一个BlurBackgroundSetting,其中包含三个子接口, + * 分别是blurBackground()用于判断是否开启模糊, + * blurForwardColorRes(boolean light)用于处理前景色(建议设置一定的透明度保证可以看到背后的模糊效果), + * blurBackgroundRoundRadiusPx()用于给定模糊效果的圆角半径,单位为像素(Px)。 + */ + public abstract class BlurBackgroundSetting { + + public boolean blurBackground() { + return false; + } + + public int blurForwardColorRes(boolean light) { + return 0; + } + + public int blurBackgroundRoundRadiusPx() { + return 0; + } + } + + /** + * 自定义按钮样式设置 + *

+ * 其中 visibleButtonCount 参数为当前显示按钮的数量,light参数为当前对话框的亮/暗色模式。 + *

+ * drawable 只接受 xml 配置,可以定义不同状态时的按钮样式效果,如有需要请参考 iOS drawable 样式 + *

+ * overrideHorizontalButtonRes和overrideVerticalButtonRes返回值return null时默认不进行样式修改设置。 + */ + public abstract class HorizontalButtonRes { + + public int overrideHorizontalOkButtonBackgroundRes(int visibleButtonCount, boolean light) { + return 0; + } + + public int overrideHorizontalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) { + return 0; + } + + public int overrideHorizontalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) { + return 0; + } + } + + public abstract class VerticalButtonRes { + + public int overrideVerticalOkButtonBackgroundRes(int visibleButtonCount, boolean light) { + return 0; + } + + public int overrideVerticalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) { + return 0; + } + + public int overrideVerticalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) { + return 0; + } + } + + /** + * 修改等待/提示框的样式设置 + *

+ * 此接口return null时采用默认样式。 + */ + public abstract class WaitTipRes { + + //重写布局资源 + public int overrideWaitLayout(boolean light) { + return 0; + } + + //设置圆角像素 + public int overrideRadiusPx() { + return 0; + } + + //判断是否需要模糊背景效果 + public boolean blurBackground() { + return false; + } + + //重新设置前景色 + public int overrideBackgroundColorRes(boolean light) { + return 0; + } + + //设置在亮/暗色时的文字颜色,注意,此颜色也将修改进度动画的颜色。 + public int overrideTextColorRes(boolean light) { + return 0; + } + + /** + * 自定义等待提示动画组件接口 ProgressViewInterface + *

+ * 需要在自定义的 View 中实现这些方法,以实现在等待提示框处于不同状态时调整显示的样式。 + * 需要注意的是,默认等待提示框在加载中到完成/警告/错误等状态时,会有衔接过渡的过程,这个过程导致目标状态可能会延后几十到几百毫秒后才真正切换,因此有一个whenShowTick(Runnable runnable)接口需要实现,在衔接过程结束后执行runnable.run()即可,若无衔接过程,直接在重写后的方法中执行runnable.run()即可。 + * 在亮暗色模式切换后,等待提示框组件应该遵循颜色的变化调整颜色,这个颜色会从setColor(int color)接口中给出,注意此处的参数color是色值,并非资源值,建议直接赋值给画笔 Paint 进行绘制操作。 + * 如需参照 Demo,您可以查看 iOS 样式的 ProgressView 实现:ProgressView.java + *

+ * //停止加载动画 + * noLoading(); + *

+ * //切换至完成状态 + * success(); + *

+ * //切换至警告状态 + * warning(); + *

+ * //切换至错误状态 + * error(); + *

+ * //切换至进度(取值0f-1f) + * progress(floatprogress); + *

+ * //切换至加载状态 + * loading(); + *

+ * //不同状态切换时,衔接动画完成后执行 + * ProgressViewInterface whenShowTick(Runnablerunnable); + *

+ * //设置颜色 + * ProgressViewInterface setColor(intcolor); + * + * @param context 上下文 + * @param light 是否为亮色模式 + * @return ProgressViewInterface + */ + public ProgressViewInterface overrideWaitView(Context context, boolean light) { + return null; + } + } + + /** + * 自定义底部对话框/菜单样式设置 + *

+ *

+ * overrideSelectionMenuBackgroundColor(boolean light)接口用于 + *

+ * selectionImageTint(boolean light)接口 + *

+ * overrideSelectionImage(boolean light, boolean isSelected)用于 + */ + public abstract class BottomDialogRes { + + //定义是否支持滑动关闭操作。 + public boolean touchSlide() { + return false; + } + + //用于设置底部对话框的布局,如需修改布局样式,请参照 底部对话框默认亮色布局 和 底部对话框默认暗色布局,请参照 Demo 布局的格式进行布局设计,不建议修改或去掉布局中的含有 id 的组件,当return 0时使用默认实现。 + public int overrideDialogLayout(boolean light) { + return 0; + } + + //用于修改默认分隔线的粗细,单位像素。 + public int overrideMenuDividerDrawableRes(boolean light) { + return 0; + } + + //修改默认分隔线的粗细,单位像素。 + public int overrideMenuDividerHeight(boolean light) { + return 0; + } + + //修改默认菜单文字的颜色,值采用为 color 的资源 ID。 + public int overrideMenuTextColor(boolean light) { + return 0; + } + + //设置默认情况下,当底部对话框内容大于屏幕可显示高度时, + //默认启动后显示的高度比例,值为浮点型,例如设置为 0.6f 时,则当内容大于可显示高度时,启动后对话框只从屏幕底部弹出 0.6×屏幕高度的大小,需要再次向上拖拽才能展开全部对话框, + //此功能需要和 touchSlide() 配合使用。 + public float overrideBottomDialogMaxHeight() { + return 0; + } + + /** + * 定义菜单条目的布局样式。 + * 例如当使用 iOS 样式时,第一条菜单默认采用左上角和右上角都为圆角的样式,当显示菜单标题、正文或自定义布局时, + * 则第一条菜单使用无圆角样式。当index == count - 1时为最后一个菜单,使用 iOS 样式时, + * 最后一个菜单应该采用左下角和右下角都为圆角的样式。菜单的布局请参照 底部菜单亮色样式参考布局 和 底部菜单暗色样式参考布局 。 + *

+ * 此接口return 0时使用默认实现。 + * + * @param light 判断亮/暗色模式 + * @param index 当前菜单项的索引值 + * @param count 菜单数量 + * @param isContentVisibility 确认当菜单显示时,是否还有其他内容显示(例如对话框标题、正文或自定义布局) + * @return 条目布局资源 + */ + public int overrideMenuItemLayout(boolean light, int index, int count, boolean isContentVisibility) { + return 0; + } + + //定义已选中的菜单默认背景颜色, + // 例如在使用 MIUI 主题样式且开启了单选模式时,默认打开菜单后会选中上次已选择的条目,此接口用预设定已选中菜单的背景颜色。 + public int overrideSelectionMenuBackgroundColor(boolean light) { + return 0; + } + + //用于确定使用此主题时,默认会不会重定义图标的颜色, + // 若开启,那么所有菜单图标会根据主题的亮/暗色的文字颜色重新覆盖颜色,若关闭,则使用图标原本的颜色。 + public boolean selectionImageTint(boolean light) { + return false; + } + + //设置默认单选菜单已选择/未选择时的图标资源,可使用 mipmap 图像或者 drawable 资源。 + public int overrideSelectionImage(boolean light, boolean isSelected) { + return 0; + } + + //设置默认多选菜单已选择/未选择时的图标资源,可使用 mipmap 图像或者 drawable 资源。 + public int overrideMultiSelectionImage(boolean light, boolean isSelected) { + return 0; + } + } + + /** + * 自定义 PopTip 样式设置 + *

+ * 当return null 时使用默认样式。 + */ + public abstract static class PopTipSettings { + + //PopTip 的默认布局样式,请参考具体布局实现:PopTip 默认亮色布局 和 PopTip 默认暗色布局 + public int layout(boolean light) { + return 0; + } + + /** + * align()接口用于判断 PopTip 的弹出规则,支持的值如下: + */ + public ALIGN align() { + return ALIGN.CENTER; + } + + public enum ALIGN { + CENTER, //屏幕中央弹出 + TOP, //屏幕顶端弹出(非安全区) + BOTTOM, //屏幕底部弹出(非安全区) + TOP_INSIDE, //屏幕顶端安全区内弹出 + BOTTOM_INSIDE //屏幕底部安全区内弹出 + } + + //设置启动动画效果。 + public int enterAnimResId(boolean light) { + return 0; + } + + //设置关闭动画效果。 + public int exitAnimResId(boolean light) { + return 0; + } + + //使图标颜色和文字保持一致 + public boolean tintIcon() { + return true; + } + + //默认完成图标 + public int defaultIconSuccess() { + return 0; + } + + //默认警告图标 + public int defaultIconWarning() { + return 0; + } + + //默认错误图标 + public int defaultIconError() { + return 0; + } + } + + public abstract class PopMenuSettings { + + //PopMenu 的默认布局样式,请参考具体布局实现:PopMenu 默认亮色布局 和 PopMenu 默认暗色布局 + public int layout(boolean light) { + return 0; + } + + public BlurBackgroundSetting blurBackgroundSettings() { + return null; + } + + public int backgroundMaskColorRes() { + return 0; + } + + //用于修改默认分隔线的粗细,单位像素。 + public int overrideMenuDividerDrawableRes(boolean light) { + return 0; + } + + //修改默认分隔线的粗细,单位像素。 + public int overrideMenuDividerHeight(boolean light) { + return 0; + } + + //修改默认菜单文字的颜色,值采用为 color 的资源 ID。 + public int overrideMenuTextColor(boolean light) { + return 0; + } + + //自定义菜单的布局资源 ID。 + public int overrideMenuItemLayoutRes(boolean light) { + return 0; + } + + /** + * 定义菜单条目的布局背景资源。 + * 例如当使用 iOS 样式时,第一条菜单默认采用左上角和右上角都为圆角的样式,当显示菜单标题、正文或自定义布局时, + * 则第一条菜单背景使用无圆角样式。当index == count - 1时为最后一个菜单,使用 iOS 样式时, + * 最后一个菜单应该采用左下角和右下角都为圆角的背景样式。菜单的布局请参照 底部菜单亮色样式参考布局 和 底部菜单暗色样式参考布局 。 + *

+ * 此接口return 0时使用默认实现。 + * + * @param light 判断亮/暗色模式 + * @param index 当前菜单项的索引值 + * @param count 菜单数量 + * @param isContentVisibility 确认当菜单显示时,是否还有其他内容显示(例如对话框标题、正文或自定义布局) + * @return 条目布局背景资源 + */ + public int overrideMenuItemBackgroundRes(boolean light, int index, int count, boolean isContentVisibility) { + return 0; + } + + //定义已选中的菜单默认背景颜色, + // 例如在使用 MIUI 主题样式且开启了单选模式时,默认打开菜单后会选中上次已选择的条目,此接口用预设定已选中菜单的背景颜色。 + public int overrideSelectionMenuBackgroundColor(boolean light) { + return 0; + } + + //用于确定使用此主题时,默认会不会重定义图标的颜色, + // 若开启,那么所有菜单图标会根据主题的亮/暗色的文字颜色重新覆盖颜色,若关闭,则使用图标原本的颜色。 + public boolean selectionImageTint(boolean light) { + return false; + } + + //PopMenu 的顶部和底部的额外 padding + public int paddingVertical() { + return 0; + } + } + + /** + * 自定义 PopNotificationSettings 样式设置 + *

+ * 当return null 时使用默认样式。 + */ + public abstract static class PopNotificationSettings { + + //PopNotificationSettings 的默认布局样式,请参考具体布局实现:PopNotificationSettings 默认亮色布局 和 PopNotificationSettings 默认暗色布局 + public int layout(boolean light) { + return 0; + } + + /** + * align()接口用于判断 PopNotificationSettings 的弹出规则,支持的值如下: + */ + public ALIGN align() { + return ALIGN.CENTER; + } + + public enum ALIGN { + CENTER, //屏幕中央弹出 + TOP, //屏幕顶端弹出(非安全区) + BOTTOM, //屏幕底部弹出(非安全区) + TOP_INSIDE, //屏幕顶端安全区内弹出 + BOTTOM_INSIDE //屏幕底部安全区内弹出 + } + + //设置启动动画效果。 + public int enterAnimResId(boolean light) { + return 0; + } + + //设置关闭动画效果。 + public int exitAnimResId(boolean light) { + return 0; + } + + //使图标颜色和文字保持一致 + public boolean tintIcon() { + return true; + } + + //默认完成图标 + public int defaultIconSuccess() { + return 0; + } + + //默认警告图标 + public int defaultIconWarning() { + return 0; + } + + //默认错误图标 + public int defaultIconError() { + return 0; + } + + public BlurBackgroundSetting blurBackgroundSettings() { + return null; + } + } + + /** + * 是否需要在设置对话框背景色时对按钮背景也进行染色 + * + * @return false 不进行染色 + */ + public boolean tintButtonBackground() { + return false; + } +} \ No newline at end of file diff --git a/DialogXInterface/src/main/java/com/kongzue/dialogx/interfaces/ProgressViewInterface.java b/DialogXInterface/src/main/java/com/kongzue/dialogx/interfaces/ProgressViewInterface.java new file mode 100644 index 0000000..b4b14ad --- /dev/null +++ b/DialogXInterface/src/main/java/com/kongzue/dialogx/interfaces/ProgressViewInterface.java @@ -0,0 +1,35 @@ +package com.kongzue.dialogx.interfaces; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/11/3 20:39 + */ +public interface ProgressViewInterface { + + //停止加载动画 + void noLoading(); + + //切换至完成状态 + void success(); + + //切换至警告状态 + void warning(); + + //切换至错误状态 + void error(); + + //切换至进度(取值 0f-1f) + void progress(float progress); + + //切换至加载状态 + void loading(); + + //不同状态切换时,衔接动画完成后执行 + ProgressViewInterface whenShowTick(Runnable runnable); + + //设置颜色 + ProgressViewInterface setColor(int color); +} diff --git a/DialogXMaterialYou/.gitignore b/DialogXMaterialYou/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/DialogXMaterialYou/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/DialogXMaterialYou/build.gradle b/DialogXMaterialYou/build.gradle new file mode 100644 index 0000000..5d57144 --- /dev/null +++ b/DialogXMaterialYou/build.gradle @@ -0,0 +1,33 @@ +plugins { + id 'com.android.library' +} + +android { + compileSdkVersion 33 + + defaultConfig { + minSdkVersion 21 + targetSdkVersion 33 + versionCode 1 + versionName "1.0" + + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + implementation project(path: ':DialogX') + implementation 'androidx.appcompat:appcompat:1.4.1' + compileOnly project(path: ':DialogXInterface') +} \ No newline at end of file diff --git a/DialogXMaterialYou/consumer-rules.pro b/DialogXMaterialYou/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/DialogXMaterialYou/proguard-rules.pro b/DialogXMaterialYou/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/DialogXMaterialYou/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/AndroidManifest.xml b/DialogXMaterialYou/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8ae6a9f --- /dev/null +++ b/DialogXMaterialYou/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/java/com/kongzue/dialogxmaterialyou/style/MaterialYouStyle.java b/DialogXMaterialYou/src/main/java/com/kongzue/dialogxmaterialyou/style/MaterialYouStyle.java new file mode 100644 index 0000000..5f3b94e --- /dev/null +++ b/DialogXMaterialYou/src/main/java/com/kongzue/dialogxmaterialyou/style/MaterialYouStyle.java @@ -0,0 +1,336 @@ +package com.kongzue.dialogxmaterialyou.style; + +import android.content.Context; + +import com.kongzue.dialogx.interfaces.DialogXStyle; +import com.kongzue.dialogx.interfaces.ProgressViewInterface; +import com.kongzue.dialogx.util.views.ProgressView; +import com.kongzue.dialogxmaterialyou.R; + +/** + * @author: Kongzue + * @github: https://github.com/kongzue/ + * @homepage: http://kongzue.com/ + * @mail: myzcxhh@live.cn + * @createTime: 2020/9/26 13:09 + */ +public class MaterialYouStyle extends DialogXStyle { + + public static MaterialYouStyle style() { + return new MaterialYouStyle(); + } + + @Override + public int layout(boolean light) { + return light ? R.layout.layout_dialogx_material_you : R.layout.layout_dialogx_material_you_dark; + } + + @Override + public int enterAnimResId() { + return com.kongzue.dialogx.R.anim.anim_dialogx_default_enter; + } + + @Override + public int exitAnimResId() { + return com.kongzue.dialogx.R.anim.anim_dialogx_default_exit; + } + + @Override + public int[] verticalButtonOrder() { + return new int[]{BUTTON_OK, BUTTON_OTHER, BUTTON_CANCEL}; + } + + @Override + public int[] horizontalButtonOrder() { + return new int[]{BUTTON_OTHER, SPACE, BUTTON_CANCEL, BUTTON_OK}; + } + + @Override + public int splitWidthPx() { + return 1; + } + + @Override + public int splitColorRes(boolean light) { + return 0; + } + + @Override + public BlurBackgroundSetting messageDialogBlurSettings() { + return null; + } + + @Override + public HorizontalButtonRes overrideHorizontalButtonRes() { + return new DefaultHorizontalButtonRes(); + } + + public class DefaultHorizontalButtonRes extends HorizontalButtonRes{ + @Override + public int overrideHorizontalOkButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_you_light : R.drawable.button_dialogx_material_you_night; + } + + @Override + public int overrideHorizontalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_you_light : R.drawable.button_dialogx_material_you_night; + } + + @Override + public int overrideHorizontalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_you_light : R.drawable.button_dialogx_material_you_night; + } + } + + @Override + public VerticalButtonRes overrideVerticalButtonRes() { + return new DefaultVerticalButtonRes(); + } + + public class DefaultVerticalButtonRes extends VerticalButtonRes{ + @Override + public int overrideVerticalOkButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_you_light : R.drawable.button_dialogx_material_you_night; + } + + @Override + public int overrideVerticalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_you_light : R.drawable.button_dialogx_material_you_night; + } + + @Override + public int overrideVerticalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) { + return light ? R.drawable.button_dialogx_material_you_light : R.drawable.button_dialogx_material_you_night; + } + } + + @Override + public WaitTipRes overrideWaitTipRes() { + return new DefaultWaitTipRes(); + } + + public class DefaultWaitTipRes extends WaitTipRes{ + @Override + public int overrideWaitLayout(boolean light) { + return com.kongzue.dialogx.R.layout.layout_dialogx_wait; + } + + @Override + public int overrideRadiusPx() { + return -1; + } + + @Override + public boolean blurBackground() { + return false; + } + + @Override + public int overrideBackgroundColorRes(boolean light) { + return 0; + } + + @Override + public int overrideTextColorRes(boolean light) { + return light ? com.kongzue.dialogx.R.color.white : com.kongzue.dialogx.R.color.black; + } + + @Override + public ProgressViewInterface overrideWaitView(Context context, boolean light) { + return new ProgressView(context); + } + } + + @Override + public BottomDialogRes overrideBottomDialogRes() { + return new DefaultBottomDialogRes(); + } + + public class DefaultBottomDialogRes extends BottomDialogRes{ + @Override + public boolean touchSlide() { + return true; + } + + @Override + public int overrideDialogLayout(boolean light) { + return light ? R.layout.layout_dialogx_bottom_material_you: R.layout.layout_dialogx_bottom_material_you_dark; + } + + @Override + public int overrideMenuDividerDrawableRes(boolean light) { + return light ? com.kongzue.dialogx.R.drawable.rect_dialogx_material_menu_split_divider : com.kongzue.dialogx.R.drawable.rect_dialogx_material_menu_split_divider_night; + } + + @Override + public int overrideMenuDividerHeight(boolean light) { + return 1; + } + + @Override + public int overrideMenuTextColor(boolean light) { + return light ? com.kongzue.dialogx.R.color.black90 : com.kongzue.dialogx.R.color.white90; + } + + @Override + public float overrideBottomDialogMaxHeight() { + return 0.6f; + } + + @Override + public int overrideMenuItemLayout(boolean light, int index, int count, boolean isContentVisibility) { + return 0; + } + + @Override + public int overrideSelectionMenuBackgroundColor(boolean light) { + return 0; + } + + @Override + public boolean selectionImageTint(boolean light) { + return false; + } + + @Override + public int overrideSelectionImage(boolean light, boolean isSelected) { + return isSelected ? com.kongzue.dialogx.R.mipmap.img_dialogx_bottom_menu_material_item_selection : com.kongzue.dialogx.R.mipmap.img_dialogx_bottom_menu_material_item_non_select; + } + + @Override + public int overrideMultiSelectionImage(boolean light, boolean isSelected) { + return isSelected ? com.kongzue.dialogx.R.mipmap.img_dialogx_bottom_menu_material_item_multi_selection : com.kongzue.dialogx.R.mipmap.img_dialogx_bottom_menu_material_item_non_multi_select; + } + } + + @Override + public PopTipSettings popTipSettings() { + return new DefaultPopTipSettings(); + } + + public class DefaultPopTipSettings extends PopTipSettings { + @Override + public int layout(boolean light) { + return light ? R.layout.layout_dialogx_poptip_material_you : R.layout.layout_dialogx_poptip_material_you_dark; + } + + @Override + public ALIGN align() { + return ALIGN.BOTTOM; + } + + @Override + public int enterAnimResId(boolean light) { + return com.kongzue.dialogx.R.anim.anim_dialogx_default_enter; + } + + @Override + public int exitAnimResId(boolean light) { + return com.kongzue.dialogx.R.anim.anim_dialogx_default_exit; + } + + @Override + public boolean tintIcon() { + return true; + } + } + + @Override + public PopMenuSettings popMenuSettings() { + return new DefaultPopMenuSettings(); + } + + public class DefaultPopMenuSettings extends PopMenuSettings{ + @Override + public int layout(boolean light) { + return light?R.layout.layout_dialogx_popmenu_material_you:R.layout.layout_dialogx_popmenu_material_you_dark; + } + + @Override + public BlurBackgroundSetting blurBackgroundSettings() { + return null; + } + + @Override + public int backgroundMaskColorRes() { + return 0; + } + + @Override + public int overrideMenuDividerDrawableRes(boolean b) { + return 0; + } + + @Override + public int overrideMenuDividerHeight(boolean b) { + return 0; + } + + @Override + public int overrideMenuTextColor(boolean b) { + return 0; + } + + @Override + public int overrideMenuItemLayoutRes(boolean b) { + return 0; + } + + @Override + public int overrideMenuItemBackgroundRes(boolean b, int i, int i1, boolean b1) { + return 0; + } + + @Override + public int overrideSelectionMenuBackgroundColor(boolean b) { + return 0; + } + + @Override + public boolean selectionImageTint(boolean b) { + return false; + } + + @Override + public int paddingVertical() { + return 0; + } + } + + @Override + public PopNotificationSettings popNotificationSettings() { + return new DefaultPopNotificationSettings() ; + } + + public class DefaultPopNotificationSettings extends PopNotificationSettings{ + @Override + public int layout(boolean light) { + return light ? R.layout.layout_dialogx_popnotification_material_you : R.layout.layout_dialogx_popnotification_material_you_dark; + } + + @Override + public PopNotificationSettings.ALIGN align() { + return ALIGN.TOP; + } + + @Override + public int enterAnimResId(boolean light) { + return com.kongzue.dialogx.R.anim.anim_dialogx_notification_enter; + } + + @Override + public int exitAnimResId(boolean light) { + return com.kongzue.dialogx.R.anim.anim_dialogx_notification_exit; + } + + @Override + public boolean tintIcon() { + return false; + } + } + + @Override + public boolean tintButtonBackground() { + return true; + } +} diff --git a/DialogXMaterialYou/src/main/res/drawable-v21/button_dialogx_material_you_light.xml b/DialogXMaterialYou/src/main/res/drawable-v21/button_dialogx_material_you_light.xml new file mode 100644 index 0000000..abc170b --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable-v21/button_dialogx_material_you_light.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable-v21/button_dialogx_material_you_night.xml b/DialogXMaterialYou/src/main/res/drawable-v21/button_dialogx_material_you_night.xml new file mode 100644 index 0000000..2507cd5 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable-v21/button_dialogx_material_you_night.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/button_dialogx_material_you_light.xml b/DialogXMaterialYou/src/main/res/drawable/button_dialogx_material_you_light.xml new file mode 100644 index 0000000..8cb8c1d --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/button_dialogx_material_you_light.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/button_dialogx_material_you_night.xml b/DialogXMaterialYou/src/main/res/drawable/button_dialogx_material_you_night.xml new file mode 100644 index 0000000..5157816 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/button_dialogx_material_you_night.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_low_api_material_you_button_press.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_low_api_material_you_button_press.xml new file mode 100644 index 0000000..53015e9 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_low_api_material_you_button_press.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_low_api_material_you_button_press_night.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_low_api_material_you_button_press_night.xml new file mode 100644 index 0000000..b53891b --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_low_api_material_you_button_press_night.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bkg_light.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bkg_light.xml new file mode 100644 index 0000000..3f2f3af --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bkg_light.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bkg_night.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bkg_night.xml new file mode 100644 index 0000000..3f2f3af --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bkg_night.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bottom_bkg_light.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bottom_bkg_light.xml new file mode 100644 index 0000000..43eea43 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bottom_bkg_light.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bottom_bkg_night.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bottom_bkg_night.xml new file mode 100644 index 0000000..43eea43 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_bottom_bkg_night.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_button_light_forword.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_button_light_forword.xml new file mode 100644 index 0000000..69a7569 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_button_light_forword.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_button_night_forword.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_button_night_forword.xml new file mode 100644 index 0000000..69a7569 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_button_night_forword.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_dialogtap.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_dialogtap.xml new file mode 100644 index 0000000..f10aebb --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_dialogtap.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_dialogtap_night.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_dialogtap_night.xml new file mode 100644 index 0000000..d32ce17 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_dialogtap_night.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popmenu_bkg.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popmenu_bkg.xml new file mode 100644 index 0000000..d7f2371 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popmenu_bkg.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popmenu_bkg_night.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popmenu_bkg_night.xml new file mode 100644 index 0000000..d7f2371 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popmenu_bkg_night.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popnotification_bkg.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popnotification_bkg.xml new file mode 100644 index 0000000..ccee0a8 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popnotification_bkg.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popnotification_bkg_night.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popnotification_bkg_night.xml new file mode 100644 index 0000000..ccee0a8 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_popnotification_bkg_night.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_poptip_bkg.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_poptip_bkg.xml new file mode 100644 index 0000000..045aae3 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_poptip_bkg.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_poptip_bkg_night.xml b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_poptip_bkg_night.xml new file mode 100644 index 0000000..045aae3 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/drawable/rect_dialogx_material_you_poptip_bkg_night.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_bottom_material_you.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_bottom_material_you.xml new file mode 100644 index 0000000..c6d3535 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_bottom_material_you.xml @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_bottom_material_you_dark.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_bottom_material_you_dark.xml new file mode 100644 index 0000000..d0c70e2 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_bottom_material_you_dark.xml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_fullscreen_material_you.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_fullscreen_material_you.xml new file mode 100644 index 0000000..d29ac81 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_fullscreen_material_you.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_fullscreen_material_you_dark.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_fullscreen_material_you_dark.xml new file mode 100644 index 0000000..e3662fb --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_fullscreen_material_you_dark.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_material_you.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_material_you.xml new file mode 100644 index 0000000..4d965d2 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_material_you.xml @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_material_you_dark.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_material_you_dark.xml new file mode 100644 index 0000000..a4744e2 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_material_you_dark.xml @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popmenu_material_you.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popmenu_material_you.xml new file mode 100644 index 0000000..3616c85 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popmenu_material_you.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popmenu_material_you_dark.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popmenu_material_you_dark.xml new file mode 100644 index 0000000..d83ad8b --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popmenu_material_you_dark.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popnotification_material_you.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popnotification_material_you.xml new file mode 100644 index 0000000..e90794f --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popnotification_material_you.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popnotification_material_you_dark.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popnotification_material_you_dark.xml new file mode 100644 index 0000000..c6dbf19 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_popnotification_material_you_dark.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_poptip_material_you.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_poptip_material_you.xml new file mode 100644 index 0000000..8e78523 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_poptip_material_you.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/layout/layout_dialogx_poptip_material_you_dark.xml b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_poptip_material_you_dark.xml new file mode 100644 index 0000000..64d8f1a --- /dev/null +++ b/DialogXMaterialYou/src/main/res/layout/layout_dialogx_poptip_material_you_dark.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/values/colors.xml b/DialogXMaterialYou/src/main/res/values/colors.xml new file mode 100644 index 0000000..fb1ce67 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/values/colors.xml @@ -0,0 +1,5 @@ + + + #F2F6FC + #2E3032 + \ No newline at end of file diff --git a/DialogXMaterialYou/src/main/res/values/styles.xml b/DialogXMaterialYou/src/main/res/values/styles.xml new file mode 100644 index 0000000..ee360e8 --- /dev/null +++ b/DialogXMaterialYou/src/main/res/values/styles.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..d0b97c6 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,2 @@ +/build +*.iml \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..cfa42e5 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,127 @@ +plugins { + id 'com.android.application' + id 'kotlin-android' +} + +def releaseTime() { + return new Date().format("yyyyMMddHHmmss") +} + +android { + compileSdkVersion 33 + defaultConfig { + applicationId "com.tairui.industrial_operation" + minSdkVersion 21 + targetSdkVersion 33 + versionCode 1 + versionName "1.0" + multiDexEnabled true + javaCompileOptions { + annotationProcessorOptions { + arguments = [ + //必须,告知RxHttp你依赖的okhttp版本,目前已适配 v3.12.0 - v4.8.1版本 + rxhttp_okhttp : '4.8.1', + //使用asXxx方法时必须,告知RxHttp你依赖的rxjava版本,可传入rxjava2、rxjava3 + rxhttp_rxjava : 'rxjava2', + rxhttp_package: 'rxhttp' //非必须,指定RxHttp相关类的生成路径,即包名 + ] + } + } + renderscriptTargetApi 21 + renderscriptSupportModeEnabled true + } + + buildFeatures { + viewBinding true + } + + sourceSets { + main { + jniLibs.srcDir 'libs' + } + } + dexOptions { + javaMaxHeapSize "4g" + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + signingConfigs { + release { + storeFile file('../key/tairui.jks') + storePassword "tairui" + keyAlias "tairui" + keyPassword "tairui" + } + } + buildTypes { + debug { + signingConfig signingConfigs.release + jniDebuggable false + zipAlignEnabled false + } + release { + zipAlignEnabled true + signingConfig signingConfigs.release + minifyEnabled false + jniDebuggable false + + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + + } + + lintOptions { + abortOnError false + } + + android.applicationVariants.all { variant -> + variant.outputs.all { + outputFileName = "IndustrialOperation_app_v${variant.versionName}_${releaseTime()}.apk" + } + } +} +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'androidx.constraintlayout:constraintlayout:2.1.3' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'androidx.recyclerview:recyclerview:1.2.1' + implementation 'com.google.android.material:material:1.5.0' + + implementation 'com.google.code.gson:gson:2.8.7' + + implementation 'com.scwang.smart:refresh-layout-kernel:2.0.1' //核心必须依赖 + implementation 'com.scwang.smart:refresh-header-classics:2.0.1' //经典刷新头 + implementation 'com.scwang.smart:refresh-header-material:2.0.1' //谷歌刷新头 + implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + + // 事件总线 + implementation 'org.greenrobot:eventbus:3.2.0' + //键值存储 + implementation 'com.orhanobut:hawk:2.0.1' + + // 底部导航库 + implementation 'com.flyco.tablayout:FlycoTabLayout_Lib:2.1.2@aar' + + implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.50' + + //rxhttp + implementation 'com.ljx.rxhttp:rxhttp:2.5.7' + implementation 'com.squareup.okhttp3:okhttp:4.8.1' //rxhttp v2.2.2版本起,需要手动依赖okhttp + annotationProcessor 'com.ljx.rxhttp:rxhttp-compiler:2.5.7' //生成RxHttp类,纯Java项目,请使用annotationProcessor代替kapt + //rxjava2 (RxJava2/Rxjava3二选一,使用asXxx方法时必须) + implementation 'io.reactivex.rxjava2:rxjava:2.2.8' + implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' + implementation 'com.rxjava.rxlife:rxlife:2.0.0' + // glide加载图片 + implementation 'com.github.bumptech.glide:glide:4.12.0' + annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' + + implementation project(path: ':DialogX') + implementation project(path: ':DialogXMaterialYou') + + implementation 'com.github.zhpanvip:bannerviewpager:3.5.12' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..de09cad --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in D:\AndroidStudio\sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} +-keep class com.kongzue.dialogx.** { *; } +-dontwarn com.kongzue.dialogx.** + +# 额外的,建议将 android.view 也列入 keep 范围: +-keep class android.view.** { *; } + +-keep class androidx.recyclerview.widget.**{*;} +-keep class androidx.viewpager2.widget.**{*;} \ No newline at end of file diff --git a/app/release/IndustrialOperation_app_v1.0_20250527114511.apk b/app/release/IndustrialOperation_app_v1.0_20250527114511.apk new file mode 100644 index 0000000..25e8201 Binary files /dev/null and b/app/release/IndustrialOperation_app_v1.0_20250527114511.apk differ diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json new file mode 100644 index 0000000..5f210b4 --- /dev/null +++ b/app/release/output-metadata.json @@ -0,0 +1,20 @@ +{ + "version": 3, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "com.tairui.industrial_operation", + "variantName": "release", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "attributes": [], + "versionCode": 1, + "versionName": "1.0", + "outputFile": "IndustrialOperation_app_v1.0_20250527114511.apk" + } + ], + "elementType": "File" +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..fe1c279 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/file_monitoring.json b/app/src/main/assets/file_monitoring.json new file mode 100644 index 0000000..8924381 --- /dev/null +++ b/app/src/main/assets/file_monitoring.json @@ -0,0 +1,148 @@ +[ + { + "name": "A区田间监测设备组", + "stations": [ + { + "name": "生长监控", + "type": "monitor", + "monitors": { + "normal": [ + { + "name": "监控001", + "status": 0 + }, + { + "name": "监控002", + "status": 0 + }, + { + "name": "监控003", + "status": 0 + }, + { + "name": "监控004", + "status": 0 + }, + { + "name": "监控005", + "status": 0 + }, + { + "name": "监控0012", + "status": 0 + }, + { + "name": "监控0013", + "status": 0 + } + ], + "exceptions": [ + { + "name": "监控006", + "status": 0 + }, + { + "name": "监控007", + "status": 0 + }, + { + "name": "监控008", + "status": 0 + }, + { + "name": "监控0011", + "status": 0 + } + ], + "offline": [ + { + "name": "监控009", + "status": 0 + }, + { + "name": "监控010", + "status": 0 + } + ] + } + }, + { + "name": "生长监测站", + "type": "station", + "sensors": [ + { + "type": "temperature", + "value": "15", + "status": 0 + }, + { + "type": "humidity", + "value": "83", + "status": 1 + }, + { + "type": "stalk_height", + "value": "3.5", + "status": 0 + } + ] + } + ] + }, + { + "name": "B区田间监测设备组", + "stations": [ + { + "name": "生长监测站1", + "monitors": { + "normal": [ + { + "name": "监控001", + "status": 0 + }, + { + "name": "监控002", + "status": 0 + }, + { + "name": "监控003", + "status": 0 + }, + { + "name": "监控004", + "status": 0 + }, + { + "name": "监控005", + "status": 0 + } + ], + "exceptions": [ + { + "name": "监控006", + "status": 0 + }, + { + "name": "监控007", + "status": 0 + }, + { + "name": "监控008", + "status": 0 + } + ], + "offline": [ + { + "name": "监控009", + "status": 0 + }, + { + "name": "监控010", + "status": 0 + } + ] + } + } + ] + } +] \ No newline at end of file diff --git a/app/src/main/java/com/tairui/industrial_operation/IOApplication.java b/app/src/main/java/com/tairui/industrial_operation/IOApplication.java new file mode 100644 index 0000000..eaaae4d --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/IOApplication.java @@ -0,0 +1,81 @@ +package com.tairui.industrial_operation; + +import java.util.concurrent.TimeUnit; + +import com.kongzue.dialogx.DialogX; +import com.kongzue.dialogxmaterialyou.style.MaterialYouStyle; +import com.orhanobut.hawk.Hawk; +import com.tairui.industrial_operation.config.AppConfig; +import com.tairui.industrial_operation.entity.Api; +import com.tairui.industrial_operation.http.ApiDns; +import com.tairui.industrial_operation.util.CrashHandler; + +import android.app.Application; +import android.content.Context; +import android.text.TextUtils; +import okhttp3.OkHttpClient; +import rxhttp.RxHttp; +import rxhttp.wrapper.ssl.HttpsUtils; + +public class IOApplication extends Application { + + protected static Context context; + + public static IOApplication instance; + + public static IOApplication getInstance() { + if (instance == null) { + instance = new IOApplication(); + } + return instance; + } + + @Override + public void onCreate() { + super.onCreate(); + context = this; + + CrashHandler.instance().init(getApplicationContext()); + initHawk(); + intiDialog(); + initHttp(); + } + + private void initHawk() { + Hawk.init(context).build(); + } + + private void intiDialog() { + //初始化 + DialogX.init(this); + DialogX.globalStyle = new MaterialYouStyle(); + //设置亮色/暗色(在启动下一个对话框时生效) + DialogX.globalTheme = DialogX.THEME.AUTO; + } + + private void initHttp() { + HttpsUtils.SSLParams sslParams = HttpsUtils.getSslSocketFactory(); + OkHttpClient okHttpClient = new OkHttpClient.Builder() + .connectTimeout(10, TimeUnit.SECONDS) + .readTimeout(10, TimeUnit.SECONDS) + .writeTimeout(10, TimeUnit.SECONDS) + .sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager) + .hostnameVerifier((hostname, session) -> true) + .dns(new ApiDns()) + .build(); + RxHttp.init(okHttpClient); + RxHttp.setDebug(BuildConfig.DEBUG); + RxHttp.setOnParamAssembly(param -> { + if (null != IOApplication.getContext()) { + if (!TextUtils.isEmpty(AppConfig.getInstance().getToken())) { + param = param.addHeader(Api.STR_AUTHORIZATION, Api.BEARER.concat(AppConfig.getInstance().getToken())); + } + } + return param; + }); + } + + public static Context getContext() { + return context; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tairui/industrial_operation/base/BaseActivity.java b/app/src/main/java/com/tairui/industrial_operation/base/BaseActivity.java new file mode 100644 index 0000000..112556a --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/base/BaseActivity.java @@ -0,0 +1,186 @@ +package com.tairui.industrial_operation.base; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import com.gyf.immersionbar.ImmersionBar; +import com.kongzue.dialogx.dialogs.PopTip; +import com.tairui.industrial_operation.R; +import com.tairui.industrial_operation.util.AppUtil; +import com.tairui.industrial_operation.widget.loading.LoadingDialog; + +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.View; +import android.widget.TextView; +import android.widget.Toast; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.viewbinding.ViewBinding; + +public abstract class BaseActivity extends AppCompatActivity { + + protected T binding; + + private LoadingDialog loading; + protected Context mContext; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mContext = this; + initStatusBar(); + try { + // 通过反射获取 ViewBinding 类的 inflate 方法 + Method inflateMethod = getBindingClass().getMethod("inflate", android.view.LayoutInflater.class); + // 调用 inflate 方法创建 ViewBinding 实例 + binding = (T) inflateMethod.invoke(null, getLayoutInflater()); + // 设置 Activity 的内容视图为 ViewBinding 的根视图 + setContentView(binding.getRoot()); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + initContentView(savedInstanceState); + } + + protected abstract Class getBindingClass(); + + protected void initStatusBar() { + ImmersionBar.with(this) + .statusBarColor(getStatusBarColor()) + .statusBarDarkFont(isDarkMode()) //状态栏字体是深色,不写默认为亮色 + .fitsSystemWindows(true) + .keyboardEnable(true) //解决软键盘与底部输入框冲突问题 + .init(); + } + + protected boolean isDarkMode() { + return true; + } + + protected int getStatusBarColor() { + return R.color.white; + } + + /** + * 解决系统字体改变而ui不协调问题,设置app字体为默认字体 + * + * @return + */ + @Override + public Resources getResources() { + Resources res = super.getResources(); + if (res != null) { + Configuration config = res.getConfiguration(); + if (config != null && config.fontScale != 1.0f) { + config.fontScale = 1.0f; + res.updateConfiguration(config, res.getDisplayMetrics()); + } + } + return res; + } + + @Override + protected void onDestroy() { + super.onDestroy(); + } + + @Override + protected void onPause() { + super.onPause(); + } + + @Override + protected void onResume() { + super.onResume(); + } + + private void initContentView(Bundle savedInstanceState) { + onQueryArguments(); + onFindView(savedInstanceState); + onBindListener(); + onApplyData(); + } + + /** + * 取得传递的参数 + */ + protected void onQueryArguments() { + + } + + /** + * 初始化控件、获取内部控件 + */ + protected void onFindView(Bundle savedInstanceState) { + } + + /** + * 设置监听事件 + */ + protected void onBindListener() { + + } + + /** + * 加载数据 + */ + protected void onApplyData() { + + } + + protected String getStr(int resId) { + return mContext.getResources().getString(resId); + } + + protected int getResColor(int resId) { + return mContext.getResources().getColor(resId); + } + + protected void showToast(String str) { + Toast.makeText(mContext, str, Toast.LENGTH_LONG).show(); + } + protected void showError(String str) { + PopTip.show(str).iconError(); + } + + protected void setText(TextView view, String txt, String defaultTxt) { + if (view != null) { + view.setText(TextUtils.isEmpty(txt) ? defaultTxt : txt); + } + } + + protected void setText(TextView view, String txt) { + setText(view, txt, ""); + } + + protected void setGone(View view, boolean visible) { + view.setVisibility(visible ? View.VISIBLE : View.GONE); + } + + protected void showLoading() { + if (loading == null) { + loading = AppUtil.getLoading(mContext); + } + if (!loading.isShow()) { + loading.show(); + } + } + + protected void hideLoading() { + if (loading != null && loading.isShow()) { + loading.close(); + } + } + + protected boolean isLoadingShow() { + if (loading != null && loading.isShow()) { + return true; + } else { + return false; + } + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/base/BaseFragment.java b/app/src/main/java/com/tairui/industrial_operation/base/BaseFragment.java new file mode 100644 index 0000000..491a102 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/base/BaseFragment.java @@ -0,0 +1,170 @@ +package com.tairui.industrial_operation.base; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import com.kongzue.dialogx.dialogs.PopTip; +import com.tairui.industrial_operation.util.AppUtil; +import com.tairui.industrial_operation.widget.loading.LoadingDialog; + +import android.content.Context; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.viewbinding.ViewBinding; + +/** + * Created by tongchao on 17/5/20. + */ + +public abstract class BaseFragment extends Fragment { + + protected T binding; + + protected Context mContext; + + private LoadingDialog loading; + + // Fragment当前状态是否可见 + protected boolean isVisible; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + mContext = getActivity(); + try { + // 获取 ViewBinding 类的 inflate 方法 + Method inflateMethod = getBindingClass().getMethod("inflate", LayoutInflater.class, ViewGroup.class, boolean.class); + // 调用 inflate 方法创建 ViewBinding 实例 + binding = (T) inflateMethod.invoke(null, inflater, container, false); + return binding.getRoot(); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + return null; + } + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + // 避免内存泄漏,在 Fragment 视图销毁时将 binding 置为 null + binding = null; + } + + /** + * 抽象方法,由子类实现以返回具体的 ViewBinding 类 + * + * @return 具体的 ViewBinding 类 + */ + protected abstract Class getBindingClass(); + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + initContentView(); + } + + private void initContentView() { + onQueryArguments(); + onFindView(getView()); + onBindListener(); + onApplyData(); + } + + /** + * 取得传递的参数 + */ + protected void onQueryArguments() { + + } + + /** + * 初始化控件、获取内部控件 + */ + protected void onFindView(View rootView) { + + } + + /** + * 设置监听事件 + */ + protected void onBindListener() { + + } + + /** + * 加载数据 + */ + protected void onApplyData() { + + } + + protected void showToast(String str) { + PopTip.show(str); + } + + protected void showError(String errorMsg) { + PopTip.show(errorMsg).iconError(); + } + + public void frResume() { + } + + public void frPause() { + } + + @Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + if (getUserVisibleHint()) { + isVisible = true; + frResume(); + } else { + isVisible = false; + frPause(); + } + } + + protected void showLoading() { + if (loading == null) { + loading = AppUtil.getLoading(getActivity()); + } + if (!loading.isShow()) { + loading.show(); + } + } + + protected void hideLoading() { + if (loading != null && loading.isShow()) { + loading.close(); + } + } + + protected boolean isLoadingShow() { + if (loading != null && loading.isShow()) { + return true; + } else { + return false; + } + } + + protected void setText(TextView view, String txt, String defaultTxt) { + if (view != null) { + view.setText(TextUtils.isEmpty(txt) ? defaultTxt : txt); + } + } + + protected void setText(TextView view, String txt) { + setText(view, txt, ""); + } + + protected void setGone(View view, boolean visible) { + view.setVisibility(visible ? View.VISIBLE : View.GONE); + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/base/adapter/CommPagerAdapter.kt b/app/src/main/java/com/tairui/industrial_operation/base/adapter/CommPagerAdapter.kt new file mode 100644 index 0000000..3d6edcc --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/base/adapter/CommPagerAdapter.kt @@ -0,0 +1,37 @@ +package cn.kpinfo.base.adapter + +import android.os.Parcelable +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentStatePagerAdapter + +/** + * create by libo + * create on 2020/5/19 + * description 公共viewPageradapter + */ +class CommPagerAdapter( + fm: FragmentManager?, + private val items: ArrayList, + private val mTitles: Array +) : FragmentStatePagerAdapter(fm!!) { + override fun getCount(): Int { + return if (items.size == 0) 0 else items.size + } + + override fun getItem(position: Int): Fragment { + return items[position] + } + + override fun getPageTitle(position: Int): CharSequence? { + return mTitles[position] + } + + override fun getItemPosition(`object`: Any): Int { + return POSITION_NONE + } + + override fun saveState(): Parcelable? { + return null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tairui/industrial_operation/base/adapter/TabFragmentPagerAdapter.java b/app/src/main/java/com/tairui/industrial_operation/base/adapter/TabFragmentPagerAdapter.java new file mode 100644 index 0000000..c57e10f --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/base/adapter/TabFragmentPagerAdapter.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 Baidu, Inc. All Rights Reserved. + */ +package com.tairui.industrial_operation.base.adapter; + +import java.util.ArrayList; +import java.util.List; + +import com.tairui.industrial_operation.base.BaseFragment; + +import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; + +public class TabFragmentPagerAdapter extends FragmentPagerAdapter { + + private ArrayList tab_title_list;//存放标签页标题 + private List fragments; + + public TabFragmentPagerAdapter(FragmentManager fm, ArrayList tab_title_list, List fragments) { + super(fm); + this.tab_title_list = tab_title_list; + this.fragments = fragments; + } + + @Override + public BaseFragment getItem(int position) { + if (position >= fragments.size() || position < 0) { + return null; + } + return fragments.get(position); + } + + @Override + public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { + //super.destroyItem(container, position, object); + } + + @Override + public int getCount() { + return fragments.size(); + } + + @Override + public CharSequence getPageTitle(int position) { + return tab_title_list.get(position); + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/base/entity/BaseHolderEntity.java b/app/src/main/java/com/tairui/industrial_operation/base/entity/BaseHolderEntity.java new file mode 100644 index 0000000..d88ed17 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/base/entity/BaseHolderEntity.java @@ -0,0 +1,48 @@ +package com.tairui.industrial_operation.base.entity; + +import com.chad.library.adapter.base.entity.MultiItemEntity; + +public class BaseHolderEntity implements MultiItemEntity { + private int type; + private Object data; + private Object subData; + private boolean select = false; + + public Object getSubData() { + return subData; + } + + public void setSubData(Object subData) { + this.subData = subData; + } + + public boolean isSelect() { + return select; + } + + public void setSelect(boolean select) { + this.select = select; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + } + + @Override + public int getItemType() { + return type; + } + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/config/AppConfig.java b/app/src/main/java/com/tairui/industrial_operation/config/AppConfig.java new file mode 100644 index 0000000..bb2a444 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/config/AppConfig.java @@ -0,0 +1,31 @@ +package com.tairui.industrial_operation.config; + +import com.orhanobut.hawk.Hawk; +import com.tairui.industrial_operation.entity.Constant; + +import android.text.TextUtils; + +public class AppConfig { + + public static AppConfig _instance; + + public static AppConfig getInstance() { + if (_instance == null) { + _instance = new AppConfig(); + } + return _instance; + } + + private String token; + + public String getToken() { + if (TextUtils.isEmpty(token)) { + token = Hawk.get(Constant.TOKEN); + } + return token; + } + + public void setToken(String token) { + this.token = token; + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/entity/Api.java b/app/src/main/java/com/tairui/industrial_operation/entity/Api.java new file mode 100644 index 0000000..bbec579 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/entity/Api.java @@ -0,0 +1,25 @@ +package com.tairui.industrial_operation.entity; + +public class Api { + +// public static String BASE_HOST = "http://192.168.18.188:8080"; + + public static String BASE_HOST = "http://47.109.205.240:8080"; + + /** + * 请求成功 + */ + public static final int CODE_SUCCESS_REQUEST = 200; + /** + * token前面需要加入的字段 + */ + public static final String BEARER = "Bearer "; + /** + * 请求接口的时候的 授权token + */ + public static final String STR_AUTHORIZATION = "Authorization"; + + public static final String REGISTER = BASE_HOST + "/auth/registerApp"; + public static final String LOGIN = BASE_HOST + "/auth/login"; + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/entity/Constant.java b/app/src/main/java/com/tairui/industrial_operation/entity/Constant.java new file mode 100644 index 0000000..2632d12 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/entity/Constant.java @@ -0,0 +1,9 @@ +package com.tairui.industrial_operation.entity; + +public class Constant { + + public static String TOKEN = "token"; + + public static String LOGIN_USER_NAME = "login_user_name"; + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/entity/PieBean.java b/app/src/main/java/com/tairui/industrial_operation/entity/PieBean.java new file mode 100644 index 0000000..3d79da1 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/entity/PieBean.java @@ -0,0 +1,38 @@ +package com.tairui.industrial_operation.entity; + + +/** + * autour : openXu + * date : 2018/6/8 9:40 + * className : PieBean + * version : 1.0 + * description : 请添加类说明 + */ +public class PieBean { + private float Numner; + private String Name; + + public PieBean() { + } + + public PieBean(float Numner, String Name) { + this.Numner = Numner; + this.Name = Name; + } + + public float getNumner() { + return Numner; + } + + public void setNumner(float numner) { + Numner = numner; + } + + public String getName() { + return Name; + } + + public void setName(String name) { + Name = name; + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/entity/RoseBean.java b/app/src/main/java/com/tairui/industrial_operation/entity/RoseBean.java new file mode 100644 index 0000000..c439e44 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/entity/RoseBean.java @@ -0,0 +1,39 @@ +package com.tairui.industrial_operation.entity; + + +/** + * autour : openXu + * date : 2018/6/8 9:40 + * className : RoseBean + * version : 1.0 + * description : 请添加类说明 + */ +public class RoseBean { + + private float count; + private String ClassName; + + public RoseBean() { + } + + public RoseBean(float count, String className) { + this.count = count; + ClassName = className; + } + + public float getCount() { + return count; + } + + public void setCount(float count) { + this.count = count; + } + + public String getClassName() { + return ClassName; + } + + public void setClassName(String className) { + ClassName = className; + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/entity/TabEntity.java b/app/src/main/java/com/tairui/industrial_operation/entity/TabEntity.java new file mode 100644 index 0000000..40934ca --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/entity/TabEntity.java @@ -0,0 +1,30 @@ +package com.tairui.industrial_operation.entity; + +import com.flyco.tablayout.listener.CustomTabEntity; + +public class TabEntity implements CustomTabEntity { + public String title; + public int selectedIcon; + public int unSelectedIcon; + + public TabEntity(String title, int selectedIcon, int unSelectedIcon) { + this.title = title; + this.selectedIcon = selectedIcon; + this.unSelectedIcon = unSelectedIcon; + } + + @Override + public String getTabTitle() { + return title; + } + + @Override + public int getTabSelectedIcon() { + return selectedIcon; + } + + @Override + public int getTabUnselectedIcon() { + return unSelectedIcon; + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/http/ApiDns.java b/app/src/main/java/com/tairui/industrial_operation/http/ApiDns.java new file mode 100644 index 0000000..4c8656d --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/http/ApiDns.java @@ -0,0 +1,35 @@ +package com.tairui.industrial_operation.http; + +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; + +import okhttp3.Dns; + +public class ApiDns implements Dns { + @Override + public List lookup(String hostname) throws UnknownHostException { + if (hostname == null) { + throw new UnknownHostException("hostname == null"); + } else { + try { + List mInetAddressesList = new ArrayList<>(); + InetAddress[] mInetAddresses = InetAddress.getAllByName(hostname); + for (InetAddress address : mInetAddresses) { + if (address instanceof Inet4Address) { + mInetAddressesList.add(0, address); + } else { + mInetAddressesList.add(address); + } + } + return mInetAddressesList; + } catch (NullPointerException var4) { + UnknownHostException unknownHostException = new UnknownHostException("Broken system behaviour"); + unknownHostException.initCause(var4); + throw unknownHostException; + } + } + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/http/ErrorInfo.java b/app/src/main/java/com/tairui/industrial_operation/http/ErrorInfo.java new file mode 100644 index 0000000..54f100e --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/http/ErrorInfo.java @@ -0,0 +1,41 @@ +package com.tairui.industrial_operation.http; + +import android.text.TextUtils; +import rxhttp.wrapper.exception.ParseException; + +/** + * Http请求错误信息 + */ +public class ErrorInfo { + + private int errorCode; //仅指服务器返回的错误码 + private String errorMsg; //错误文案,网络错误、请求失败错误、服务器返回的错误等文案 + private Throwable throwable; //异常信息 + + public ErrorInfo(Throwable throwable) { + this.throwable = throwable; + String errorMsg = ExceptionHelper.handleNetworkException(throwable); //网络异常 + if (throwable instanceof ParseException) { // ParseException异常表明请求成功,但是数据不正确 + String errorCode = throwable.getLocalizedMessage(); + this.errorCode = Integer.valueOf(errorCode); + errorMsg = throwable.getMessage(); + if (TextUtils.isEmpty(errorMsg)) { + errorMsg = errorCode;//errorMsg为空,显示errorCode + } + } + this.errorMsg = errorMsg; + } + + public int getErrorCode() { + return errorCode; + } + + public String getErrorMsg() { + return errorMsg; + } + + public Throwable getThrowable() { + return throwable; + } + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/http/ExceptionHelper.java b/app/src/main/java/com/tairui/industrial_operation/http/ExceptionHelper.java new file mode 100644 index 0000000..67ea0a8 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/http/ExceptionHelper.java @@ -0,0 +1,50 @@ +package com.tairui.industrial_operation.http; + +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; +import java.util.concurrent.TimeoutException; + +import com.tairui.industrial_operation.IOApplication; +import com.tairui.industrial_operation.R; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; + +/** + * 异常处理帮助类 + */ +public class ExceptionHelper { + + //处理网络异常 + public static String handleNetworkException(T throwable) { + int stringId = -1; + if (throwable instanceof UnknownHostException) { + if (!isNetworkConnected(IOApplication.getInstance())) { + stringId = R.string.network_error; + } else { + stringId = R.string.notify_no_network; + } + } else if (throwable instanceof SocketTimeoutException || throwable instanceof TimeoutException) { + //前者是通过OkHttpClient设置的超时引发的异常,后者是对单个请求调用timeout方法引发的超时异常 + stringId = R.string.time_out_please_try_again_later; + } else if (throwable instanceof ConnectException) { + stringId = R.string.esky_service_exception; + } + return stringId == -1 ? null : IOApplication.getInstance().getString(stringId); + } + + public static boolean isNetworkConnected(Context context) { + if (context != null) { + ConnectivityManager mConnectivityManager = + (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo(); + if (mNetworkInfo != null) { + return mNetworkInfo.isAvailable(); + } + } + + return false; + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/http/OnError.java b/app/src/main/java/com/tairui/industrial_operation/http/OnError.java new file mode 100644 index 0000000..236898a --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/http/OnError.java @@ -0,0 +1,17 @@ +package com.tairui.industrial_operation.http; + + +import io.reactivex.functions.Consumer; + +/** + * RxJava 错误回调 ,加入网络异常处理 + */ +public interface OnError extends Consumer { + + @Override + default void accept(Throwable throwable) throws Exception { + onError(new ErrorInfo(throwable)); + } + + void onError(ErrorInfo error) throws Exception; +} diff --git a/app/src/main/java/com/tairui/industrial_operation/http/Response.java b/app/src/main/java/com/tairui/industrial_operation/http/Response.java new file mode 100644 index 0000000..52131f7 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/http/Response.java @@ -0,0 +1,32 @@ +package com.tairui.industrial_operation.http; + +public class Response { + + private int code; + private String msg; + private T data; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/http/ResponseParser.java b/app/src/main/java/com/tairui/industrial_operation/http/ResponseParser.java new file mode 100644 index 0000000..2b375fc --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/http/ResponseParser.java @@ -0,0 +1,69 @@ +package com.tairui.industrial_operation.http; + +import java.io.IOException; +import java.lang.reflect.Type; + +import com.tairui.industrial_operation.entity.Api; + +import android.annotation.SuppressLint; +import android.text.TextUtils; +import rxhttp.wrapper.annotation.Parser; +import rxhttp.wrapper.entity.ParameterizedTypeImpl; +import rxhttp.wrapper.exception.ParseException; +import rxhttp.wrapper.parse.AbstractParser; + +/** + * Response 数据解析器,解析完成对Response对象做判断,如果ok,返回数据 T + * User: ljx + * Date: 2018/10/23 + * Time: 13:49 + */ +@Parser(name = "Response") +public class ResponseParser extends AbstractParser { + + /** + * 此构造方法适用于任意Class对象,但更多用于带泛型的Class对象,如:List + *

+ * 用法: + * Java: .asParser(new ResponseParser>(){}) + * Kotlin: .asParser(object : ResponseParser>() {}) + *

+ * 注:此构造方法一定要用protected关键字修饰,否则调用此构造方法将拿不到泛型类型 + */ + protected ResponseParser() { + super(); + } + + /** + * 此构造方法仅适用于不带泛型的Class对象,如: Student.class + *

+ * 用法 + * Java: .asParser(new ResponseParser<>(Student.class)) 或者 .asResponse(Student.class) + * Kotlin: .asParser(ResponseParser(Student::class.java)) 或者 .asResponse(Student::class.java) + */ + public ResponseParser(Type type) { + super(type); + } + + @SuppressWarnings("unchecked") + @SuppressLint("NewApi") + @Override + public T onParse(okhttp3.Response response) throws IOException { + final Type type = ParameterizedTypeImpl.get(Response.class, mType); //获取泛型类型 + Response data = convert(response, type); + T t = data.getData(); //获取data字段 + if (t == null && mType == String.class) { + /* + * 考虑到有些时候服务端会返回:{"errorCode":0,"errorMsg":"关注成功"} 类似没有data的数据 + * 此时code正确,但是data字段为空,直接返回data的话,会报空指针错误, + * 所以,判断泛型为String类型时,重新赋值,并确保赋值不为null + */ + t = (T) (TextUtils.isEmpty(data.getMsg()) ? "" : data.getMsg()); + } + if (data.getCode() == Api.CODE_SUCCESS_REQUEST) { + return t; + } else { + throw new ParseException("-1", data.getMsg(), response); + } + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/ui/MainActivity.java b/app/src/main/java/com/tairui/industrial_operation/ui/MainActivity.java new file mode 100644 index 0000000..2c56fb7 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/ui/MainActivity.java @@ -0,0 +1,114 @@ +package com.tairui.industrial_operation.ui; + +import java.util.ArrayList; + +import com.flyco.tablayout.listener.CustomTabEntity; +import com.flyco.tablayout.listener.OnTabSelectListener; +import com.gyf.immersionbar.ImmersionBar; +import com.tairui.industrial_operation.R; +import com.tairui.industrial_operation.base.BaseActivity; +import com.tairui.industrial_operation.base.BaseFragment; +import com.tairui.industrial_operation.base.adapter.TabFragmentPagerAdapter; +import com.tairui.industrial_operation.databinding.ActivityMainBinding; +import com.tairui.industrial_operation.entity.TabEntity; +import com.tairui.industrial_operation.ui.home.HomeFragment; +import com.tairui.industrial_operation.ui.my.MyFragment; +import com.tairui.industrial_operation.ui.service.ServiceFragment; +import com.tairui.industrial_operation.ui.shopping.ShoppingFragment; + +import android.os.Bundle; +import androidx.viewpager.widget.ViewPager; + +public class MainActivity extends BaseActivity { + + private boolean isInit = false; + + private ArrayList mFragments = new ArrayList<>(); + private ArrayList mTitles = new ArrayList<>(); + private ArrayList mTabEntities = new ArrayList<>(); + private TabFragmentPagerAdapter mPagerAdapter; + + @Override + protected void initStatusBar() { + ImmersionBar.with(this).fitsSystemWindows(false).transparentStatusBar() + .statusBarDarkFont(false).init(); + } + + @Override + protected Class getBindingClass() { + return ActivityMainBinding.class; + } + + @Override + protected void onFindView(Bundle savedInstanceState) { + mTabEntities.add(new TabEntity("首页", R.mipmap.tabar_home_sel, R.mipmap.tabar_home_nor)); + mTitles.add("首页"); + mFragments.add(new HomeFragment()); + mTabEntities.add(new TabEntity("商城", R.mipmap.tabar_shopping_sel, R.mipmap.tabar_shopping_nor)); + mTitles.add("商城"); + mFragments.add(new ShoppingFragment()); + mTabEntities.add(new TabEntity("服务", R.mipmap.tabar_service_sel, R.mipmap.tabar_service_nor)); + mTitles.add("服务"); + mFragments.add(new ServiceFragment()); + mTabEntities.add(new TabEntity("我的", R.mipmap.tabar_mine_sel, R.mipmap.tabar_mine_nor)); + mTitles.add("我的"); + mFragments.add(new MyFragment()); + + mPagerAdapter = new TabFragmentPagerAdapter(getSupportFragmentManager(), mTitles, mFragments); + binding.vpContent.setAdapter(mPagerAdapter);//给ViewPager设置适配器 + binding.mainTab.setTabData(mTabEntities); + } + + @Override + protected void onBindListener() { + + binding.mainTab.setOnTabSelectListener(new OnTabSelectListener() { + @Override + public void onTabSelect(int position) { + binding.vpContent.setCurrentItem(position); + if (position == 0 || position == 4) { + ImmersionBar.with(MainActivity.this).fitsSystemWindows(false).transparentStatusBar() + .statusBarDarkFont(false).init(); + } else if (position == 1) { + ImmersionBar.with(MainActivity.this).fitsSystemWindows(false).transparentStatusBar() + .statusBarDarkFont(true).init(); + } else { + ImmersionBar.with(MainActivity.this).fitsSystemWindows(true).statusBarColor(getStatusBarColor()) + .statusBarDarkFont(true).init(); + } + } + + @Override + public void onTabReselect(int position) { + } + }); + + binding.vpContent.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + } + + @Override + public void onPageSelected(int position) { + binding.mainTab.setCurrentTab(position); + if (position == 0 || position == 4) { + ImmersionBar.with(MainActivity.this).fitsSystemWindows(false).transparentStatusBar() + .statusBarDarkFont(false).init(); + } else if (position == 1) { + ImmersionBar.with(MainActivity.this).fitsSystemWindows(false).transparentStatusBar() + .statusBarDarkFont(true).init(); + } else { + ImmersionBar.with(MainActivity.this).fitsSystemWindows(true).statusBarColor(getStatusBarColor()) + .statusBarDarkFont(true).init(); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + }); + } + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/ui/SplashActivity.java b/app/src/main/java/com/tairui/industrial_operation/ui/SplashActivity.java new file mode 100644 index 0000000..fd8a0a3 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/ui/SplashActivity.java @@ -0,0 +1,129 @@ +package com.tairui.industrial_operation.ui; + +import java.lang.ref.WeakReference; + +import com.tairui.industrial_operation.base.BaseActivity; +import com.tairui.industrial_operation.config.AppConfig; +import com.tairui.industrial_operation.databinding.ActivitySplashBinding; +import com.tairui.industrial_operation.ui.account.LoginActivity; +import com.tairui.industrial_operation.util.IntentUtil; +import com.tairui.industrial_operation.util.MyCountDownTimer; +import com.tairui.industrial_operation.util.SingleClickListener; + +import android.text.TextUtils; +import android.view.View; + +public class SplashActivity extends BaseActivity { + + private CountTimer countTimer; + private static final int INTERVAL = 1000; + private boolean isSkipAble = true;// 跳过按钮可以点击 + + @Override + protected Class getBindingClass() { + return ActivitySplashBinding.class; + } + + @Override + protected void onBindListener() { + binding.txtTimerSplash.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + skipTimeCountDown(); + } + }); + } + + @Override + protected void onApplyData() { + goMain(); + } + + private void goMain() { + // KpLog.getInstance().doSaveEventLog(mContext, true); + //KpLog.getInstance().onAppStart(mContext); + + if (null != binding.txtTimerSplash) { + setGone(binding.txtTimerSplash, true); + countTimer = new CountTimer(3 * 1000, INTERVAL); + WeakReference weakTimerCountDowner = new WeakReference<>(countTimer); + if (null != weakTimerCountDowner.get()) { + weakTimerCountDowner.get().start(); + } + } + } + + /** + * 跳过倒计时 + */ + private void skipTimeCountDown() { + if (isSkipAble) { + isSkipAble = false; + if (null != binding.txtTimerSplash) { + binding.txtTimerSplash.setClickable(false); + } + if (null != countTimer) { + countTimer.cancel(); + countTimer = null; + } + if (TextUtils.isEmpty(AppConfig.getInstance().getToken())) { + IntentUtil.startActivity(mContext, LoginActivity.class, null); + finish(); + } else { + gotoMain(); + } + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (null != countTimer) { + countTimer.cancel(); + countTimer = null; + } + } + + private void gotoMain() { + IntentUtil.startActivity(mContext, MainActivity.class, null); + finish(); + } + + /** + * 倒计时 + */ + class CountTimer extends MyCountDownTimer { + + /** + * @param millisInFuture The number of millis in the future from the call + * to {@link #start()} until the countdown is done and {@link #onFinish()} + * is called. + * @param countDownInterval The interval along the way to receive + * {@link #onTick(long)} callbacks. + */ + CountTimer(long millisInFuture, long countDownInterval) { + super(millisInFuture, countDownInterval); + } + + @Override + public void onTick(long millisUntilFinished) { + int time = (int) (millisUntilFinished / INTERVAL) + 1; + if (null != binding.txtTimerSplash) { + String TIME_PASS = "跳过 "; + binding.txtTimerSplash.setText(TIME_PASS + time); + } + } + + @Override + public void onFinish() { + if (TextUtils.isEmpty(AppConfig.getInstance().getToken())) { + IntentUtil.startActivity(mContext, LoginActivity.class, null); + finish(); + } else { + gotoMain(); + } + } + + } + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/ui/account/LoginActivity.java b/app/src/main/java/com/tairui/industrial_operation/ui/account/LoginActivity.java new file mode 100644 index 0000000..65f9664 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/ui/account/LoginActivity.java @@ -0,0 +1,84 @@ +package com.tairui.industrial_operation.ui.account; + +import com.google.gson.JsonObject; +import com.orhanobut.hawk.Hawk; +import com.rxjava.rxlife.RxLife; +import com.tairui.industrial_operation.BuildConfig; +import com.tairui.industrial_operation.base.BaseActivity; +import com.tairui.industrial_operation.databinding.ActivityLoginBinding; +import com.tairui.industrial_operation.entity.Api; +import com.tairui.industrial_operation.entity.Constant; +import com.tairui.industrial_operation.http.OnError; +import com.tairui.industrial_operation.ui.MainActivity; +import com.tairui.industrial_operation.util.IntentUtil; +import com.tairui.industrial_operation.util.SingleClickListener; + +import android.os.Bundle; +import android.text.TextUtils; +import android.view.View; +import rxhttp.RxHttp; + +public class LoginActivity extends BaseActivity { + + @Override + protected Class getBindingClass() { + return ActivityLoginBinding.class; + } + + @Override + protected void onFindView(Bundle savedInstanceState) { + if(BuildConfig.DEBUG){ + setText(binding.inputUserName,"admin"); + setText(binding.inputUserPass,"admin123"); + } + } + + @Override + protected void onBindListener() { + binding.btnLogin.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + checkLogin(); + } + }); + binding.goRegister.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + IntentUtil.startActivity(mContext, RegisterActivity.class, null); + } + }); + } + + private void checkLogin() { + String username = binding.inputUserName.getText().toString().trim(); + if (TextUtils.isEmpty(username)) { + showToast("用户名不能为空"); + return; + } + String userpass = binding.inputUserPass.getText().toString().trim(); + if (TextUtils.isEmpty(userpass)) { + showToast("密码不能为空"); + return; + } + showLoading(); + doLogin(username, userpass); + } + + private void doLogin(String username, String userpass) { + RxHttp.postJson(Api.LOGIN) + .add("username", username) + .add("password", userpass) + .asResponse(JsonObject.class) + .as(RxLife.asOnMain(this)) + .subscribe(data -> { + hideLoading(); + String access_token = data.get("access_token").getAsString(); + Hawk.put(Constant.TOKEN, access_token); + IntentUtil.startActivity(mContext, MainActivity.class, null); + finish(); + }, (OnError) error -> { + hideLoading(); + showToast(error.getErrorMsg()); + }); + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/ui/account/RegisterActivity.java b/app/src/main/java/com/tairui/industrial_operation/ui/account/RegisterActivity.java new file mode 100644 index 0000000..868d3d0 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/ui/account/RegisterActivity.java @@ -0,0 +1,102 @@ +package com.tairui.industrial_operation.ui.account; + +import com.kongzue.dialogx.dialogs.MessageDialog; +import com.rxjava.rxlife.RxLife; +import com.tairui.industrial_operation.R; +import com.tairui.industrial_operation.base.BaseActivity; +import com.tairui.industrial_operation.databinding.ActivityRegisterBinding; +import com.tairui.industrial_operation.entity.Api; +import com.tairui.industrial_operation.http.OnError; +import com.tairui.industrial_operation.util.SingleClickListener; + +import android.text.InputType; +import android.text.TextUtils; +import android.view.View; +import rxhttp.RxHttp; + +public class RegisterActivity extends BaseActivity { + + private boolean showPass = false; + private boolean showConfirmPass = false; + + @Override + protected Class getBindingClass() { + return ActivityRegisterBinding.class; + } + + @Override + protected void onBindListener() { + binding.btnRegister.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + checkRegister(); + } + }); + binding.btnShowPass.setOnClickListener(v -> { + if (showPass) { + binding.inputUserPass.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); + binding.btnShowPass.setImageResource(R.mipmap.ic_hide_pass); + } else { + binding.inputUserPass.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); + binding.btnShowPass.setImageResource(R.mipmap.ic_show_pass); + } + showPass = !showPass; + }); + + binding.btnShowConfirmPass.setOnClickListener(v -> { + if (showConfirmPass) { + binding.inputConfirmPass.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); + binding.btnShowConfirmPass.setImageResource(R.mipmap.ic_hide_pass); + } else { + binding.inputConfirmPass.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); + binding.btnShowConfirmPass.setImageResource(R.mipmap.ic_show_pass); + } + showConfirmPass = !showConfirmPass; + }); + } + + private void checkRegister() { + String username = binding.inputUserName.getText().toString().trim(); + if (TextUtils.isEmpty(username)) { + showToast("用户名不能为空"); + return; + } + String userpass = binding.inputUserPass.getText().toString().trim(); + if (TextUtils.isEmpty(userpass)) { + showToast("密码不能为空"); + return; + } + String confirmPass = binding.inputConfirmPass.getText().toString().trim(); + if (TextUtils.isEmpty(confirmPass)) { + showToast("确认密码不能为空"); + return; + } + if (!userpass.equals(confirmPass)) { + showToast("两次密码输入不一致"); + return; + } + + showLoading(); + doRegister(username, userpass); + } + + private void doRegister(String username, String userpass) { + RxHttp.postJson(Api.REGISTER) + .add("username", username) + .add("password", userpass) + .asResponse(String.class) + .as(RxLife.asOnMain(this)) + .subscribe(data -> { + hideLoading(); + MessageDialog.show("注册成功", "点击确定前往登录页面", "确定") + .setOkButton((baseDialog, v1) -> { + finish(); + return false; + }); + finish(); + }, (OnError) error -> { + hideLoading(); + showToast(error.getErrorMsg()); + }); + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/ui/home/HomeFragment.java b/app/src/main/java/com/tairui/industrial_operation/ui/home/HomeFragment.java new file mode 100644 index 0000000..ce604a4 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/ui/home/HomeFragment.java @@ -0,0 +1,167 @@ +package com.tairui.industrial_operation.ui.home; + +import java.util.ArrayList; +import java.util.List; + +import com.tairui.industrial_operation.R; +import com.tairui.industrial_operation.base.BaseFragment; +import com.tairui.industrial_operation.base.entity.BaseHolderEntity; +import com.tairui.industrial_operation.databinding.FragmentHomeBinding; +import com.tairui.industrial_operation.ui.home.adapter.HomeAdapter; +import com.tairui.industrial_operation.widget.RefreshRecyclerView; + +import android.view.View; +import androidx.recyclerview.widget.LinearLayoutManager; + +public class HomeFragment extends BaseFragment { + + private HomeAdapter adapter; + private int currentPageIndex = 1; + + @Override + protected Class getBindingClass() { + return FragmentHomeBinding.class; + } + + @Override + protected void onFindView(View rootView) { + binding.refreshRecycler.setLayoutManager(new LinearLayoutManager(mContext)); + adapter = new HomeAdapter(new ArrayList<>()); + binding.refreshRecycler.setAdapter(adapter); + } + + @Override + protected void onBindListener() { + binding.refreshRecycler.setRefreshRecyclerListener(new RefreshRecyclerView.RefreshRecyclerListener() { + @Override + public void onRefresh() { + currentPageIndex = 1; + requestData(false); + } + + @Override + public void onLoadmore() { + requestData(true); + } + + @Override + public void onBlankClick() { + binding.refreshRecycler.showLoading(); + currentPageIndex = 1; + requestData(false); + } + }); + adapter.setOnItemClickListener((baseQuickAdapter, view, i) -> { + // Bundle bundle = new Bundle(); + // bundle.putSerializable("data", (Serializable) adapter.getItem(i)); + // IntentUtil.startActivity(mContext, LandInfoDetailActivity.class, bundle); + }); + } + + @Override + protected void onApplyData() { + // binding.refreshRecycler.showLoading(); + // currentPageIndex = 1; + // requestData(false); + + List list = new ArrayList<>(); + BaseHolderEntity bannerItem = new BaseHolderEntity(); + bannerItem.setType(HomeAdapter.TYPE_BANNER); + List picList = new ArrayList<>(); + picList.add(R.mipmap.pic_banner); + picList.add(R.mipmap.pic_banner); + picList.add(R.mipmap.pic_banner); + picList.add(R.mipmap.pic_banner); + bannerItem.setData(picList); + list.add(bannerItem); + + BaseHolderEntity funcItem = new BaseHolderEntity(); + funcItem.setType(HomeAdapter.TYPE_FUNC); + List funcList = new ArrayList<>(); + BaseHolderEntity itemFunc = new BaseHolderEntity(); + itemFunc.setData("智慧种植"); + itemFunc.setSubData(R.mipmap.pic_home_func_1); + funcList.add(itemFunc); + itemFunc = new BaseHolderEntity(); + itemFunc.setData("农事服务"); + itemFunc.setSubData(R.mipmap.pic_home_func_2); + funcList.add(itemFunc); + itemFunc = new BaseHolderEntity(); + itemFunc.setData("金融服务"); + itemFunc.setSubData(R.mipmap.pic_home_func_3); + funcList.add(itemFunc); + itemFunc = new BaseHolderEntity(); + itemFunc.setData("公共品牌"); + itemFunc.setSubData(R.mipmap.pic_home_func_4); + funcList.add(itemFunc); + itemFunc = new BaseHolderEntity(); + itemFunc.setData("溯源查询"); + itemFunc.setSubData(R.mipmap.pic_home_func_5); + funcList.add(itemFunc); + funcItem.setData(funcList); + list.add(funcItem); + + BaseHolderEntity serviceItem = new BaseHolderEntity(); + serviceItem.setType(HomeAdapter.TYPE_SERVICE); + list.add(serviceItem); + + BaseHolderEntity guideItem = new BaseHolderEntity(); + guideItem.setType(HomeAdapter.TYPE_GUIDE); + list.add(guideItem); + + BaseHolderEntity goodsItem = new BaseHolderEntity(); + goodsItem.setType(HomeAdapter.TYPE_GOODS); + List goodsList = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + goodsList.add(String.valueOf(i)); + } + goodsItem.setData(goodsList); + list.add(goodsItem); + + adapter.setNewData(list); + binding.refreshRecycler.setNoMoreData(true); + binding.refreshRecycler.showContent(); + + } + + private void requestData(boolean loadmore) { + // RxHttpNoBodyParam http = RxHttp.get(Api.LAND_RESOURCE_LIST); + // if (!TextUtils.isEmpty(regionCode)) { + // http.add("regionCode", regionCode); + // } + // if (!TextUtils.isEmpty(searchWord)) { + // http.add("landName", searchWord); + // } + // http.add("landType", landTypeId) + // .add("current", currentPageIndex) + // .add("size", Api.SIZE_PAGE) + // .asResponse(LandResourceEntity.class) + // .as(RxLife.asOnMain(this)) + // .subscribe(data -> { + // if (loadmore) { + // binding.refreshRecycler.finishLoadMore(); + // } + // if (!ArrayUtil.isEmpty(data.getRecords())) { + // if (loadmore) { + // adapter.addData(data.getRecords()); + // } else { + // adapter.setNewData(data.getRecords()); + // } + // currentPageIndex += 1; + // if (ArrayUtil.size(data.getRecords()) < Api.SIZE_PAGE) { + // binding.refreshRecycler.setNoMoreData(true); + // } else { + // binding.refreshRecycler.setNoMoreData(false); + // } + // binding.refreshRecycler.showContent(); + // } else { + // binding.refreshRecycler.showError(); + // binding.refreshRecycler.setNoMoreData(true); + // } + // }, (OnError) error -> { + // binding.refreshRecycler.showError(); + // binding.refreshRecycler.setNoMoreData(true); + // PopTip.show(error.getErrorMsg()).iconError(); + // }); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tairui/industrial_operation/ui/home/adapter/BannerAdapter.kt b/app/src/main/java/com/tairui/industrial_operation/ui/home/adapter/BannerAdapter.kt new file mode 100644 index 0000000..b80c6b0 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/ui/home/adapter/BannerAdapter.kt @@ -0,0 +1,34 @@ +package com.tairui.industrial_operation.ui.home.adapter + +import android.view.View +import android.view.ViewGroup +import com.tairui.industrial_operation.R +import com.tairui.industrial_operation.databinding.ItemBannerBinding +import com.zhpan.bannerview.BaseBannerAdapter +import com.zhpan.bannerview.BaseViewHolder + +class BannerAdapter(private val mRoundCorner: Int) : BaseBannerAdapter() { + + override fun createViewHolder( + parent: ViewGroup, + itemView: View, + viewType: Int + ): BaseViewHolder { + return ViewBindingViewHolder(ItemBannerBinding.bind(itemView)) + } + + override fun bindData(holder: BaseViewHolder, data: Int, position: Int, pageSize: Int) { + if (holder is ViewBindingViewHolder) { + holder.viewBinding.bannerImage.setRoundCorner(mRoundCorner) + holder.viewBinding.bannerImage.setImageResource(data) + } + } + + override fun getLayoutId(viewType: Int): Int { + return R.layout.item_banner + } +} + +internal class ViewBindingViewHolder(var viewBinding: ItemBannerBinding) : + BaseViewHolder(viewBinding.root) + diff --git a/app/src/main/java/com/tairui/industrial_operation/ui/home/adapter/HomeAdapter.java b/app/src/main/java/com/tairui/industrial_operation/ui/home/adapter/HomeAdapter.java new file mode 100644 index 0000000..414b2b4 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/ui/home/adapter/HomeAdapter.java @@ -0,0 +1,96 @@ +package com.tairui.industrial_operation.ui.home.adapter; + +import java.util.List; + +import com.chad.library.adapter.base.BaseMultiItemQuickAdapter; +import com.chad.library.adapter.base.BaseQuickAdapter; +import com.chad.library.adapter.base.BaseViewHolder; +import com.tairui.industrial_operation.R; +import com.tairui.industrial_operation.base.entity.BaseHolderEntity; +import com.tairui.industrial_operation.ui.smart_farm.SmartFarmActivity; +import com.tairui.industrial_operation.util.DensityUtils; +import com.tairui.industrial_operation.util.IntentUtil; +import com.tairui.industrial_operation.widget.GridSpacingItemDecoration; +import com.zhpan.bannerview.BannerViewPager; +import com.zhpan.bannerview.constants.IndicatorGravity; +import com.zhpan.bannerview.utils.BannerUtils; +import com.zhpan.indicator.enums.IndicatorSlideMode; +import com.zhpan.indicator.enums.IndicatorStyle; + +import android.graphics.Color; +import android.util.Log; +import android.view.View; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +public class HomeAdapter extends BaseMultiItemQuickAdapter { + + public static final int TYPE_BANNER = 1; + public static final int TYPE_FUNC = 2; + public static final int TYPE_SERVICE = 3; + public static final int TYPE_GUIDE = 4; + public static final int TYPE_GOODS = 5; + + public HomeAdapter(List list) { + super(list); + addItemType(TYPE_BANNER, R.layout.item_home_banner); + addItemType(TYPE_FUNC, R.layout.item_home_func); + addItemType(TYPE_SERVICE, R.layout.item_home_service); + addItemType(TYPE_GUIDE, R.layout.item_home_guide); + addItemType(TYPE_GOODS, R.layout.item_home_goods); + } + + @Override + protected void convert(@NonNull BaseViewHolder holder, BaseHolderEntity entity) { + switch (holder.getItemViewType()) { + case TYPE_BANNER: + BannerViewPager bannerViewPager = holder.getView(R.id.banner_view); + bannerViewPager.setIndicatorSliderGap(BannerUtils.dp2px(6)) + .setScrollDuration(1000) + .setIndicatorGravity(IndicatorGravity.CENTER) + .setOnPageClickListener((clickedView, position) -> Log.e("tongchao", "click pos:" + position)) + .setAdapter(new BannerAdapter(DensityUtils.dp2px(mContext, 12))) + .setIndicatorStyle(IndicatorStyle.ROUND_RECT) + .setIndicatorHeight(DensityUtils.dp2px(mContext, 6)) + .setIndicatorSlideMode(IndicatorSlideMode.SCALE) + .setIndicatorSliderGap(DensityUtils.dp2px(mContext, 6)) + .setIndicatorSliderWidth(DensityUtils.dp2px(mContext, 6), DensityUtils.dp2px(mContext, 16)) + .setIndicatorSliderColor(Color.parseColor("#E5E5E5"), Color.parseColor("#25BF82")) + .create(); + bannerViewPager.refreshData((List) entity.getData()); + break; + case TYPE_FUNC: + RecyclerView funcRecycler = holder.getView(R.id.recycle_view); + funcRecycler.setLayoutManager(new GridLayoutManager(mContext, 5)); + HomeFuncAdapter funcAdapter = new HomeFuncAdapter(); + funcAdapter.setNewData((List) entity.getData()); + funcRecycler.setAdapter(funcAdapter); + funcAdapter.setOnItemClickListener(new OnItemClickListener() { + @Override + public void onItemClick(BaseQuickAdapter baseQuickAdapter, View view, int i) { + if (i == 0) { + IntentUtil.startActivity(mContext, SmartFarmActivity.class); + } + } + }); + break; + case TYPE_SERVICE: + break; + case TYPE_GUIDE: + break; + case TYPE_GOODS: + RecyclerView goodsRecycler = holder.getView(R.id.recycle_view); + goodsRecycler.setLayoutManager(new GridLayoutManager(mContext, 2)); + goodsRecycler.addItemDecoration(new GridSpacingItemDecoration(2, DensityUtils.dp2px(mContext, 12), true)); + HomeGoodsAdapter goodsAdapter = new HomeGoodsAdapter(); + goodsAdapter.setNewData((List) entity.getData()); + goodsRecycler.setAdapter(goodsAdapter); + break; + default: + break; + } + } + +} + diff --git a/app/src/main/java/com/tairui/industrial_operation/ui/home/adapter/HomeFuncAdapter.java b/app/src/main/java/com/tairui/industrial_operation/ui/home/adapter/HomeFuncAdapter.java new file mode 100644 index 0000000..6c17a35 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/ui/home/adapter/HomeFuncAdapter.java @@ -0,0 +1,21 @@ +package com.tairui.industrial_operation.ui.home.adapter; + +import com.chad.library.adapter.base.BaseQuickAdapter; +import com.chad.library.adapter.base.BaseViewHolder; +import com.tairui.industrial_operation.R; +import com.tairui.industrial_operation.base.entity.BaseHolderEntity; + +import androidx.annotation.NonNull; + +public class HomeFuncAdapter extends BaseQuickAdapter { + + public HomeFuncAdapter() { + super(R.layout.item_func); + } + + @Override + protected void convert(@NonNull BaseViewHolder holder, BaseHolderEntity entity) { + holder.setText(R.id.title, (String) entity.getData()); + holder.setImageResource(R.id.img, (int) entity.getSubData()); + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/ui/home/adapter/HomeGoodsAdapter.java b/app/src/main/java/com/tairui/industrial_operation/ui/home/adapter/HomeGoodsAdapter.java new file mode 100644 index 0000000..a0d21f8 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/ui/home/adapter/HomeGoodsAdapter.java @@ -0,0 +1,36 @@ +package com.tairui.industrial_operation.ui.home.adapter; + +import com.chad.library.adapter.base.BaseQuickAdapter; +import com.chad.library.adapter.base.BaseViewHolder; +import com.tairui.industrial_operation.R; +import com.tairui.industrial_operation.widget.RoundBackgroundSpan; + +import android.graphics.Color; +import android.text.Spannable; +import android.text.SpannableString; +import android.widget.TextView; +import androidx.annotation.NonNull; + +public class HomeGoodsAdapter extends BaseQuickAdapter { + + public HomeGoodsAdapter() { + super(R.layout.item_goods); + } + + @Override + protected void convert(@NonNull BaseViewHolder holder, String entity) { + TextView titleTv = holder.getView(R.id.title); + SpannableString spannableString = new SpannableString("溯源耿马普罗斯西红柿沙瓤番茄甄选番茄苗"); + // 创建带圆角和padding的背景Span (参数: 背景色, 文字颜色, 圆角半径, padding) + RoundBackgroundSpan span = new RoundBackgroundSpan( + Color.parseColor("#D3F2E6"), // 浅绿背景 + Color.parseColor("#25BF82"), // 深绿文字 + 8, // 圆角半径 + 10 // padding大小 + ); + + // 应用Span到"溯源"两个字 + spannableString.setSpan(span, 0, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + titleTv.setText(spannableString); + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/ui/my/MyFragment.java b/app/src/main/java/com/tairui/industrial_operation/ui/my/MyFragment.java new file mode 100644 index 0000000..5f0dc39 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/ui/my/MyFragment.java @@ -0,0 +1,24 @@ +package com.tairui.industrial_operation.ui.my; + +import com.tairui.industrial_operation.base.BaseFragment; +import com.tairui.industrial_operation.databinding.FragmentHomeBinding; +import com.tairui.industrial_operation.databinding.FragmentMyBinding; + +import android.os.Bundle; +import android.view.View; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class MyFragment extends BaseFragment { + + @Override + protected Class getBindingClass() { + return FragmentMyBinding.class; + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tairui/industrial_operation/ui/service/ServiceFragment.java b/app/src/main/java/com/tairui/industrial_operation/ui/service/ServiceFragment.java new file mode 100644 index 0000000..201b14e --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/ui/service/ServiceFragment.java @@ -0,0 +1,23 @@ +package com.tairui.industrial_operation.ui.service; + +import com.tairui.industrial_operation.base.BaseFragment; +import com.tairui.industrial_operation.databinding.FragmentServiceBinding; + +import android.os.Bundle; +import android.view.View; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class ServiceFragment extends BaseFragment { + + @Override + protected Class getBindingClass() { + return FragmentServiceBinding.class; + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tairui/industrial_operation/ui/shopping/ShoppingFragment.java b/app/src/main/java/com/tairui/industrial_operation/ui/shopping/ShoppingFragment.java new file mode 100644 index 0000000..2057773 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/ui/shopping/ShoppingFragment.java @@ -0,0 +1,23 @@ +package com.tairui.industrial_operation.ui.shopping; + +import com.tairui.industrial_operation.base.BaseFragment; +import com.tairui.industrial_operation.databinding.FragmentShoppingBinding; + +import android.os.Bundle; +import android.view.View; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class ShoppingFragment extends BaseFragment { + + @Override + protected Class getBindingClass() { + return FragmentShoppingBinding.class; + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tairui/industrial_operation/ui/smart_farm/FieldMonitoringActivity.java b/app/src/main/java/com/tairui/industrial_operation/ui/smart_farm/FieldMonitoringActivity.java new file mode 100644 index 0000000..df28543 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/ui/smart_farm/FieldMonitoringActivity.java @@ -0,0 +1,198 @@ +package com.tairui.industrial_operation.ui.smart_farm; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import com.chad.library.adapter.base.BaseQuickAdapter; +import com.chad.library.adapter.base.BaseViewHolder; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.gyf.immersionbar.ImmersionBar; +import com.kongzue.dialogx.dialogs.PopMenu; +import com.kongzue.dialogx.util.ItemDivider; +import com.tairui.industrial_operation.R; +import com.tairui.industrial_operation.base.BaseActivity; +import com.tairui.industrial_operation.databinding.ActivityFieldMonitoringBinding; +import com.tairui.industrial_operation.ui.smart_farm.entity.FileMonitoringEntity; +import com.tairui.industrial_operation.util.ArrayUtil; +import com.tairui.industrial_operation.util.IntentUtil; +import com.tairui.industrial_operation.util.JsonUtils; +import com.tairui.industrial_operation.util.SingleClickListener; + +import android.os.Bundle; +import android.view.View; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +public class FieldMonitoringActivity extends BaseActivity { + + private List mData; + private FileMonitoringEntity selectGroup; + private FileMonitoringEntity.StationsEntity selectStation; + private FileMonitoringEntity.StationsEntity.MonitorsEntity.MonitorItemEntity selectMonitor; + + private StationAdapter stationAdapter; + + @Override + protected Class getBindingClass() { + return ActivityFieldMonitoringBinding.class; + } + + @Override + protected void initStatusBar() { + ImmersionBar.with(this).fitsSystemWindows(false).transparentStatusBar() + .statusBarDarkFont(true).init(); + } + + @Override + protected void onFindView(Bundle savedInstanceState) { + binding.stationRecycler.setLayoutManager(new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false)); + binding.stationRecycler.setHasFixedSize(true); + stationAdapter = new StationAdapter(); + binding.stationRecycler.setAdapter(stationAdapter); + } + + private void showGroupMenu() { + List menus = new ArrayList<>(); + for (FileMonitoringEntity item : mData) { + menus.add(item.getName()); + } + PopMenu.show(binding.layoutGroup, menus) + .setBackgroundColorRes(R.color.white) + .setOnMenuItemClickListener((dialog, text, index) -> { + selectGroup = mData.get(index); + selectStation = selectGroup.getStations().get(0); + selectMonitor = selectStation.getMonitors().getNormal().get(0); + initView(); + return false; + }).setItemDivider(new ItemDivider(15, 15, 1)); + } + + private void showMonitorMenu() { + List menus = new ArrayList<>(); + for (FileMonitoringEntity.StationsEntity.MonitorsEntity.MonitorItemEntity item : selectStation.getMonitors() + .getNormal()) { + menus.add(item.getName()); + } + PopMenu.show(binding.layoutGroup, menus) + .setBackgroundColorRes(R.color.white) + .setOnMenuItemClickListener((dialog, text, index) -> { + selectMonitor = selectStation.getMonitors().getNormal().get(index); + setText(binding.tvMonitorName, selectMonitor.getName()); + return false; + }).setItemDivider(new ItemDivider(15, 15, 1)); + } + + @Override + protected void onBindListener() { + binding.btnBack.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + onBackPressed(); + } + }); + binding.layoutGroup.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + showGroupMenu(); + } + }); + binding.layoutMonitor.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + showMonitorMenu(); + } + }); + stationAdapter.setOnItemClickListener((baseQuickAdapter, view, i) -> { + selectStation = selectGroup.getStations().get(i); + selectMonitor = selectStation.getMonitors().getNormal().get(0); + stationAdapter.setSelect(i); + setText(binding.tvNormalCount, String.valueOf(ArrayUtil.size(selectStation.getMonitors().getNormal()))); + setText(binding.tvExceptionCount, String.valueOf(ArrayUtil.size(selectStation.getMonitors().getExceptions()))); + setText(binding.tvOfflineCount, String.valueOf(ArrayUtil.size(selectStation.getMonitors().getOffline()))); + setText(binding.tvMonitorName, selectMonitor.getName()); + }); + binding.normalLayout.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + Bundle bundle = new Bundle(); + bundle.putInt("status", 1); + bundle.putSerializable("monitor_list", (Serializable) selectStation.getMonitors().getNormal()); + IntentUtil.startActivity(mContext, MonitorListActivity.class, bundle); + } + }); + binding.exceptionLayout.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + Bundle bundle = new Bundle(); + bundle.putInt("status", 2); + bundle.putSerializable("monitor_list", (Serializable) selectStation.getMonitors().getExceptions()); + IntentUtil.startActivity(mContext, MonitorListActivity.class, bundle); + } + }); + binding.offlineLayout.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + Bundle bundle = new Bundle(); + bundle.putInt("status", 3); + bundle.putSerializable("monitor_list", (Serializable) selectStation.getMonitors().getOffline()); + IntentUtil.startActivity(mContext, MonitorListActivity.class, bundle); + } + }); + } + + @Override + protected void onApplyData() { + String jsonData = JsonUtils.getJsonFromAssets(mContext, "file_monitoring.json"); + mData = new Gson().fromJson(jsonData, new TypeToken>() { + }.getType()); + selectGroup = mData.get(0); + selectStation = selectGroup.getStations().get(0); + selectMonitor = selectStation.getMonitors().getNormal().get(0); + initView(); + } + + private void initView() { + setText(binding.tvGroupName, selectGroup.getName()); + stationAdapter.setNewData(selectGroup.getStations()); + setText(binding.tvNormalCount, String.valueOf(ArrayUtil.size(selectStation.getMonitors().getNormal()))); + setText(binding.tvExceptionCount, String.valueOf(ArrayUtil.size(selectStation.getMonitors().getExceptions()))); + setText(binding.tvOfflineCount, String.valueOf(ArrayUtil.size(selectStation.getMonitors().getOffline()))); + setText(binding.tvMonitorName, selectMonitor.getName()); + } + + private class StationAdapter extends BaseQuickAdapter { + + private int selectIndex = 0; + + public void setSelect(int index) { + selectIndex = index; + notifyDataSetChanged(); + } + + public StationAdapter() { + super(R.layout.item_station); + } + + @Override + protected void convert(@NonNull BaseViewHolder holder, FileMonitoringEntity.StationsEntity entity) { + int pos = holder.getLayoutPosition(); + View rootView = holder.getView(R.id.rootView); + if (selectIndex == pos) { + rootView.setBackgroundResource(R.drawable.bg_container_f5_green_border_raduis_10); + } else { + rootView.setBackgroundResource(R.drawable.bg_container_f5_raduis_10); + } + if (pos % 2 == 0) { + holder.setImageResource(R.id.img, R.mipmap.pic_station_2); + } else { + holder.setImageResource(R.id.img, R.mipmap.pic_station_1); + } + holder.setText(R.id.title, entity.getName()); + + } + } + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/ui/smart_farm/MonitorListActivity.java b/app/src/main/java/com/tairui/industrial_operation/ui/smart_farm/MonitorListActivity.java new file mode 100644 index 0000000..a765f9e --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/ui/smart_farm/MonitorListActivity.java @@ -0,0 +1,104 @@ +package com.tairui.industrial_operation.ui.smart_farm; + +import java.util.List; + +import com.chad.library.adapter.base.BaseQuickAdapter; +import com.chad.library.adapter.base.BaseViewHolder; +import com.gyf.immersionbar.ImmersionBar; +import com.tairui.industrial_operation.R; +import com.tairui.industrial_operation.base.BaseActivity; +import com.tairui.industrial_operation.databinding.ActivityMonitorListBinding; +import com.tairui.industrial_operation.ui.smart_farm.entity.FileMonitoringEntity; +import com.tairui.industrial_operation.util.ArrayUtil; +import com.tairui.industrial_operation.util.SingleClickListener; + +import android.os.Bundle; +import android.view.View; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.LinearLayoutManager; + +public class MonitorListActivity extends BaseActivity { + + private List mData; + private int mStatus = -1; // 1->正常设备 2->异常设备 3->离线设备 + + private MonitorAdapter monitorAdapter; + + @Override + protected Class getBindingClass() { + return ActivityMonitorListBinding.class; + } + + @Override + protected void initStatusBar() { + ImmersionBar.with(this).fitsSystemWindows(false).transparentStatusBar() + .statusBarDarkFont(true).init(); + } + + @Override + protected void onQueryArguments() { + mStatus = getIntent().getIntExtra("status", -1); + mData = (List) + getIntent().getSerializableExtra("monitor_list"); + } + + @Override + protected void onFindView(Bundle savedInstanceState) { + if (mStatus == 1) { + setText(binding.tvTitle, "正常设备"); + } else if (mStatus == 2) { + setText(binding.tvTitle, "异常设备"); + } else { + setText(binding.tvTitle, "离线设备"); + } + if (ArrayUtil.notNull(mData)) { + binding.monitorRecycler.setLayoutManager(new LinearLayoutManager(mContext)); + monitorAdapter = new MonitorAdapter(); + binding.monitorRecycler.setAdapter(monitorAdapter); + monitorAdapter.setNewData(mData); + setGone(binding.blankView, false); + } else { + binding.blankView.setBlankView(R.mipmap.img_no_content, mContext.getResources().getString(R.string.str_no_content)); + setGone(binding.monitorRecycler, false); + setGone(binding.blankView, true); + } + } + + @Override + protected void onBindListener() { + binding.btnBack.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + onBackPressed(); + } + }); + } + + private class MonitorAdapter + extends BaseQuickAdapter { + + public MonitorAdapter() { + super(R.layout.item_monitor); + } + + @Override + protected void convert(@NonNull BaseViewHolder holder, + FileMonitoringEntity.StationsEntity.MonitorsEntity.MonitorItemEntity entity) { + holder.setText(R.id.title, entity.getName()); + if (mStatus == 1) { + holder.setImageResource(R.id.statusDot, R.mipmap.ic_station_status_normal); + holder.setText(R.id.statusLabel, "正常"); + holder.setTextColor(R.id.statusLabel, getResColor(R.color.color_green)); + } else if (mStatus == 2) { + holder.setImageResource(R.id.statusDot, R.mipmap.ic_station_status_exception); + holder.setText(R.id.statusLabel, "异常"); + holder.setTextColor(R.id.statusLabel, getResColor(R.color.color_txt_red)); + } else { + holder.setImageResource(R.id.statusDot, R.mipmap.ic_station_status_offline); + holder.setText(R.id.statusLabel, "离线"); + holder.setTextColor(R.id.statusLabel, getResColor(R.color.color_txt_label)); + } + } + } + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/ui/smart_farm/SmartFarmActivity.java b/app/src/main/java/com/tairui/industrial_operation/ui/smart_farm/SmartFarmActivity.java new file mode 100644 index 0000000..af3b70e --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/ui/smart_farm/SmartFarmActivity.java @@ -0,0 +1,40 @@ +package com.tairui.industrial_operation.ui.smart_farm; + +import com.gyf.immersionbar.ImmersionBar; +import com.tairui.industrial_operation.base.BaseActivity; +import com.tairui.industrial_operation.databinding.ActivitySmartFarmBinding; +import com.tairui.industrial_operation.util.IntentUtil; +import com.tairui.industrial_operation.util.SingleClickListener; + +import android.view.View; + +public class SmartFarmActivity extends BaseActivity { + + @Override + protected Class getBindingClass() { + return ActivitySmartFarmBinding.class; + } + + @Override + protected void initStatusBar() { + ImmersionBar.with(this).fitsSystemWindows(false).transparentStatusBar() + .statusBarDarkFont(true).init(); + } + + @Override + protected void onBindListener() { + binding.btnBack.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + onBackPressed(); + } + }); + binding.layoutFieldMonitoring.setOnClickListener(new SingleClickListener() { + @Override + protected void onSingleClick(View v) { + IntentUtil.startActivity(mContext, FieldMonitoringActivity.class); + } + }); + } + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/ui/smart_farm/entity/FileMonitoringEntity.java b/app/src/main/java/com/tairui/industrial_operation/ui/smart_farm/entity/FileMonitoringEntity.java new file mode 100644 index 0000000..a15e0f5 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/ui/smart_farm/entity/FileMonitoringEntity.java @@ -0,0 +1,109 @@ +package com.tairui.industrial_operation.ui.smart_farm.entity; + +import java.io.Serializable; +import java.util.List; + +import com.google.gson.annotations.SerializedName; + +public class FileMonitoringEntity { + + @SerializedName("name") + private String name; + @SerializedName("stations") + private List stations; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getStations() { + return stations; + } + + public void setStations(List stations) { + this.stations = stations; + } + + public static class StationsEntity { + @SerializedName("name") + private String name; + @SerializedName("monitors") + private MonitorsEntity monitors; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public MonitorsEntity getMonitors() { + return monitors; + } + + public void setMonitors(MonitorsEntity monitors) { + this.monitors = monitors; + } + + public static class MonitorsEntity { + @SerializedName("normal") + private List normal; + @SerializedName("exceptions") + private List exceptions; + @SerializedName("offline") + private List offline; + + public List getNormal() { + return normal; + } + + public void setNormal(List normal) { + this.normal = normal; + } + + public List getExceptions() { + return exceptions; + } + + public void setExceptions(List exceptions) { + this.exceptions = exceptions; + } + + public List getOffline() { + return offline; + } + + public void setOffline(List offline) { + this.offline = offline; + } + + public static class MonitorItemEntity implements Serializable { + @SerializedName("name") + private String name; + @SerializedName("status") + private Integer status; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + } + } + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/util/AppUtil.java b/app/src/main/java/com/tairui/industrial_operation/util/AppUtil.java new file mode 100644 index 0000000..9dce0d4 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/util/AppUtil.java @@ -0,0 +1,24 @@ +package com.tairui.industrial_operation.util; + +import com.tairui.industrial_operation.widget.loading.LoadingDialog; + +import android.content.Context; + +public class AppUtil { + + public static LoadingDialog getLoading(Context context) { + return getLoading(context, 1); + } + + public static LoadingDialog getLoading(Context context, int style) { + if (null != context) { + LoadingDialog loading = new LoadingDialog(context); + loading.setLoadStyle(style); //STYLE_RING = 0; STYLE_LINE = 1; + loading.setLoadingText(null); + loading.setInterceptBack(false); + return loading; + } else { + return null; + } + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/util/ArrayUtil.java b/app/src/main/java/com/tairui/industrial_operation/util/ArrayUtil.java new file mode 100644 index 0000000..72b9e31 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/util/ArrayUtil.java @@ -0,0 +1,33 @@ +package com.tairui.industrial_operation.util; + +import java.util.Collection; +import java.util.Map; + +public class ArrayUtil { + + public static int size(Collection data) { + return data == null ? 0 : data.size(); + } + + public static boolean isEmpty(Collection data) { + return data == null || data.isEmpty(); + } + + public static boolean notNull(Collection data) { + return !isEmpty(data); + } + + + public static int size(Map data) { + return data == null ? 0 : data.size(); + } + + public static boolean isEmpty(Map data) { + return data == null || data.isEmpty(); + } + + public static boolean notNull(Map data) { + return !isEmpty(data); + } + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/util/Base64CoderUtils.java b/app/src/main/java/com/tairui/industrial_operation/util/Base64CoderUtils.java new file mode 100644 index 0000000..6a06801 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/util/Base64CoderUtils.java @@ -0,0 +1,282 @@ +package com.tairui.industrial_operation.util; + +/** + * A Base64 encoder/decoder. + * Base64 的工具类 + */ + +public class Base64CoderUtils { + + // The line separator string of the operating system. + private static final String systemLineSeparator = System + .getProperty("line.separator"); + + // Mapping table from 6-bit nibbles to Base64 characters. + private static char[] map1 = new char[64]; + static { + int i = 0; + for (char c = 'A'; c <= 'Z'; c++) + map1[i++] = c; + for (char c = 'a'; c <= 'z'; c++) + map1[i++] = c; + for (char c = '0'; c <= '9'; c++) + map1[i++] = c; + map1[i++] = '+'; + map1[i++] = '/'; + } + + // Mapping table from Base64 characters to 6-bit nibbles. + private static byte[] map2 = new byte[128]; + static { + for (int i = 0; i < map2.length; i++) + map2[i] = -1; + for (int i = 0; i < 64; i++) + map2[map1[i]] = (byte) i; + } + + /** + * 将一个字符串转为Base64 + */ + public static String encodeString(String s) { + return new String(encode(s.getBytes())); + } + + /** + * Encodes a byte array into Base 64 format and breaks the output into lines + * of 76 characters. This method is compatible with + * sun.misc.BASE64Encoder.encodeBuffer(byte[]). + * + * @param in + * An array containing the data bytes to be encoded. + * @return A String containing the Base64 encoded data, broken into lines. + */ + public static String encodeLines(byte[] in) { + return encodeLines(in, 0, in.length, 76, systemLineSeparator); + } + + /** + * Encodes a byte array into Base 64 format and breaks the output into + * lines. + * + * @param in + * An array containing the data bytes to be encoded. + * @param iOff + * Offset of the first byte in in to be processed. + * @param iLen + * Number of bytes to be processed in in, starting + * at iOff. + * @param lineLen + * Line length for the output data. Should be a multiple of 4. + * @param lineSeparator + * The line separator to be used to separate the output lines. + * @return A String containing the Base64 encoded data, broken into lines. + */ + public static String encodeLines(byte[] in, int iOff, int iLen, + int lineLen, String lineSeparator) { + int blockLen = (lineLen * 3) / 4; + if (blockLen <= 0) + throw new IllegalArgumentException(); + int lines = (iLen + blockLen - 1) / blockLen; + int bufLen = ((iLen + 2) / 3) * 4 + lines * lineSeparator.length(); + StringBuilder buf = new StringBuilder(bufLen); + int ip = 0; + while (ip < iLen) { + int l = Math.min(iLen - ip, blockLen); + buf.append(encode(in, iOff + ip, l)); + buf.append(lineSeparator); + ip += l; + } + return buf.toString(); + } + + /** + * Encodes a byte array into Base64 format. No blanks or line breaks are + * inserted in the output. + * + * @param in + * An array containing the data bytes to be encoded. + * @return A character array containing the Base64 encoded data. + */ + public static char[] encode(byte[] in) { + return encode(in, 0, in.length); + } + + /** + * Encodes a byte array into Base64 format. No blanks or line breaks are + * inserted in the output. + * + * @param in + * An array containing the data bytes to be encoded. + * @param iLen + * Number of bytes to process in in. + * @return A character array containing the Base64 encoded data. + */ + public static char[] encode(byte[] in, int iLen) { + return encode(in, 0, iLen); + } + + /** + * Encodes a byte array into Base64 format. No blanks or line breaks are + * inserted in the output. + * + * @param in + * An array containing the data bytes to be encoded. + * @param iOff + * Offset of the first byte in in to be processed. + * @param iLen + * Number of bytes to process in in, starting at + * iOff. + * @return A character array containing the Base64 encoded data. + */ + public static char[] encode(byte[] in, int iOff, int iLen) { + int oDataLen = (iLen * 4 + 2) / 3; // output length without padding + int oLen = ((iLen + 2) / 3) * 4; // output length including padding + char[] out = new char[oLen]; + int ip = iOff; + int iEnd = iOff + iLen; + int op = 0; + while (ip < iEnd) { + int i0 = in[ip++] & 0xff; + int i1 = ip < iEnd ? in[ip++] & 0xff : 0; + int i2 = ip < iEnd ? in[ip++] & 0xff : 0; + int o0 = i0 >>> 2; + int o1 = ((i0 & 3) << 4) | (i1 >>> 4); + int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); + int o3 = i2 & 0x3F; + out[op++] = map1[o0]; + out[op++] = map1[o1]; + out[op] = op < oDataLen ? map1[o2] : '='; + op++; + out[op] = op < oDataLen ? map1[o3] : '='; + op++; + } + return out; + } + + /** + * Decodes a string from Base64 format. No blanks or line breaks are allowed + * within the Base64 encoded input data. + * + * @param s + * A Base64 String to be decoded. + * @return A String containing the decoded data. + * @throws IllegalArgumentException + * If the input is not valid Base64 encoded data. + */ + public static String decodeString(String s) { + return new String(decode(s)); + } + + /** + * Decodes a byte array from Base64 format and ignores line separators, tabs + * and blanks. CR, LF, Tab and Space characters are ignored in the input + * data. This method is compatible with + * sun.misc.BASE64Decoder.decodeBuffer(String). + * + * @param s + * A Base64 String to be decoded. + * @return An array containing the decoded data bytes. + * @throws IllegalArgumentException + * If the input is not valid Base64 encoded data. + */ + public static byte[] decodeLines(String s) { + char[] buf = new char[s.length() + 3]; + int p = 0; + for (int ip = 0; ip < s.length(); ip++) { + char c = s.charAt(ip); + if (c != ' ' && c != '\r' && c != '\n' && c != '\t') + buf[p++] = c; + } + while ((p % 4) != 0) + buf[p++] = '0'; + + return decode(buf, 0, p); + } + + /** + * Decodes a byte array from Base64 format. No blanks or line breaks are + * allowed within the Base64 encoded input data. + * + * @param s + * A Base64 String to be decoded. + * @return An array containing the decoded data bytes. + * @throws IllegalArgumentException + * If the input is not valid Base64 encoded data. + */ + public static byte[] decode(String s) { + return decode(s.toCharArray()); + } + + /** + * Decodes a byte array from Base64 format. No blanks or line breaks are + * allowed within the Base64 encoded input data. + * + * @param in + * A character array containing the Base64 encoded data. + * @return An array containing the decoded data bytes. + * @throws IllegalArgumentException + * If the input is not valid Base64 encoded data. + */ + public static byte[] decode(char[] in) { + return decode(in, 0, in.length); + } + + /** + * Decodes a byte array from Base64 format. No blanks or line breaks are + * allowed within the Base64 encoded input data. + * + * @param in + * A character array containing the Base64 encoded data. + * @param iOff + * Offset of the first character in in to be + * processed. + * @param iLen + * Number of characters to process in in, starting + * at iOff. + * @return An array containing the decoded data bytes. + * @throws IllegalArgumentException + * If the input is not valid Base64 encoded data. + */ + public static byte[] decode(char[] in, int iOff, int iLen) { + if (iLen % 4 != 0) + throw new IllegalArgumentException( + "Length of Base64 encoded input string is not a multiple of 4."); + while (iLen > 0 && in[iOff + iLen - 1] == '=') + iLen--; + int oLen = (iLen * 3) / 4; + byte[] out = new byte[oLen]; + int ip = iOff; + int iEnd = iOff + iLen; + int op = 0; + while (ip < iEnd) { + int i0 = in[ip++]; + int i1 = in[ip++]; + int i2 = ip < iEnd ? in[ip++] : 'A'; + int i3 = ip < iEnd ? in[ip++] : 'A'; + if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) + throw new IllegalArgumentException( + "Illegal character in Base64 encoded data."); + int b0 = map2[i0]; + int b1 = map2[i1]; + int b2 = map2[i2]; + int b3 = map2[i3]; + if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) + throw new IllegalArgumentException( + "Illegal character in Base64 encoded data."); + int o0 = (b0 << 2) | (b1 >>> 4); + int o1 = ((b1 & 0xf) << 4) | (b2 >>> 2); + int o2 = ((b2 & 3) << 6) | b3; + out[op++] = (byte) o0; + if (op < oLen) + out[op++] = (byte) o1; + if (op < oLen) + out[op++] = (byte) o2; + } + return out; + } + + // Dummy constructor. + private Base64CoderUtils() { + } + +} // end class Base64Coder \ No newline at end of file diff --git a/app/src/main/java/com/tairui/industrial_operation/util/CrashHandler.java b/app/src/main/java/com/tairui/industrial_operation/util/CrashHandler.java new file mode 100644 index 0000000..6fa6ffc --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/util/CrashHandler.java @@ -0,0 +1,178 @@ +package com.tairui.industrial_operation.util; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.reflect.Field; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Looper; +import android.util.Log; + +public class CrashHandler implements Thread.UncaughtExceptionHandler { + private static CrashHandler crashHandler; + private static SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss"); + private static Date curDate = new Date(System.currentTimeMillis());//获取当前时间 + private static String str = formatter.format(curDate); + private static String TAG = "MyCrashHandler"; + //系统默认的UncaughtException处理类 + private Thread.UncaughtExceptionHandler mDefaultHandler; + //程序的Context对象 + private Context mContext; + //用来存储设备信息和异常信息 + private Map infos = new HashMap(); + + @Override + public void uncaughtException(Thread thread, Throwable ex) { + + if (!handleException(ex) && mDefaultHandler != null) { + //如果用户没有处理则让系统默认的异常处理器来处理 + mDefaultHandler.uncaughtException(thread, ex); + } else { + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + Log.e(TAG, "error : ", e); + } + //退出程序 + android.os.Process.killProcess(android.os.Process.myPid()); + System.exit(1); + } + } + + /** + * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. + * + * @param ex + * @return true:如果处理了该异常信息;否则返回false. + */ + private boolean handleException(Throwable ex) { + if (ex == null) { + return false; + } + //使用Toast来显示异常信息 + new Thread() { + @Override + public void run() { + Looper.prepare();//准备发消息的MessageQueue + ToastUtil.showLongToast("很抱歉,程序出现异常,即将退出."); + Looper.loop(); + } + }.start(); + //收集设备参数信息 + collectDeviceInfo(mContext); + //保存日志文件 + saveCrashInfo2File(ex); + return true; + } + + /** + * 保存错误信息到文件中 + * + * @param ex + * @return 返回文件名称, 便于将文件传送到服务器 + */ + private String saveCrashInfo2File(Throwable ex) { + + StringBuffer sb = new StringBuffer(); + for (Map.Entry entry : infos.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + sb.append(key + "=" + value + "\n"); + } + + Writer writer = new StringWriter(); + PrintWriter printWriter = new PrintWriter(writer); + ex.printStackTrace(printWriter); + Throwable cause = ex.getCause(); + while (cause != null) { + cause.printStackTrace(printWriter); + cause = cause.getCause(); + } + printWriter.close(); + String result = writer.toString(); + sb.append(result); + //KpLog.getInstance().onAppCrashed(mContext, sb.toString()); + try { + long timestamp = System.currentTimeMillis(); + String fileName = "crash-" + str + "-" + timestamp + ".txt"; + String path = mContext.getFilesDir() + "/crash"; + File dir = new File(path); + if (!dir.exists()) { + dir.mkdirs(); + } + FileOutputStream fos = new FileOutputStream(path + "/" + fileName); + fos.write(sb.toString().getBytes()); + System.out.println(sb.toString()); + fos.close(); + return fileName; + } catch (Exception e) { + Log.e(TAG, "an error occured while wr iting file...", e); + } + return null; + } + + /** + * 初始化 + * + * @param context + */ + public void init(Context context) { + mContext = context; + //获取系统默认的UncaughtException处理器 + mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); + //设置该CrashHandler为程序的默认处理器 + Thread.setDefaultUncaughtExceptionHandler(this); + } + + /** + * 收集设备参数信息 + * + * @param ctx + */ + public void collectDeviceInfo(Context ctx) { + try { + PackageManager pm = ctx.getPackageManager(); + PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES); + if (pi != null) { + String versionName = pi.versionName == null ? "null" : pi.versionName; + String versionCode = pi.versionCode + ""; + infos.put("versionName", versionName); + infos.put("versionCode", versionCode); + } + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "an error occured when collect package info", e); + } + Field[] fields = Build.class.getDeclaredFields(); + for (Field field : fields) { + try { + field.setAccessible(true); + infos.put(field.getName(), field.get(null).toString()); + Log.d(TAG, field.getName() + " : " + field.get(null)); + } catch (Exception e) { + Log.e(TAG, "an error occured when collect crash info", e); + } + } + } + + private CrashHandler() { + } + + //单例 + public static CrashHandler instance() { + if (crashHandler == null) { + crashHandler = new CrashHandler(); + } + return crashHandler; + } + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/util/DensityUtils.java b/app/src/main/java/com/tairui/industrial_operation/util/DensityUtils.java new file mode 100644 index 0000000..8b94f11 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/util/DensityUtils.java @@ -0,0 +1,89 @@ +package com.tairui.industrial_operation.util; + +import android.content.Context; +import android.util.TypedValue; + +/** + * Created by tongchao on 17/5/22. + */ + +public class DensityUtils { + + private DensityUtils() { + /* cannot be instantiated */ + throw new UnsupportedOperationException("cannot be instantiated"); + } + + private static int _SCREEN_WIDTH = 0; + + /** + * 屏幕的宽,像素 + */ + public static int SCREEN_WIDTH(Context context) { + if (_SCREEN_WIDTH == 0) { + _SCREEN_WIDTH = context.getResources().getDisplayMetrics().widthPixels; + } + return _SCREEN_WIDTH; + } + + private static int _SCREEN_HEIGHT = 0; + + /** + * 屏幕的高,像素 + */ + public static int SCREEN_HEIGHT(Context context) { + if (_SCREEN_HEIGHT == 0) { + _SCREEN_HEIGHT = context.getResources().getDisplayMetrics().heightPixels; + } + return _SCREEN_HEIGHT; + } + + /** + * dp转px + * + * @param context + * @return + */ + public static int dp2px(Context context, float dpVal) { + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + dpVal, context.getResources().getDisplayMetrics()); + } + + /** + * sp转px + * + * @param context + * @return + */ + public static int sp2px(Context context, float spVal) { + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, + spVal, context.getResources().getDisplayMetrics()); + } + + /** + * px转dp + * + * @param context + * @param pxVal + * @return + */ + public static float px2dp(Context context, float pxVal) { + final float scale = context.getResources().getDisplayMetrics().density; + return (pxVal / scale); + } + + /** + * px转sp + * + * @return + */ + public static float px2sp(Context context, float pxVal) { + return (pxVal / context.getResources().getDisplayMetrics().scaledDensity); + } + + public static int dp2sp(Context context, float dpVal) { + return (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, + context.getResources().getDisplayMetrics())); + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/tairui/industrial_operation/util/FontUtil.java b/app/src/main/java/com/tairui/industrial_operation/util/FontUtil.java new file mode 100644 index 0000000..6fe571a --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/util/FontUtil.java @@ -0,0 +1,37 @@ +package com.tairui.industrial_operation.util; + +import android.graphics.Paint; + +/** + * autour : openXu + * date : 2016/7/24 14:12 + * className : FontUtil + * version : 1.0 + * description : 文字相关处理帮助类(自定义控件专用) + */ +public class FontUtil { + /** + * @param paint + * @param str + * @return 返回指定笔和指定字符串的长度 + */ + public static float getFontlength(Paint paint, String str) { + return paint.measureText(str); + } + /** + * @return 返回指定笔的文字高度 + */ + public static float getFontHeight(Paint paint) { + Paint.FontMetrics fm = paint.getFontMetrics(); + return fm.descent - fm.ascent; + } + /** + * @return 返回指定笔离文字顶部的基准距离 + */ + public static float getFontLeading(Paint paint) { + Paint.FontMetrics fm = paint.getFontMetrics(); + return fm.leading- fm.ascent; + } + + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/util/IntentUtil.java b/app/src/main/java/com/tairui/industrial_operation/util/IntentUtil.java new file mode 100644 index 0000000..75c5498 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/util/IntentUtil.java @@ -0,0 +1,72 @@ +package com.tairui.industrial_operation.util; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +/** + * Intent 跳转的 工具类 + */ + +public class IntentUtil { + + /** + * 跳转到对于的 页面 + * + * @param context 上下午对象 + * @param cls 跳转的 目标Activity + * @param bundle 带的数据 + */ + + public static void startActivity(Context context, Class cls, Bundle bundle) { + if (null != context) { + Intent intent = new Intent(); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setClass(context, cls); + if (null != bundle) { + intent.putExtras(bundle); + } + context.startActivity(intent); + } + + } + + /** + * 跳转到对于的 页面 + * + * @param context 上下午对象 + * @param cls 跳转的 目标Activity + * @param bundle 带的数据 + */ + + public static void gotoActivity(Context context, Class cls, Bundle bundle) { + if (null != context) { + Intent intent = new Intent(); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setClass(context, cls); + if (null != bundle) { + intent.putExtras(bundle); + } + context.startActivity(intent); + } + + } + + /** + * 跳转到对于的 页面 + * + * @param context 上下午对象 + * @param cls 跳转的 目标Activity + */ + + public static void startActivity(Context context, Class cls) { + if (null != context) { + Intent intent = new Intent(); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setClass(context, cls); + context.startActivity(intent); + } + + } + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/util/JsonUtils.java b/app/src/main/java/com/tairui/industrial_operation/util/JsonUtils.java new file mode 100644 index 0000000..7a1df4a --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/util/JsonUtils.java @@ -0,0 +1,29 @@ +package com.tairui.industrial_operation.util; + +import java.io.IOException; +import java.io.InputStream; + +import android.content.Context; +import android.util.Log; + +public class JsonUtils { + private static final String TAG = "JsonUtils"; + + // 从 assets 目录读取 JSON 文件内容 + public static String getJsonFromAssets(Context context, String fileName) { + String json = null; + try { + InputStream is = context.getAssets().open(fileName); + int size = is.available(); + byte[] buffer = new byte[size]; + is.read(buffer); + is.close(); + json = new String(buffer, "UTF-8"); + } catch (IOException ex) { + Log.e(TAG, "读取 JSON 文件失败: " + ex.getMessage()); + ex.printStackTrace(); + return null; + } + return json; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tairui/industrial_operation/util/LogUtil.java b/app/src/main/java/com/tairui/industrial_operation/util/LogUtil.java new file mode 100644 index 0000000..5152c67 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/util/LogUtil.java @@ -0,0 +1,76 @@ +package com.tairui.industrial_operation.util; + +import com.tairui.industrial_operation.BuildConfig; + +import android.util.Log; + +/** + * autour : openXu + * date : 2017/11/6 11:36 + * className : LogUtil + * version : 1.0 + * description : log日志 + */ +public class LogUtil { + + public static void i(String TAG, Object msg) { + logLong("i", TAG, msg); + } + public static void d(String TAG, Object msg) { + logLong("d", TAG, msg); + } + public static void v(String TAG, Object msg) { + logLong("v", TAG, msg); + } + public static void w(String TAG, Object msg) { + logLong("w", TAG, msg); + } + public static void e(String TAG, Object msg) { + logLong("e", TAG, msg); + } + + /** + * 日志太长打印不全时,分段打印 + * @param type + * @param tag + * @param msg + */ + private static void logLong(String type, String tag, Object msg) { + String content = (null==msg)?"":msg.toString(); + int maxLength = 1000; + long length = content.length(); + if (length <= maxLength) { + logByType(type, tag, content); + }else { + while (content.length() > maxLength) { + String logContent = content.substring(0, maxLength); + content = content.replace(logContent, ""); + logByType(type, tag, logContent); + } + logByType(type, tag, content); + } + } + /**打印不同颜色日志*/ + private static void logByType(String type, String tag, String content){ + if(BuildConfig.DEBUG) { + switch (type){ + case "i": + Log.i(tag, ""+(null==content?"":content)); + break; + case "d": + Log.d(tag, ""+(null==content?"":content)); + break; + case "v": + Log.v(tag, ""+(null==content?"":content)); + break; + case "w": + Log.w(tag, ""+(null==content?"":content)); + break; + case "e": + Log.e(tag, ""+(null==content?"":content)); + break; + } + } + } + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/util/MyCountDownTimer.java b/app/src/main/java/com/tairui/industrial_operation/util/MyCountDownTimer.java new file mode 100644 index 0000000..8f15298 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/util/MyCountDownTimer.java @@ -0,0 +1,110 @@ +package com.tairui.industrial_operation.util; + +import android.annotation.SuppressLint; +import android.os.Handler; +import android.os.Message; +import android.os.SystemClock; + +/** + * 倒计时 + */ + +public abstract class MyCountDownTimer { + + /** + * Millis since epoch when alarm should stop. + */ + private final long mMillisInFuture; + + /** + * The interval in millis that the user receives callbacks + */ + private final long mCountdownInterval; + + private long mStopTimeInFuture; + + /** + * boolean representing if the timer was cancelled + */ + private boolean mCancelled = false; + + /** + * @param millisInFuture The number of millis in the future from the call + * to {@link #start()} until the countdown is done and {@link #onFinish()} + * is called. + * @param countDownInterval The interval along the way to receive + * {@link #onTick(long)} callbacks. + */ + public MyCountDownTimer(long millisInFuture, long countDownInterval) { + mMillisInFuture = millisInFuture; + mCountdownInterval = countDownInterval; + } + + /** + * Cancel the countdown. + */ + public synchronized final void cancel() { + mCancelled = true; + mHandler.removeMessages(MSG); + } + + /** + * Start the countdown. + */ + public synchronized final MyCountDownTimer start() { + mCancelled = false; + if (mMillisInFuture <= 0) { + onFinish(); + return this; + } + mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture; + mHandler.sendMessage(mHandler.obtainMessage(MSG)); + return this; + } + + + /** + * Callback fired on regular interval. + * + * @param millisUntilFinished The amount of time until finished. + */ + public abstract void onTick(long millisUntilFinished); + + /** + * Callback fired when the time is up. + */ + public abstract void onFinish(); + + + private static final int MSG = 1; + + + // handles counting down + @SuppressLint("HandlerLeak") + private Handler mHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + + synchronized (MyCountDownTimer.this) { + if (mCancelled) { + return; + } + final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime(); +// LogUtil.e("millisLeft = " + millisLeft); + if (millisLeft <= 0) { + onFinish(); + } else { + long lastTickStart = SystemClock.elapsedRealtime(); + onTick(millisLeft); + // take into account user's onTick taking time to execute + long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime(); + // special case: user's onTick took more than interval to + // complete, skip to next interval + while (delay < 0) delay += mCountdownInterval; + sendMessageDelayed(obtainMessage(MSG), delay); + } + } + } + }; +} diff --git a/app/src/main/java/com/tairui/industrial_operation/util/SingleClickListener.java b/app/src/main/java/com/tairui/industrial_operation/util/SingleClickListener.java new file mode 100644 index 0000000..7d4f846 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/util/SingleClickListener.java @@ -0,0 +1,33 @@ +package com.tairui.industrial_operation.util; + +import android.view.View; + +public abstract class SingleClickListener implements View.OnClickListener { + private long mLastClickTime; + private long timeInterval = 1000L; + + public SingleClickListener() { + + } + + public SingleClickListener(long interval) { + this.timeInterval = interval; + } + + @Override + public void onClick(View v) { + long nowTime = System.currentTimeMillis(); + if (nowTime - mLastClickTime > timeInterval) { + // 单次点击事件 + onSingleClick(v); + mLastClickTime = nowTime; + } else { + // 快速点击事件 + //onFastClick(); + } + } + + protected abstract void onSingleClick(View v); + + //protected abstract void onFastClick(); +} diff --git a/app/src/main/java/com/tairui/industrial_operation/util/ToastUtil.java b/app/src/main/java/com/tairui/industrial_operation/util/ToastUtil.java new file mode 100644 index 0000000..2097235 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/util/ToastUtil.java @@ -0,0 +1,93 @@ +package com.tairui.industrial_operation.util; + +import com.tairui.industrial_operation.IOApplication; +import com.tairui.industrial_operation.R; + +import android.content.Context; +import android.graphics.Color; +import android.text.TextUtils; +import android.view.Gravity; +import android.widget.TextView; +import android.widget.Toast; + + +/** + *

+ * Toast 弹窗工具类 + */ + +public class ToastUtil { + + /** + * 显示时常较短的弹窗 + * + * @param strId 弹窗的信息 + */ + public static void showShortToast(int strId) { + String msg = IOApplication.getContext().getResources().getString(strId); + showShortToast(msg); + } + + /** + * 显示时常较短的弹窗 + * + * @param msg 弹窗的信息 + */ + public static void showShortToast(String msg) { + + Context context = IOApplication.getContext(); + if (null != context && !TextUtils.isEmpty(msg)) { + + TextView tv = new TextView(context); + tv.setBackgroundColor(Color.parseColor("#000000")); + tv.setTextColor(Color.WHITE); + tv.setTextSize(16); + tv.setPadding(DensityUtils.dp2px(context, 20), DensityUtils.dp2px(context, 8), DensityUtils.dp2px(context, 20), DensityUtils.dp2px(context, 8)); + + tv.setBackgroundResource(R.drawable.bg_container_toast); + tv.setText(msg); + Toast toast = new Toast(context); + toast.setGravity(Gravity.CENTER, 0, 0); + toast.setDuration(Toast.LENGTH_SHORT); + toast.setView(tv); + toast.show(); + } + } + + /** + * 显示时常较长的弹窗 + * + * @param strId 弹窗的信息 + */ + public static void showLongToast(int strId) { + String msg = IOApplication.getContext().getResources().getString(strId); + showLongToast(msg); + } + + /** + * 显示时常较长的弹窗 + * + * @param msg 弹窗的信息 + */ + public static void showLongToast(String msg) { + + Context context = IOApplication.getContext(); + if (null != context && !TextUtils.isEmpty(msg)) { + + TextView tv = new TextView(context); + tv.setBackgroundColor(Color.parseColor("#000000")); + tv.setTextColor(Color.WHITE); + tv.setTextSize(16); + tv.setPadding(DensityUtils.dp2px(context, 20), DensityUtils.dp2px(context, 8), DensityUtils.dp2px(context, 20), DensityUtils.dp2px(context, 8)); + tv.setBackgroundResource(R.drawable.bg_container_toast); + tv.setText(msg); + + Toast toast = new Toast(context); + toast.setGravity(Gravity.CENTER, 0, 0); + toast.setDuration(Toast.LENGTH_LONG); + toast.setView(tv); + toast.show(); + } + } + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/util/ext/ClickExt.kt b/app/src/main/java/com/tairui/industrial_operation/util/ext/ClickExt.kt new file mode 100644 index 0000000..b23e311 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/util/ext/ClickExt.kt @@ -0,0 +1,53 @@ +package com.tairui.industrial_operation.util.ext + +import android.view.View + +/** + * 作者 : hegaojian + * 时间 : 2020/11/18 + * 描述 : + */ + +/** + * 设置防止重复点击事件 + * @param views 需要设置点击事件的view + * @param interval 时间间隔 默认0.5秒 + * @param onClick 点击触发的方法 + */ +fun setOnclickNoRepeat(vararg views: View?, interval: Long = 500, onClick: (View) -> Unit) { + views.forEach { + it?.clickNoRepeat(interval = interval) { view -> + onClick.invoke(view) + } + } +} + +/** + * 防止重复点击事件 默认0.5秒内不可重复点击 + * @param interval 时间间隔 默认0.5秒 + * @param action 执行方法 + */ +var lastClickTime = 0L +fun View.clickNoRepeat(interval: Long = 500, action: (view: View) -> Unit) { + setOnClickListener { + val currentTime = System.currentTimeMillis() + if (lastClickTime != 0L && (currentTime - lastClickTime < interval)) { + return@setOnClickListener + } + lastClickTime = currentTime + action.invoke(it) + } +} + +/** + * 设置点击事件 + * @param views 需要设置点击事件的view + * @param onClick 点击触发的方法 + */ +fun setOnclick(vararg views: View?, onClick: (View) -> Unit) { + views.forEach { + it?.setOnClickListener { view -> + onClick.invoke(view) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tairui/industrial_operation/util/glide/GlideLoader.java b/app/src/main/java/com/tairui/industrial_operation/util/glide/GlideLoader.java new file mode 100644 index 0000000..7ec0ddc --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/util/glide/GlideLoader.java @@ -0,0 +1,140 @@ +package com.tairui.industrial_operation.util.glide; + +import java.io.File; + +import com.bumptech.glide.GenericTransitionOptions; +import com.bumptech.glide.Glide; +import com.bumptech.glide.RequestBuilder; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.load.resource.bitmap.CenterCrop; +import com.bumptech.glide.load.resource.bitmap.CircleCrop; +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; +import com.bumptech.glide.request.RequestListener; +import com.bumptech.glide.request.RequestOptions; +import com.tairui.industrial_operation.IOApplication; +import com.tairui.industrial_operation.util.DensityUtils; + +import android.content.Context; +import android.widget.ImageView; +import androidx.annotation.AnimRes; + +/** + * @author LYH + * @date 2019-08-20 + **/ +public class GlideLoader { + private static Context context() { + return IOApplication.getContext().getApplicationContext(); + } + + public static void loadRound(ImageView imageView, String url) { + loadRound(imageView, url, -1); + } + + public static void loadRound(ImageView imageView, String url, int placeHolder) { + if (placeHolder > 0) { + RequestOptions options = new RequestOptions() + .placeholder(placeHolder) + .error(placeHolder) + .diskCacheStrategy(DiskCacheStrategy.DATA) + .transforms(new CenterCrop(), new RoundedCorners(DensityUtils.dp2px(context(), 4))); + load(options, imageView, url, -1, null); + } else { + RequestOptions options = new RequestOptions() + .diskCacheStrategy(DiskCacheStrategy.DATA) + .transforms(new CenterCrop(), new RoundedCorners(DensityUtils.dp2px(context(), 4))); + load(options, imageView, url, -1, null); + } + + } + + public static void loadCircle(ImageView imageView, String url) { + loadCircle(imageView, url, -1); + } + + public static void loadCircle(ImageView imageView, String url, int placeHolder) { + if (placeHolder > 0) { + RequestOptions options = new RequestOptions() + .placeholder(placeHolder) + .error(placeHolder) + .diskCacheStrategy(DiskCacheStrategy.DATA) + .transforms(new CenterCrop(), new CircleCrop()); + load(options, imageView, url, -1, null); + } else { + RequestOptions options = new RequestOptions() + .diskCacheStrategy(DiskCacheStrategy.DATA) + .transforms(new CenterCrop(), new CircleCrop()); + load(options, imageView, url, -1, null); + } + + } + + public static void loadFile(ImageView imageView, File file, int placeHolder) { + RequestOptions options = new RequestOptions() + .placeholder(placeHolder) + .error(placeHolder) + .diskCacheStrategy(DiskCacheStrategy.DATA) + .transforms(new CenterCrop()); + RequestBuilder builder = Glide.with(imageView) + .setDefaultRequestOptions(options) + .load(file); + builder.into(imageView); + } + + public static void loadRes(ImageView imageView, int resId) { + RequestOptions options = new RequestOptions() + .diskCacheStrategy(DiskCacheStrategy.DATA) + .transforms(new CenterCrop()); + loadRes(options, imageView, resId, -1, null); + } + + public static void loadOriginal(ImageView imageView, String url) { + loadOriginal(imageView, url, -1); + } + + public static void loadOriginal(ImageView imageView, String url, int placeHolder) { + if (placeHolder > 0) { + RequestOptions options = new RequestOptions() + .placeholder(placeHolder) + .error(placeHolder) + .diskCacheStrategy(DiskCacheStrategy.DATA) + .transforms(new CenterCrop()); + load(options, imageView, url, -1, null); + } else { + RequestOptions options = new RequestOptions() + .diskCacheStrategy(DiskCacheStrategy.DATA) + .transforms(new CenterCrop()); + load(options, imageView, url, -1, null); + } + } + + + private static void loadRes(RequestOptions option, ImageView imageView, int redId, @AnimRes int anim, RequestListener listener) { + RequestBuilder builder = Glide.with(imageView) + .setDefaultRequestOptions(option) + .load(redId); + if (anim != -1) { + builder.transition(GenericTransitionOptions.with(anim)); + } + + if (listener != null) { + builder.listener(listener); + } + builder.into(imageView); + } + + + private static void load(RequestOptions option, ImageView imageView, String url, @AnimRes int anim, RequestListener listener) { + RequestBuilder builder = Glide.with(imageView) + .setDefaultRequestOptions(option) + .load(url); + if (anim != -1) { + builder.transition(GenericTransitionOptions.with(anim)); + } + + if (listener != null) { + builder.listener(listener); + } + builder.into(imageView); + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/CornerImageView.kt b/app/src/main/java/com/tairui/industrial_operation/widget/CornerImageView.kt new file mode 100644 index 0000000..c9337b6 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/CornerImageView.kt @@ -0,0 +1,18 @@ +package com.tairui.industrial_operation.widget + +import android.content.Context +import android.util.AttributeSet + +import androidx.appcompat.widget.AppCompatImageView +import com.zhpan.bannerview.provider.ViewStyleSetter + +class CornerImageView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : AppCompatImageView(context, attrs, defStyleAttr) { + + fun setRoundCorner(radius: Int) { + ViewStyleSetter.applyRoundCorner(this, radius.toFloat()) + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/GridSpacingItemDecoration.java b/app/src/main/java/com/tairui/industrial_operation/widget/GridSpacingItemDecoration.java new file mode 100644 index 0000000..2221123 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/GridSpacingItemDecoration.java @@ -0,0 +1,46 @@ +package com.tairui.industrial_operation.widget; + +import android.graphics.Rect; +import android.view.View; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration { + + private final int spacing; + private final int spanCount; + private final boolean includeEdge; + + public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) { + this.spanCount = spanCount; + this.spacing = spacing; + this.includeEdge = includeEdge; + } + + @Override + public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, + @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { + int position = parent.getChildAdapterPosition(view); + if (position == RecyclerView.NO_POSITION) { + return; + } + + int column = position % spanCount; + + if (includeEdge) { + outRect.left = spacing - column * spacing / spanCount; + outRect.right = (column + 1) * spacing / spanCount; + + if (position < spanCount) { + outRect.top = spacing; + } + outRect.bottom = spacing; + } else { + outRect.left = column * spacing / spanCount; + outRect.right = spacing - (column + 1) * spacing / spanCount; + if (position >= spanCount) { + outRect.top = spacing; + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/RefreshRecyclerView.java b/app/src/main/java/com/tairui/industrial_operation/widget/RefreshRecyclerView.java new file mode 100644 index 0000000..5fdcc1c --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/RefreshRecyclerView.java @@ -0,0 +1,144 @@ +package com.tairui.industrial_operation.widget; + +import com.scwang.smart.refresh.layout.SmartRefreshLayout; +import com.tairui.industrial_operation.R; +import com.tairui.industrial_operation.widget.blankview.BlankView; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView; + +/** + * 封装了带刷新的recyclerview, 支持loading和error + */ +public class RefreshRecyclerView extends FrameLayout { + + private Context mContext; + private SmartRefreshLayout refreshLayout; + private RecyclerView recyclerView; + private View loadingView; + private BlankView blankView; + + private RefreshRecyclerListener listener; + + public RefreshRecyclerView(@NonNull Context context) { + super(context); + init(context); + } + + public RefreshRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public RefreshRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context); + } + + private void init(Context context) { + mContext = context; + View view = LayoutInflater.from(context).inflate(R.layout.view_refresh_recycler, this, true); + if (null != view) { + refreshLayout = view.findViewById(R.id.refreshLayout); + recyclerView = view.findViewById(R.id.recycle_view); + loadingView = view.findViewById(R.id.loading_view); + blankView = view.findViewById(R.id.blank_view); + blankView.setBlankView(R.mipmap.img_no_content, mContext.getResources().getString(R.string.str_no_content)); + + refreshLayout.setOnLoadMoreListener(refreshLayout -> { + if (listener != null) { + listener.onLoadmore(); + } + }); + + refreshLayout.setOnRefreshListener(refreshLayout -> { + if (listener != null) { + listener.onRefresh(); + } + }); + + blankView.setOnBlankViewClickListener(view1 -> { + if (listener != null) { + listener.onBlankClick(); + } + }); + } + } + + public void autoRefresh() { + refreshLayout.autoRefresh(); + } + + public void setEnableLoadMore(boolean loadMore) { + refreshLayout.setEnableLoadMore(loadMore); + } + + public void setEnableRefresh(boolean refresh) { + refreshLayout.setEnableRefresh(refresh); + } + + public void setNoMoreData(boolean noMoreData) { + refreshLayout.setNoMoreData(noMoreData); + } + + public RecyclerView getRecyclerView() { + return recyclerView; + } + + public void finishRefresh() { + refreshLayout.finishRefresh(); + } + + public void finishLoadMore() { + refreshLayout.finishLoadMore(); + } + + public void showContent() { + refreshLayout.setVisibility(View.VISIBLE); + loadingView.setVisibility(View.GONE); + blankView.setVisibility(View.GONE); + } + + public void showError() { + refreshLayout.setVisibility(View.GONE); + loadingView.setVisibility(View.GONE); + blankView.setVisibility(View.VISIBLE); + } + + public void showLoading() { + refreshLayout.setVisibility(View.GONE); + loadingView.setVisibility(View.VISIBLE); + blankView.setVisibility(View.GONE); + } + + public void setLayoutManager(RecyclerView.LayoutManager layoutManager) { + recyclerView.setLayoutManager(layoutManager); + } + + public void setAdapter(RecyclerView.Adapter adapter) { + recyclerView.setAdapter(adapter); + } + + public void setRefreshRecyclerListener(RefreshRecyclerListener _listener) { + listener = _listener; + } + + public void setBlankView(int imageRes, String content) { + blankView.setBlankView(imageRes, content); + } + + public interface RefreshRecyclerListener { + void onRefresh(); + + void onLoadmore(); + + void onBlankClick(); + + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/RoundBackgroundSpan.java b/app/src/main/java/com/tairui/industrial_operation/widget/RoundBackgroundSpan.java new file mode 100644 index 0000000..d0bc7db --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/RoundBackgroundSpan.java @@ -0,0 +1,42 @@ +package com.tairui.industrial_operation.widget; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.text.style.ReplacementSpan; + +public class RoundBackgroundSpan extends ReplacementSpan { + private int backgroundColor; + private int textColor; + private int cornerRadius; + private int padding; + + public RoundBackgroundSpan(int backgroundColor, int textColor, int cornerRadius, int padding) { + this.backgroundColor = backgroundColor; + this.textColor = textColor; + this.cornerRadius = cornerRadius; + this.padding = padding; + } + + @Override + public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) { + return ((int) paint.measureText(text, start, end) + 2 * padding); + } + + @Override + public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) { + float width = paint.measureText(text, start, end); + Paint bgPaint = new Paint(paint); + bgPaint.setColor(backgroundColor); + bgPaint.setStyle(Paint.Style.FILL); + bgPaint.setAntiAlias(true); + + // 绘制圆角矩形背景 + RectF rect = new RectF(x, top, x + width + 2 * padding, bottom); + canvas.drawRoundRect(rect, cornerRadius, cornerRadius, bgPaint); + + // 绘制文字 + paint.setColor(textColor); + canvas.drawText(text, start, end, x + padding, y, paint); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/blankview/BlankView.java b/app/src/main/java/com/tairui/industrial_operation/widget/blankview/BlankView.java new file mode 100644 index 0000000..a5e7314 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/blankview/BlankView.java @@ -0,0 +1,230 @@ +package com.tairui.industrial_operation.widget.blankview; + +import com.bumptech.glide.Glide; +import com.tairui.industrial_operation.R; +import com.tairui.industrial_operation.widget.blankview.interfaces.BlankViewClickListener; + +import android.content.Context; +import android.graphics.Color; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.style.ForegroundColorSpan; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class BlankView extends LinearLayout { + + private ImageView img; + private TextView txt; + + private TextView infoTv; + private Context context; + + private BlankViewClickListener listener; + private boolean isLoading = false;// 是否正在加载 + + + /** + * 设置监听 + * + * @param listener 监听器 + */ + public void setOnBlankViewClickListener(BlankViewClickListener listener) { + this.listener = listener; + } + + + public BlankView(Context context) { + super(context); + this.context = context; + init(context); + } + + public BlankView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + this.context = context; + init(context); + } + + public BlankView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + this.context = context; + init(context); + } + + + /** + * 设置 空白页信息 + * + * @param postAvatarUrl 显示的图片 + * @param describe 显示的文字 + * @param highLightStr 高亮的文字 + */ + public void setBlankView(String postAvatarUrl, @NonNull String describe, @NonNull String highLightStr) { + if (null != img && null != txt) { + Glide.with(context).load(postAvatarUrl).into(img); + if (describe.contains(highLightStr)) { + int startPos = describe.indexOf(highLightStr); + int endPos = startPos + (highLightStr.length()); + if (endPos <= startPos) { + txt.setText(describe); + } else { + txt.setText(renderColorfulStr(describe, startPos, endPos)); + } + } else { + txt.setText(describe); + } +// txt.setText(renderColorfulStr(describe, startPos, endPos)); + } + } + + /** + * 设置 空白页信息 + * + * @param resId 显示的图片 + * @param describe 显示的文字 + * @param highLightStr 高亮的文字 + */ + public void setBlankView(int resId, @NonNull String describe, @NonNull String highLightStr) { + + if (null != img && null != txt) { + Glide.with(context).load(resId).into(img); + if (describe.contains(highLightStr)) { + int startPos = describe.indexOf(highLightStr); + int endPos = startPos + (highLightStr.length()); + if (endPos <= startPos) { + txt.setText(describe); + } else { + txt.setText(renderColorfulStr(describe, startPos, endPos)); + } + } else { + txt.setText(describe); + } + } + } + + + /** + * 设置 空白页信息 + * + * @param resId 显示的图片 + * @param describe 显示的文字 + */ + public void setBlankView(int resId, @NonNull String describe) { + + if (null != img && null != txt) { + Glide.with(context).load(resId).into(img); + txt.setText(describe); + } + } + + /** + * 设置 空白页信息 + * + * @param resId 显示的图片 + * @param describe 显示的文字 + * @param info 提示的内容 + */ + public void setBlankViewWithHint(int resId, @NonNull String describe, @NonNull String info) { + + if (null != img && null != txt && null != infoTv) { + Glide.with(context).load(resId).into(img); + txt.setText(describe); + infoTv.setText(info); + infoTv.setVisibility(VISIBLE); + } + } + + /** + * 设置 空白页信息 + * + * @param resId 默认的图片 + */ + public void setBlankView(int resId) { + if (null != txt && null != img) { + txt.setText(GONE); + Glide.with(context).load(resId).into(img); + } + } + + + /** + * 设置 空白页信息 + * + * @param describe 显示的文字 + * @param highLightStr 高亮的文字 + */ + public void setBlankView(@NonNull String describe, @NonNull String highLightStr) { + + if (null != img && null != txt) { + + if (describe.contains(highLightStr)) { + int startPos = describe.indexOf(highLightStr); + int endPos = startPos + (highLightStr.length()); + if (endPos <= startPos) { + txt.setText(describe); + } else { + txt.setText(renderColorfulStr(describe, startPos, endPos)); + } + } else { + txt.setText(describe); + } +// txt.setText(renderColorfulStr(describe, startPos, endPos)); + } + } + + + /** + * 禁止 可以点击 + */ + public void forbidClick() { + isLoading = false; + } + + /** + * 允许点击 + */ + public void permitClick() { + isLoading = true; + } + + + /** + * 初始化 + * + * @param context 上下文对象 + */ + private void init(Context context) { + View view = LayoutInflater.from(context).inflate(R.layout.layout_blank, this, true); + if (null != view) { + img = view.findViewById(R.id.img_blank); + txt = view.findViewById(R.id.txt_describe_blank); + infoTv = view.findViewById(R.id.txt_info_blank); + view.setOnClickListener(v -> { + if (null != listener && !isLoading) { + listener.onBlankViewClickListener(v); + } + }); + } + } + + /** + * render 带有颜色的 spannableStringBuilder + * + * @param targetStr 对应的文字 + * @param endPos 结束位置 + */ + private SpannableStringBuilder renderColorfulStr(String targetStr, int startPos, int endPos) { + SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(targetStr); + spannableStringBuilder.setSpan(new ForegroundColorSpan(Color.RED), startPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + return spannableStringBuilder; + } + + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/blankview/interfaces/BlankViewClickListener.java b/app/src/main/java/com/tairui/industrial_operation/widget/blankview/interfaces/BlankViewClickListener.java new file mode 100644 index 0000000..3a977bc --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/blankview/interfaces/BlankViewClickListener.java @@ -0,0 +1,11 @@ +package com.tairui.industrial_operation.widget.blankview.interfaces; + +import android.view.View; + + +public interface BlankViewClickListener { + + + void onBlankViewClickListener(View view); + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/charts/BaseChart.java b/app/src/main/java/com/tairui/industrial_operation/widget/charts/BaseChart.java new file mode 100644 index 0000000..b008133 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/charts/BaseChart.java @@ -0,0 +1,354 @@ +package com.tairui.industrial_operation.widget.charts; + +import com.tairui.industrial_operation.R; +import com.tairui.industrial_operation.util.FontUtil; +import com.tairui.industrial_operation.util.LogUtil; +import com.tairui.industrial_operation.widget.charts.anim.AngleEvaluator; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PointF; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.DecelerateInterpolator; + +/** + * autour : openXu + * date : 2017/7/24 10:46 + * className : BaseChart + * version : 1.0 + * description : 图表基类 + */ +public abstract class BaseChart extends View { + + protected String TAG = "BaseChart"; + protected int ScrWidth,ScrHeight; //屏幕宽高 + protected RectF rectChart; //图表矩形 + + protected PointF centerPoint; //chart中心点坐标 + + protected int total; //总数量 + + /**可设置属性*/ + protected int startAngle = -90; //开始的角度 + protected int backColor = Color.parseColor("#F8F8F8"); + protected int lineWidth = 1; //辅助线宽度 + protected String loadingStr = "loading..."; + protected int defColor = Color.parseColor("#00000000"); //底色 + + + protected Paint paint; + protected Paint paintEffect; + protected Paint paintLabel; + + /**动画相关统一属性,也可以设置,需要写set方法*/ + protected long animDuration = 1000; + protected ValueAnimator anim; + protected boolean startDraw = false; + + /**正在加载*/ + protected boolean isLoading = true; + protected boolean drawLine = true; + protected boolean drawBottomLine = true; + protected boolean debug = false; + + + /**手指抬起后,动画*/ + protected ValueAnimator touchAnim; + protected GestureDetector mGestureDetector; + + + public void setLoading(boolean loading) { + isLoading = loading; + invalidate(); + } + + public void setLineWidth(int lineWidth) { + this.lineWidth = lineWidth; + } + + public BaseChart(Context context) { + this(context, null); + } + + public BaseChart(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public BaseChart(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + init(context, attrs, defStyle); + } + + public void setBackColor(int backColor) { + this.backColor = backColor; + } + + public void init() { + TAG = getClass().getSimpleName(); + + DisplayMetrics dm = getResources().getDisplayMetrics(); + ScrHeight = dm.heightPixels; + ScrWidth = dm.widthPixels; + + //画笔初始化 + paint = new Paint(); + paint.setAntiAlias(true); + + paintLabel = new Paint(); + paintLabel.setAntiAlias(true); + + paintEffect = new Paint(); + paintEffect.setAntiAlias(true); + paintEffect.setStyle(Paint.Style.FILL); + paintEffect.setStrokeWidth(lineWidth); + paintEffect.setColor(Color.RED); + + mGestureDetector = new GestureDetector(getContext(), new MyOnGestureListener()); + + } + + public abstract void init(Context context, AttributeSet attrs, int defStyleAttr); + public int getTotal() { + return total; + } + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + centerPoint = new PointF(getMeasuredWidth()/2, getMeasuredHeight()/2); + rectChart = new RectF(getPaddingLeft(),getPaddingTop(),getMeasuredWidth()-getPaddingRight(), + getMeasuredHeight()-getPaddingBottom()); + } + + protected TOUCH_EVENT_TYPE touchEventType = TOUCH_EVENT_TYPE.EVENT_NULL; + /**需要拦截的事件方向*/ + public enum TOUCH_EVENT_TYPE{ + EVENT_NULL, /*不处理事件*/ + EVENT_X, /*拦截X轴方向的事件*/ + EVENT_Y, /*拦截Y轴方向的事件*/ + EVENT_XY /*拦截XY轴方向的事件*/ + } + /**设置事件方向*/ + public void setTouchEventType(TOUCH_EVENT_TYPE touchEventType) { + this.touchEventType = touchEventType; + } + + protected boolean touchEnable = false; //是否超界,控件的大小是否足以显示内容,是否需要滑动来展示。子控件根据计算赋值 + protected float mDownX, mDownY; + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + LogUtil.d(TAG, "dispatchTouchEvent "+touchEventType); + if(!touchEnable){ + LogUtil.w(TAG, "没超界"); + }else if(TOUCH_EVENT_TYPE.EVENT_NULL == touchEventType){ + LogUtil.w(TAG, "不需要处理事件"); + }else if(TOUCH_EVENT_TYPE.EVENT_XY == touchEventType){ + LogUtil.w(TAG, "需要拦截XY方向的事件"); + getParent().requestDisallowInterceptTouchEvent(true); + }else{ + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + mDownX = event.getX(); + mDownY = event.getY(); + if(null!=touchAnim && touchAnim.isRunning()) + touchAnim.cancel(); + getParent().requestDisallowInterceptTouchEvent(true);//ACTION_DOWN的时候,赶紧把事件hold住 + break; + case MotionEvent.ACTION_MOVE: + if(TOUCH_EVENT_TYPE.EVENT_X == touchEventType){ + if(Math.abs(event.getY()-mDownY)> Math.abs(event.getX() - mDownX)) { + getParent().requestDisallowInterceptTouchEvent(false); + LogUtil.i(TAG, "竖直滑动的距离大于水平的时候,将事件还给父控件"); + }else { + getParent().requestDisallowInterceptTouchEvent(true); + LogUtil.i(TAG, "正常请求事件"); + dispatchTouchEvent1(event); + } + }else if(TOUCH_EVENT_TYPE.EVENT_Y == touchEventType){ + if(Math.abs(event.getX() - mDownX)> Math.abs(event.getY()-mDownY)) { + getParent().requestDisallowInterceptTouchEvent(false); + LogUtil.i(TAG, "水平滑动的距离大于竖直的时候,将事件还给父控件"); + }else { + getParent().requestDisallowInterceptTouchEvent(true); + dispatchTouchEvent1(event); + LogUtil.i(TAG, "正常请求事件"); + } + } + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + break; + } + } + return super.dispatchTouchEvent(event); + } + + protected void dispatchTouchEvent1(MotionEvent event){ + + } + + protected PointF lastTouchPoint; + @Override + public boolean onTouchEvent(MotionEvent event) { + if(!touchEnable) + return false; + boolean result = mGestureDetector.onTouchEvent(event); + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + lastTouchPoint = new PointF(event.getX(), event.getY()); + onTouchMoved(lastTouchPoint); + return true; + case MotionEvent.ACTION_MOVE: + int move = 0; + if(TOUCH_EVENT_TYPE.EVENT_X == touchEventType){ + move = (int)(event.getX() - lastTouchPoint.x); + }else if(TOUCH_EVENT_TYPE.EVENT_Y == touchEventType){ + move = (int)(event.getY() - lastTouchPoint.y); + } + LogUtil.i(TAG, "MotionEvent.ACTION_MOVE"+move); + lastTouchPoint.x = (int)event.getX(); + lastTouchPoint.y = (int)event.getY(); + onTouchMoved(lastTouchPoint); + evaluatorFling(move); + invalidate(); + return true; + case MotionEvent.ACTION_UP: + lastTouchPoint.x = 0; + lastTouchPoint.y = 0; + onTouchMoved(null); + return true; + } + return result; + } + + protected void onTouchMoved(PointF point){ + } + + + public void onDraw(Canvas canvas){ + //画布背景 +// canvas.drawColor(backColor); + if(debug) { + drawDebug(canvas); + } + drawDefult(canvas); + if(isLoading){ + drawLoading(canvas); + return; + } +// drawChart(canvas); + if(!startDraw){ + startDraw = true; + startAnimation(canvas); + }else{ + drawChart(canvas); + } + } + + + + public void drawLoading(Canvas canvas) { + paintLabel.setTextSize(35); + float NullTextLead = FontUtil.getFontLeading(paintLabel); + float NullTextHeight = FontUtil.getFontHeight(paintLabel); + float textY = centerPoint.y-NullTextHeight/2+NullTextLead; + paintLabel.setColor(getContext().getResources().getColor(R.color.text_color_def)); + canvas.drawText(loadingStr, centerPoint.x- FontUtil.getFontlength(paintLabel, loadingStr)/2, textY, paintLabel); + } + + /**绘制图表基本框架*/ + public abstract void drawDefult(Canvas canvas); + /**绘制debug辅助线*/ + public void drawDebug(Canvas canvas){ + paint.setStyle(Paint.Style.STROKE);//设置空心 + paint.setStrokeWidth(lineWidth); + //绘制边界--chart区域 + paint.setColor(Color.BLUE); + RectF r = new RectF(0,0,getMeasuredWidth(), getMeasuredHeight()); + canvas.drawRect(r, paint); + paint.setColor(Color.RED); + canvas.drawRect(rectChart, paint); + } + /**绘制图表*/ + public abstract void drawChart(Canvas canvas); + /**创建动画*/ + protected abstract ValueAnimator initAnim(); + /**动画值变化之后计算数据*/ + protected abstract void evaluatorData(ValueAnimator animation); + + + public void setAnimDuration(long duration){ + this.animDuration = duration; + } + protected void startAnimation(Canvas canvas) { + if(anim!=null){ + anim.cancel(); + } + LogUtil.w(TAG, "开始绘制动画"); + anim = initAnim(); + if(anim==null){ + drawChart(canvas); + }else{ + anim.setInterpolator(new AccelerateDecelerateInterpolator()); + anim.addUpdateListener((ValueAnimator animation)->{ + evaluatorData(animation); + invalidate(); + }); + anim.setDuration(animDuration); + anim.start(); + } + } + + + /**动画值变化之后计算数据*/ + protected void evaluatorFling(float velocityAnim){} + protected int mMoveLen = 0; //滚动距离 + protected boolean moveOver = false; //是否滚动到头了 + /**手指松开后滑动动画效果*/ + protected void startFlingAnimation(float velocity) { + if(touchAnim!=null){ + touchAnim.cancel(); + } + touchAnim = ValueAnimator.ofObject(new AngleEvaluator(), 1f, 0f); + touchAnim.setInterpolator(new DecelerateInterpolator()); //越来越慢 + touchAnim.addUpdateListener((ValueAnimator animation)->{ + float velocityAnim = (float)animation.getAnimatedValue(); + evaluatorFling(velocity /100 * velocityAnim); + invalidate(); + }); + touchAnim.setDuration(1000+(int)+Math.abs(velocity)/4); + touchAnim.start(); + } + + + class MyOnGestureListener extends GestureDetector.SimpleOnGestureListener { + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + LogUtil.e(TAG,"onFling------------>velocityX="+velocityX+" velocityY="+velocityY); + if(TOUCH_EVENT_TYPE.EVENT_X == touchEventType){ + startFlingAnimation(velocityX); + }else if(TOUCH_EVENT_TYPE.EVENT_Y == touchEventType){ + startFlingAnimation(velocityY); + } + return false; + } + } + + + + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/charts/ProgressPieChart.java b/app/src/main/java/com/tairui/industrial_operation/widget/charts/ProgressPieChart.java new file mode 100644 index 0000000..25008b4 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/charts/ProgressPieChart.java @@ -0,0 +1,164 @@ +package com.tairui.industrial_operation.widget.charts; + +import java.util.List; + +import com.tairui.industrial_operation.util.DensityUtils; +import com.tairui.industrial_operation.util.FontUtil; +import com.tairui.industrial_operation.widget.charts.anim.AngleEvaluator; +import com.tairui.industrial_operation.widget.charts.bean.ChartLable; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.animation.AccelerateDecelerateInterpolator; + + +/** + * autour : openXu + * date : 2017/7/24 10:46 + * className : ProgressPieChart + * version : 1.0 + * description : 进度图表 + */ +public class ProgressPieChart extends BaseChart { + + private int total; //总数量 + private int progress; //占比数量 + private float animPro; //动画计算的占比数量 + private List lableList; //需要绘制的lable集合 + private int textSpace = DensityUtils.dp2px(getContext(), 3); //文字间距 + + private int raidus; //圆环半径 + private int proSize = DensityUtils.dp2px(getContext(), 8); //圈宽度 + private int proColor = Color.rgb(0, 255, 0); //进度环颜色 + + + public ProgressPieChart(Context context) { + super(context, null); + } + public ProgressPieChart(Context context, AttributeSet attrs) { + super(context, attrs, 0); + } + public ProgressPieChart(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public void init(Context context, AttributeSet attrs, int defStyleAttr) { + startAngle = -90; //开始的角度 + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int width = getMeasuredWidth(); + int height = getMeasuredHeight(); + + if(width lableList){ + this.total = total; + this.progress = progress; + this.lableList = lableList; + startDraw = false; + invalidate(); + } + + /**绘制图表基本框架*/ + @Override + public void drawDefult(Canvas canvas) { + paint.setStyle(Paint.Style.STROKE);//设置空心 + paint.setStrokeWidth(proSize); + paint.setColor(defColor); + canvas.drawCircle(centerPoint.x,centerPoint.y,raidus-proSize/2, paint); + } + + /**绘制图表*/ + @Override + public void drawChart(Canvas canvas) { + if(progress>0) { + paint.setStrokeWidth(proSize); + paint.setColor(proColor); + //1绘制开始圆点 +// paint.setStyle(Paint.Style.FILL); +// canvas.drawCircle(centerPoint.x, centerPoint.y - raidus + proSize / 2, proSize / 2, paint); + //2绘制进度圆弧 + paint.setStyle(Paint.Style.STROKE);//设置空心 + int r = raidus - proSize / 2; + float arcLeft = centerPoint.x - r; + float arcTop = centerPoint.y - r; + float arcRight = centerPoint.x + r; + float arcBottom = centerPoint.y + r; + canvas.drawArc(new RectF(arcLeft, arcTop, arcRight, arcBottom), startAngle, animPro, false, paint); + //3绘制结束圆点 +// paint.setStyle(Paint.Style.FILL); +// float endX = centerPoint.x + (float) (r * Math.cos(Math.toRadians(startAngle + animPro))); +// float endY = centerPoint.y + (float) (r * Math.sin(Math.toRadians(startAngle + animPro))); +// canvas.drawCircle(endX, endY, proSize / 2, paint); + } + //4绘制文字 + if(lableList!=null &&lableList.size()>0){ + float textAllHeight = 0; + float textW, textH, textL; + for(ChartLable lable : lableList){ + paintLabel.setTextSize(lable.getTextSize()); + textH = FontUtil.getFontHeight(paintLabel); + textAllHeight += (textH+textSpace); + } + textAllHeight -= textSpace; + int top = (int)(centerPoint.y-textAllHeight/2); + for(ChartLable lable : lableList){ + paintLabel.setColor(lable.getTextColor()); + paintLabel.setTextSize(lable.getTextSize()); + textW = FontUtil.getFontlength(paintLabel, lable.getText()); + textH = FontUtil.getFontHeight(paintLabel); + textL = FontUtil.getFontLeading(paintLabel); + canvas.drawText(lable.getText(), centerPoint.x-textW/2, top + textL, paintLabel); + top += (textH+textSpace); + } + } + + } + /**创建动画*/ + @Override + protected ValueAnimator initAnim() { + if(progress>0) { + float angle = (float) progress / (float) total * 360; + ValueAnimator anim = ValueAnimator.ofObject(new AngleEvaluator(), 0f, angle); + anim.setInterpolator(new AccelerateDecelerateInterpolator()); + return anim; + } + return null; + } + /**动画值变化之后计算数据*/ + @Override + protected void evaluatorData(ValueAnimator animation) { + animPro = (float)animation.getAnimatedValue(); + } + + + + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/charts/anim/AngleEvaluator.java b/app/src/main/java/com/tairui/industrial_operation/widget/charts/anim/AngleEvaluator.java new file mode 100644 index 0000000..9045060 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/charts/anim/AngleEvaluator.java @@ -0,0 +1,22 @@ +package com.tairui.industrial_operation.widget.charts.anim; + + +import android.animation.TypeEvaluator; + +/** + * autour : openXu + * date : 2017/7/28 9:12 + * className : AngleEvaluator + * version : 1.0 + * description : 角度计算器 + */ +public class AngleEvaluator implements TypeEvaluator { + + @Override + public Object evaluate(float fraction, Object startValue, Object endValue) { + float start = (float)startValue; + float end = (float)endValue; + return start+fraction*(end-start); + } + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/charts/barchart/BarHorizontalChart.java b/app/src/main/java/com/tairui/industrial_operation/widget/charts/barchart/BarHorizontalChart.java new file mode 100644 index 0000000..f65c6ab --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/charts/barchart/BarHorizontalChart.java @@ -0,0 +1,415 @@ +package com.tairui.industrial_operation.widget.charts.barchart; + +import static com.tairui.industrial_operation.widget.charts.BaseChart.TOUCH_EVENT_TYPE.EVENT_Y; + +import java.util.ArrayList; +import java.util.List; + +import com.tairui.industrial_operation.R; +import com.tairui.industrial_operation.util.DensityUtils; +import com.tairui.industrial_operation.util.FontUtil; +import com.tairui.industrial_operation.util.LogUtil; +import com.tairui.industrial_operation.widget.charts.BaseChart; +import com.tairui.industrial_operation.widget.charts.anim.AngleEvaluator; +import com.tairui.industrial_operation.widget.charts.bean.BarBean; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PointF; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.animation.AccelerateDecelerateInterpolator; + +/** + * autour : openXu + * date : 2017/7/24 10:46 + * className : BarHorizontalChart + * version : 1.0 + * description : 横向(柱子水平) 柱状图,支持多柱 + */ +public class BarHorizontalChart extends BaseChart { + + private List> dataList; + private List strList; + + /** + * 可以设置的属性 + */ + + private int barNum = 2; //柱子数量 + private int XMARK_NUM = 5; //X轴刻度数量 + + private int barWidth = DensityUtils.dp2px(getContext(), 15); //柱宽度 + private int barSpace = DensityUtils.dp2px(getContext(), 1); //双柱间的间距 + private int barItemSpace = DensityUtils.dp2px(getContext(), 25);//一组柱之间的间距 + private int[] barColor = new int[] {R.color.color_blue, Color.YELLOW}; //柱颜色 + + private int textSizeCoordinate = (int) getResources().getDimension(R.dimen.text_size_level_small); //坐标文字大小 + private int textColorCoordinate = getResources().getColor(R.color.text_color_light_gray); + private int textSizeTag = (int) getResources().getDimension(R.dimen.text_size_level_small); //数值字体 + private int textColorTag = getResources().getColor(R.color.color_f8f8f8); + + private int textSpace = DensityUtils.dp2px(getContext(), 3); //默认的字与其他的间距 + + /** + * 需要计算相关值 + */ + private int oneBarW; //单个宽度,需要计算 + private int YMARK_MAX_WIDTH = DensityUtils.dp2px(getContext(), 30); //Y轴刻度最大值的宽度 + private int XMARK_H, XMARK_ALL_H; //Y轴刻度间距值 + private int topStartPointY; //从左侧开始绘制的X坐标 + private int minTopPointY; //滑动到最右侧时的X坐标 + + private PointF zeroPoint = new PointF(); //柱状图图体圆点坐标 + + /*字体绘制相关*/ + private int XMARK = 1; //X刻度间隔 + private int XMARK_MAX = 1; //X轴刻度最大值(根据设置的数据自动赋值) + + private int heightCoordinate; + private int leadCoordinate; + private int heightTag; + private int leadTag; + + public BarHorizontalChart(Context context) { + this(context, null); + } + + public BarHorizontalChart(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public BarHorizontalChart(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public void init(Context context, AttributeSet attrs, int defStyleAttr) { + touchEventType = EVENT_Y; + dataList = new ArrayList<>(); + strList = new ArrayList<>(); + } + + /***********************************设置属性set方法**********************************/ + public void setBarNum(int barNum) { + this.barNum = barNum; + } + + public void setXMARK_NUM(int XMARK_NUM) { + this.XMARK_NUM = XMARK_NUM; + } + + public void setBarWidth(int barWidth) { + this.barWidth = barWidth; + } + + public void setBarSpace(int barSpace) { + this.barSpace = barSpace; + } + + public void setBarItemSpace(int barItemSpace) { + this.barItemSpace = barItemSpace; + } + + public void setBarColor(int[] barColor) { + this.barColor = barColor; + } + + public void setTextSizeCoordinate(int textSizeCoordinate) { + this.textSizeCoordinate = textSizeCoordinate; + } + + public void setTextColorCoordinate(int textColorCoordinate) { + this.textColorCoordinate = textColorCoordinate; + } + + public void setTextSizeTag(int textSizeTag) { + this.textSizeTag = textSizeTag; + } + + public void setTextColorTag(int textColorTag) { + this.textColorTag = textColorTag; + } + + public void setTextSpace(int textSpace) { + this.textSpace = textSpace; + } + + /***********************************设置属性set方法over**********************************/ + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + evaluatorByData(); + postInvalidate(); + } + + /***************************************************/ + /** + * 滑动 + */ + @Override + protected void evaluatorFling(float fling) { + LogUtil.i(TAG, "fling = " + fling); + //已经滚动到顶部或者底部 + if (topStartPointY + mMoveLen + fling <= minTopPointY) { + mMoveLen = minTopPointY - topStartPointY; + moveOver = true; + if (null != touchAnim && touchAnim.isRunning()) { + touchAnim.cancel(); + } + } else if (topStartPointY + mMoveLen + fling >= topStartPointY) { + mMoveLen = 0; + moveOver = true; + if (null != touchAnim && touchAnim.isRunning()) { + touchAnim.cancel(); + } + } else { + moveOver = false; + mMoveLen += fling; + } + } + /***************************************************/ + + /** + * 设置数据 + * + * @param dataList 柱状图数据 + * @param strYList Y轴坐标数据 + */ + public void setData(List> dataList, List strYList) { + this.strList.clear(); + this.dataList.clear(); + if (dataList != null) { + this.dataList.addAll(dataList); + } + if (strYList != null) { + this.strList.addAll(strYList); + } + if (rectChart != null) { + evaluatorByData(); + startDraw = false; + invalidate(); + } + } + + /** + * 设置数据后,计算相关值 + */ + private void evaluatorByData() { + total = 0; + //三种字体计算 + paintLabel.setTextSize(textSizeCoordinate); + heightCoordinate = (int) FontUtil.getFontHeight(paintLabel); + leadCoordinate = (int) FontUtil.getFontLeading(paintLabel); + paintLabel.setTextSize(textSizeTag); + heightTag = (int) FontUtil.getFontHeight(paintLabel); + leadTag = (int) FontUtil.getFontLeading(paintLabel); + //zeroPoint坐标 + if (strList != null && strList.size() > 0) { + YMARK_MAX_WIDTH = 0; + paintLabel.setTextSize(textSizeCoordinate); + for (String xStr : strList) { + int tw = (int) FontUtil.getFontlength(paintLabel, xStr); + YMARK_MAX_WIDTH = tw > YMARK_MAX_WIDTH ? tw : YMARK_MAX_WIDTH; + } + } + zeroPoint.x = rectChart.left + YMARK_MAX_WIDTH + textSpace; + zeroPoint.y = heightCoordinate + textSpace; + + /*计算X刻度最大值*/ + XMARK_MAX = 1; + for (List list : dataList) { + for (BarBean bean : list) { + total += bean.getNum(); + XMARK_MAX = (int) bean.getNum() > XMARK_MAX ? (int) bean.getNum() : XMARK_MAX; + } + } + + LogUtil.i(TAG, "真实XMARK_MAX=" + XMARK_MAX); + if (XMARK_MAX <= 5) { + XMARK_MAX = 5; + } + XMARK = XMARK_MAX / XMARK_NUM + 1; + + int MARK = (Integer.parseInt((XMARK + "").substring(0, 1)) + 1); + + if ((XMARK + "").length() == 1) { + //XMARK = 1、2、5、10 + XMARK = (XMARK == 3 || XMARK == 4 || XMARK == 6 || XMARK == 7 || XMARK == 8 || XMARK == 9) ? ((XMARK == 3 || XMARK == 4) ? 5 + : 10) : XMARK; + } else if ((XMARK + "").length() == 2) { + XMARK = MARK * 10; + } else if ((XMARK + "").length() == 3) { + XMARK = MARK * 100; + } else if ((XMARK + "").length() == 4) { + XMARK = MARK * 1000; + } else if ((XMARK + "").length() == 5) { + XMARK = MARK * 10000; + } else if ((XMARK + "").length() == 6) { + XMARK = MARK * 100000; + } + XMARK_MAX = XMARK * XMARK_NUM; + LogUtil.i(TAG, "计算XMARK_MAX=" + XMARK_MAX + " XMARK=" + XMARK); + + //Y轴刻度间距值 + XMARK_ALL_H = (int) (rectChart.right - zeroPoint.x); + XMARK_H = (int) ((float) XMARK_ALL_H / XMARK_NUM); + + //单份柱子(包含多个及其间距)宽度 + oneBarW = (barWidth * barNum) + (barSpace * (barNum - 1)); + + topStartPointY = (int) zeroPoint.y + barItemSpace / 2; //从左侧开始绘制的X坐标 + + int allW = (oneBarW + barItemSpace) * dataList.size(); //每份柱子宽度+item间距(其中包含第一个item左边和最后一个item右边的半份间距) + int contentW = (int) (rectChart.bottom - zeroPoint.y); + touchEnable = allW > contentW; + + if (touchEnable) { + //超出总宽度了 + minTopPointY = -allW + (int) (rectChart.bottom - topStartPointY - barItemSpace); + } + + LogUtil.w(TAG, "柱状图表宽高:" + getMeasuredWidth() + "*" + getMeasuredHeight() + + " 图表范围" + rectChart + " 圆点坐标zeroPoint=" + zeroPoint); + + } + + /** + * 绘制图表基本框架 + */ + @Override + public void drawDefult(Canvas canvas) { + paint.setStyle(Paint.Style.FILL); + paint.setStrokeWidth(lineWidth); + paint.setColor(defColor); + + // canvas.drawLine(zeroPoint.x, zeroPoint.y, rectChart.right, zeroPoint.y, paint); + if (touchEnable) { + //超出高度了 + // canvas.drawLine(zeroPoint.x, zeroPoint.y,zeroPoint.x , rectChart.bottom, paint); + } else { + // canvas.drawLine(zeroPoint.x, zeroPoint.y,zeroPoint.x , zeroPoint.y+(oneBarW+barItemSpace)*dataList.size(), paint); + } + } + + /** + * 绘制debug辅助线 + */ + @Override + public void drawDebug(Canvas canvas) { + super.drawDebug(canvas); + } + + /** + * 绘制图表 + */ + @Override + public void drawChart(Canvas canvas) { + + int topStart = topStartPointY + mMoveLen; + + // LogUtil.i(TAG, "leftStart:"+leftStart+" zezeroPoint"+zeroPoint+" barItemSpace="+barItemSpace+" + // barWidth="+barWidth +" oneBarW="+oneBarW); + + paint.setStyle(Paint.Style.FILL); + LogUtil.w(TAG, ""); + + for (int i = 0; i < dataList.size(); i++) { + String str = strList.get(i); + List list = dataList.get(i); + //柱子开始绘制的Y坐标 + int topY = topStart + (oneBarW + barItemSpace) * i; + //绘制X刻度 + paintLabel.setTextSize(textSizeCoordinate); + paintLabel.setColor(textColorCoordinate); + float tw = FontUtil.getFontlength(paintLabel, str); + canvas.drawText(str, zeroPoint.x - textSpace - tw, topY + oneBarW / 2 - heightCoordinate / 2 + leadCoordinate, paintLabel); + + //绘制辅助线 + // paint.setStrokeWidth(0.5f); + // paint.setColor(defColor); + // canvas.drawLine(zeroPoint.x, topY-15, rectChart.right, topY-15, paint); + + paintLabel.setTextSize(textSizeTag); + paintLabel.setColor(textColorTag); + for (int j = 0; j < list.size(); j++) { + BarBean bean = list.get(j); + + // //绘制辅助线 + // paint.setStrokeWidth(0.5f); + // paint.setColor(defColor); + // canvas.drawLine(zeroPoint.x, topY+barWidth/2, rectChart.right, topY+barWidth/2, paint); + + //绘制柱子 + paint.setColor(barColor[j]); + float right = (zeroPoint.x + XMARK_ALL_H * (bean.getNum() / XMARK_MAX) * animPro); + RectF br = new RectF(zeroPoint.x, topY, right, topY + barWidth); + canvas.drawRect(br, paint); + + //绘制柱子占比 + str = (int) bean.getNum() + ""; + canvas.drawText(str, right + textSpace, topY + barWidth / 2 - heightTag / 2 + leadTag, paintLabel); + + // LogUtil.d(TAG, "绘制bar:"+(br)+" "+getMeasuredWidth()+"*"+getMeasuredHeight()); + topY += (barWidth + barSpace); + lastY = topY; + } + + //绘制辅助线 + // paint.setStrokeWidth(0.5f); + // paint.setColor(defColor); + // canvas.drawLine(zeroPoint.x, topY-barSpace+15, rectChart.right, topY-barSpace+15, paint); + + } + + drawYmark(canvas); + } + + private int lastY; + + private void drawYmark(Canvas canvas) { + //遮盖超出的柱状 + paint.setStyle(Paint.Style.FILL); + paint.setColor(backColor); + canvas.drawRect(new RectF(0, 0, rectChart.right, zeroPoint.y - lineWidth), paint); + canvas.drawRect(new RectF(0, rectChart.bottom, rectChart.right, getMeasuredHeight()), paint); + + paintLabel.setTextSize(textSizeCoordinate); + paintLabel.setColor(textColorCoordinate); + for (int i = 0; i <= XMARK_MAX / XMARK; i++) { + //绘制X刻度 + String textX = (XMARK * i) + ""; + float tw = FontUtil.getFontlength(paintLabel, textX); + // canvas.drawText(textX, (int)(zeroPoint.x + (i*XMARK_H) - tw/2), zeroPoint + // .y-textSpace-heightCoordinate+leadCoordinate,paintLabel); + canvas.drawText(textX, (int) (zeroPoint.x + (i * XMARK_H) - tw / 2), lastY + textSpace + heightCoordinate, paintLabel); + } + } + + private float animPro; //动画计算的占比数量 + + /** + * 创建动画 + */ + @Override + protected ValueAnimator initAnim() { + if (dataList.size() > 0) { + ValueAnimator anim = ValueAnimator.ofObject(new AngleEvaluator(), 0f, 1f); + anim.setInterpolator(new AccelerateDecelerateInterpolator()); + return anim; + } + return null; + } + + /** + * 动画值变化之后计算数据 + */ + @Override + protected void evaluatorData(ValueAnimator animation) { + animPro = (float) animation.getAnimatedValue(); + } + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/charts/barchart/BarVerticalChart.java b/app/src/main/java/com/tairui/industrial_operation/widget/charts/barchart/BarVerticalChart.java new file mode 100644 index 0000000..0ec15b4 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/charts/barchart/BarVerticalChart.java @@ -0,0 +1,539 @@ +package com.tairui.industrial_operation.widget.charts.barchart; + +import static com.tairui.industrial_operation.widget.charts.BaseChart.TOUCH_EVENT_TYPE.EVENT_X; + +import java.util.ArrayList; +import java.util.List; + +import com.tairui.industrial_operation.R; +import com.tairui.industrial_operation.util.DensityUtils; +import com.tairui.industrial_operation.util.FontUtil; +import com.tairui.industrial_operation.util.LogUtil; +import com.tairui.industrial_operation.widget.charts.BaseChart; +import com.tairui.industrial_operation.widget.charts.anim.AngleEvaluator; +import com.tairui.industrial_operation.widget.charts.bean.BarBean; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PointF; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.animation.AccelerateDecelerateInterpolator; + +/** + * autour : openXu + * date : 2017/7/24 10:46 + * className : BarVerticalChart + * version : 1.0 + * description : 竖向(柱子竖直) 柱状图,支持多柱 + */ +public class BarVerticalChart extends BaseChart { + + private List> dataList; + private List strList; + + /** + * 可以设置的属性 + */ + + private boolean showLable = true; //是否显示下面的lable + + private boolean showEnd = true; //当数据超出一屏宽度时,实现最后的数据 + + private int barNum = 2; //柱子数量 + private int YMARK_NUM = 5; //Y轴刻度数量 + + private int barWidth = DensityUtils.dp2px(getContext(), 15); //柱宽度 + private int barSpace = DensityUtils.dp2px(getContext(), 1); //双柱间的间距 + private int barItemSpace = DensityUtils.dp2px(getContext(), 25);//一组柱之间的间距 + private int[] barColor = new int[]{Color.BLUE, Color.YELLOW}; //柱颜色 + + private int rectW = DensityUtils.dp2px(getContext(), 10); //lable矩形宽高 + private int rectH = DensityUtils.dp2px(getContext(), 10); + + private int textSizeCoordinate = (int) getResources().getDimension(R.dimen.text_size_level_small); //坐标文字大小 + private int textColorCoordinate = getResources().getColor(R.color.text_color_light_gray); + private int textSizeTag = (int) getResources().getDimension(R.dimen.text_size_level_small); //数值字体 + private int textColorTag = getResources().getColor(R.color.text_color_light_gray); + private int textSizeLable = (int) getResources().getDimension(R.dimen.text_size_level_small); //lable字体 + private int textColorLable = getResources().getColor(R.color.text_color_light_gray); + + private int textSpace = DensityUtils.dp2px(getContext(), 3); //默认的字与其他的间距 + private int textLableSpace = DensityUtils.dp2px(getContext(), 10); //默认的lable字与其他的间距 + private int lableItemSpace = DensityUtils.dp2px(getContext(), 30); //默认的lable字与其他的间距 + private int lableTopSpace = DensityUtils.dp2px(getContext(), 20); //默认的lable字与其他的间距 + + /** + * 需要计算相关值 + */ + private int oneBarW; //单个宽度,需要计算 + private int YMARK_MAX_WIDTH; //Y轴刻度最大值的宽度 + private int YMARK_H, YMARK_ALL_H; //Y轴刻度间距值 + private int leftStartPointX; //从左侧开始绘制的X坐标 + private int minLeftPointX; //滑动到最右侧时的X坐标 + + private PointF zeroPoint = new PointF(); //柱状图图体圆点坐标 + + private RectF lableRect; + + /*字体绘制相关*/ + private int YMARK = 1; //Y轴刻度最大值(根据设置的数据自动赋值) + private int YMARK_MAX = 1; //Y轴刻度最大值(根据设置的数据自动赋值) + + private int heightCoordinate; + private int leadCoordinate; + private int heightTag; + private int leadTag; + private int heightLable; + private int leadLable; + private float animPro; //动画计算的占比数量 + + public BarVerticalChart(Context context) { + this(context, null); + } + + public BarVerticalChart(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public BarVerticalChart(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public void init(Context context, AttributeSet attrs, int defStyleAttr) { + touchEventType = EVENT_X; + dataList = new ArrayList<>(); + strList = new ArrayList<>(); + } + + public void setShowEnd(boolean showEnd) { + this.showEnd = showEnd; + } + + /***********************************设置属性set方法over**********************************/ + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + evaluatorByData(); + postInvalidate(); + } + + /** + * 设置数据后,计算相关值 + */ + private void evaluatorByData() { + total = 0; + //三种字体计算 + paintLabel.setTextSize(textSizeCoordinate); + heightCoordinate = (int) FontUtil.getFontHeight(paintLabel); + leadCoordinate = (int) FontUtil.getFontLeading(paintLabel); + paintLabel.setTextSize(textSizeTag); + heightTag = (int) FontUtil.getFontHeight(paintLabel); + leadTag = (int) FontUtil.getFontLeading(paintLabel); + paintLabel.setTextSize(textSizeLable); + heightLable = (int) FontUtil.getFontHeight(paintLabel); + leadLable = (int) FontUtil.getFontLeading(paintLabel); + //下方lable范围计算 + int lableH = 0; + if (showLable) { + lableH = (heightLable > rectH ? heightLable : rectH) + lableTopSpace; + lableRect = new RectF(rectChart.left, getMeasuredHeight() - getPaddingBottom() - lableH, + rectChart.right, rectChart.bottom); + } + //Y轴开始绘制的坐标 = 控件高度-padingB-lable高度-X轴刻度字高度-X刻度间距 + zeroPoint.y = getMeasuredHeight() - getPaddingBottom() - lableH - heightCoordinate - textSpace; + + LogUtil.w(TAG, "lableRect:" + lableRect + " lableH=" + lableH + " heightCoordinate=" + heightCoordinate + " textSpace=" + textSpace); + YMARK_MAX = 1; + /*计算Y刻度最大值*/ + for (List list : dataList) { + for (BarBean bean : list) { + total += bean.getNum(); + YMARK_MAX = (int) bean.getNum() > YMARK_MAX ? (int) bean.getNum() : YMARK_MAX; + } + } + LogUtil.i(TAG, "真实YMARK_MAX=" + YMARK_MAX); + if (YMARK_MAX <= 5) + YMARK_MAX = 5; + YMARK = YMARK_MAX / YMARK_NUM + 1; + + int MARK = (Integer.parseInt((YMARK + "").substring(0, 1)) + 1); + + if ((YMARK + "").length() == 1) { + //YMARK = 1、2、5、10 + YMARK = (YMARK == 3 || YMARK == 4 || YMARK == 6 || YMARK == 7 || YMARK == 8 || YMARK == 9) ? ((YMARK == 3 || YMARK == 4) ? 5 : 10) : YMARK; + } else if ((YMARK + "").length() == 2) { + YMARK = MARK * 10; + } else if ((YMARK + "").length() == 3) { + YMARK = MARK * 100; + } else if ((YMARK + "").length() == 4) { + YMARK = MARK * 1000; + } else if ((YMARK + "").length() == 5) { + YMARK = MARK * 10000; + } else if ((YMARK + "").length() == 6) { + YMARK = MARK * 100000; + } + YMARK_MAX = YMARK * YMARK_NUM; + + LogUtil.i(TAG, "计算YMARK_MAX=" + YMARK_MAX + " YMARK=" + YMARK); + + //Y轴刻度间距值 + YMARK_ALL_H = (int) (zeroPoint.y - getPaddingTop() - heightCoordinate); + YMARK_H = (int) ((float) YMARK_ALL_H / YMARK_NUM); + + //单份柱子(包含多个及其间距)宽度 + oneBarW = (barWidth * barNum) + (barSpace * (barNum - 1)); + + int allW = (oneBarW + barItemSpace) * dataList.size(); //每份柱子宽度+item间距(其中包含第一个item左边和最后一个item右边的半份间距) + paintLabel.setTextSize(textSizeCoordinate); + if(drawLine){ + YMARK_MAX_WIDTH = (int) FontUtil.getFontlength(paintLabel, YMARK_MAX + ""); + }else{ + YMARK_MAX_WIDTH = 0; + } + + int contentW = (int) (rectChart.right - rectChart.left - YMARK_MAX_WIDTH - textSpace); + touchEnable = allW > contentW; + + zeroPoint.x = (int) rectChart.left + YMARK_MAX_WIDTH + textSpace; + leftStartPointX = (int) zeroPoint.x + barItemSpace / 2; //从左侧开始绘制的X坐标 + + if (touchEnable) { + //超出总宽度了 + minLeftPointX = -allW + (int) rectChart.right; + mMoveLen = showEnd?(minLeftPointX - leftStartPointX):0; + } else { + minLeftPointX = 0; + mMoveLen = 0; + } + + LogUtil.w(TAG, "柱状图表宽高:" + getMeasuredWidth() + "*" + getMeasuredHeight() + + " 图表范围" + rectChart + " 圆点坐标zeroPoint=" + zeroPoint); + LogUtil.w(TAG, "YMARK_MAX=" + YMARK_MAX + " YMARK=" + YMARK + " YMARK_H=" + YMARK_H + " YMARK_MAX_WIDTH=" + YMARK_MAX_WIDTH); + LogUtil.w(TAG, "minLeftPointX=" + minLeftPointX + " mMoveLen=" + mMoveLen + " leftStartPointX=" + leftStartPointX); + } + + /** + * 绘制图表基本框架 + */ + @Override + public void drawDefult(Canvas canvas) { + paint.setStyle(Paint.Style.FILL); + paint.setStrokeWidth(lineWidth); + paint.setColor(defColor); + //float startX, float startY, float stopX, float stopY + for (int i = 0; i <= YMARK_MAX / YMARK; i++) { + //绘制横向X轴刻度 + float startX = zeroPoint.x; + float startY = zeroPoint.y - (i * YMARK_H); + float stopX = rectChart.right; + float stopY = startY; + if (drawLine) { + canvas.drawLine(startX, startY, stopX, stopY, paint); + } +// 绘制最下面的基线 + if(drawBottomLine&&i==0){ + canvas.drawLine(startX, startY, stopX, stopY, paint); + } + + +// LogUtil.w(TAG, "绘制直线start="+startX+"*"+startY+" stop="+stopX+"*"+stopY); + + //绘制Y刻度 +// paintLabel.setTextSize(textSizeCoordinate); +// String textY = (YMARK*i)+""; +// float tw = FontUtil.getFontlength(paintLabel, textY); +// canvas.drawText(textY, getPaddingLeft()+(YMARK_MAX_WIDTH-tw), startY - heightCoordinate/2+leadCoordinate,paintLabel); + } + + //绘制左侧Y轴 + if (drawLine) { + canvas.drawLine(zeroPoint.x + lineWidth / 2, zeroPoint.y, zeroPoint.x + lineWidth / 2, getPaddingTop(), paint); + } + + if (showLable) { + //绘制lable + List lableList = new ArrayList<>(); + if (dataList.size() > 0) { + for (BarBean bean : dataList.get(0)) { + lableList.add(bean.getName()); + } + } + + paintLabel.setTextSize(textSizeLable); + paintLabel.setColor(textColorLable); + if (lableList != null && lableList.size() > 0) { + int lableAllW = 0; + for (int i = 0; i < lableList.size(); i++) { + lableAllW += (rectW + textLableSpace + FontUtil.getFontlength(paintLabel, lableList.get(i))); + } + lableAllW += (lableItemSpace * lableList.size() - 1); + int lableStart = 0; + if (lableAllW > ((int) (lableRect.right - lableRect.left))) { + //超过了,从left开始画 + lableStart = (int) lableRect.left; + } else { + //没超过,话中间 + lableStart = (int) (lableRect.left + ((lableRect.right - lableRect.left) / 2 - lableAllW / 2)); + } + int left = lableStart; + + for (int i = 0; i < lableList.size(); i++) { + String lable = lableList.get(i); + int tw = (int) FontUtil.getFontlength(paintLabel, lable); + paint.setColor(barColor[i]); + + float rectTop = lableRect.bottom - rectH; + RectF rect = new RectF(left, rectTop, left + rectW, rectTop + rectH); + canvas.drawRect(rect, paint); + + left += (rectW + textLableSpace); + + rectTop = rectTop + rectH / 2 - heightLable / 2 + leadLable; + canvas.drawText(lable, left, rectTop, paintLabel); + + left += (tw + lableItemSpace); + } + } + } + + } + // 是否绘制最基础的线 + public void setBaseLineAndText(boolean drawLine) { + this.drawLine = drawLine; + } + + + // 是否绘制最下面的基线 + public void setBottomLine(boolean drawBottomLine) { + this.drawBottomLine = drawBottomLine; + } + + + /** + * 绘制debug辅助线 + */ + @Override + public void drawDebug(Canvas canvas) { + super.drawDebug(canvas); + paint.setStyle(Paint.Style.STROKE);//设置空心 + paint.setStrokeWidth(lineWidth); + paint.setColor(Color.BLACK); + canvas.drawRect(lableRect, paint); + } + + /** + * 绘制图表 + */ + @Override + public void drawChart(Canvas canvas) { + + int leftStart = leftStartPointX + mMoveLen; + +// LogUtil.i(TAG, "leftStart:"+leftStart+" zezeroPoint"+zeroPoint+" barItemSpace="+barItemSpace+" barWidth="+barWidth +" oneBarW="+oneBarW); + + paint.setStyle(Paint.Style.FILL); + LogUtil.w(TAG, ""); + + for (int i = 0; i < dataList.size(); i++) { + String str = strList.get(i); + List list = dataList.get(i); + //柱子开始绘制的X坐标 + int leftX = leftStart + (oneBarW + barItemSpace) * i; + //绘制X刻度 + paintLabel.setTextSize(textSizeCoordinate); + paintLabel.setColor(textColorCoordinate); + float tw = FontUtil.getFontlength(paintLabel, str); + canvas.drawText(str, leftX + oneBarW / 2 - tw / 2, zeroPoint.y + textSpace + leadCoordinate, paintLabel); +// LogUtil.i(TAG, "绘制X刻度:leftX="+leftX +" "+(leftX + oneBarW/2-tw/2)+"*"+(zeroPoint.y+textSpace + leadCoordinate)); +// LogUtil.i(TAG, "leftStartPointX="+leftStartPointX+" leftStart="+leftStart+ " oneBarW="+oneBarW +" barWidth="+barWidth+" barSpace="+barSpace); + paintLabel.setTextSize(textSizeTag); + paintLabel.setColor(textColorTag); + for (int j = 0; j < list.size(); j++) { + BarBean bean = list.get(j); + paint.setColor(barColor[j]); + float top = (zeroPoint.y - YMARK_ALL_H * (bean.getNum() / YMARK_MAX) * animPro); + RectF br = new RectF(leftX, top, leftX + barWidth, zeroPoint.y); + canvas.drawRect(br, paint); + + str = (int) bean.getNum() + ""; + tw = FontUtil.getFontlength(paintLabel, str); + canvas.drawText(str, leftX + barWidth / 2 - tw / 2, top - textSpace - heightTag + leadTag, paintLabel); + +// LogUtil.d(TAG, "绘制bar:"+(br)+" "+getMeasuredWidth()+"*"+getMeasuredHeight()); + leftX += (barWidth + barSpace); + } + } + if (drawLine) { + drawYmark(canvas); + } + + } + + private void drawYmark(Canvas canvas) { + //遮盖超出的柱状 + paint.setStyle(Paint.Style.FILL); + paint.setColor(backColor); + canvas.drawRect(new RectF(0, 0, zeroPoint.x, lableRect.top), paint); + canvas.drawRect(new RectF(rectChart.right, 0, getMeasuredWidth(), lableRect.top), paint); +// LogUtil.d(TAG, "遮盖超出的柱状:"+new RectF(0,0,zeroPoint.x, lableRect.top)); +// LogUtil.d(TAG, "遮盖超出的柱状:"+new RectF(rectChart.right,0,getMeasuredWidth(), lableRect.top)); + + paintLabel.setTextSize(textSizeCoordinate); + paintLabel.setColor(textColorCoordinate); + for (int i = 0; i <= YMARK_MAX / YMARK; i++) { + //绘制Y刻度 + String textY = (YMARK * i) + ""; + float tw = FontUtil.getFontlength(paintLabel, textY); + canvas.drawText(textY, getPaddingLeft() + (YMARK_MAX_WIDTH - tw), zeroPoint.y - (i * YMARK_H) - heightCoordinate / 2 + leadCoordinate, paintLabel); + } + } + + /** + * 创建动画 + */ + @Override + protected ValueAnimator initAnim() { + if (dataList.size() > 0) { + ValueAnimator anim = ValueAnimator.ofObject(new AngleEvaluator(), 0f, 1f); + anim.setInterpolator(new AccelerateDecelerateInterpolator()); + return anim; + } + return null; + } + + /** + * 动画值变化之后计算数据 + */ + @Override + protected void evaluatorData(ValueAnimator animation) { + animPro = (float) animation.getAnimatedValue(); + } + + /***************************************************/ + + @Override + protected void evaluatorFling(float fling) { + LogUtil.i(TAG, "fling = " + fling); + if (leftStartPointX + mMoveLen + fling <= minLeftPointX) { + //最右侧 + mMoveLen = minLeftPointX - leftStartPointX; + if (null != touchAnim && touchAnim.isRunning()) + touchAnim.cancel(); + } else if (leftStartPointX + mMoveLen + fling >= leftStartPointX) { + //最左侧 + mMoveLen = 0; + if (null != touchAnim && touchAnim.isRunning()) + touchAnim.cancel(); + } else { + mMoveLen += fling; + } + } + + /***********************************设置属性set方法**********************************/ + public void setRectW(int rectW) { + this.rectW = rectW; + } + + public void setRectH(int rectH) { + this.rectH = rectH; + } + + public void setShowLable(boolean showLable) { + this.showLable = showLable; + } + + public void setBarNum(int barNum) { + this.barNum = barNum; + } + + public void setYMARK_NUM(int YMARK_NUM) { + this.YMARK_NUM = YMARK_NUM; + } + + public void setBarWidth(int barWidth) { + this.barWidth = barWidth; + } + + public void setBarSpace(int barSpace) { + this.barSpace = barSpace; + } + + public void setBarItemSpace(int barItemSpace) { + this.barItemSpace = barItemSpace; + } + + public void setBarColor(int[] barColor) { + this.barColor = barColor; + } + + public void setTextSizeCoordinate(int textSizeCoordinate) { + this.textSizeCoordinate = textSizeCoordinate; + } + + public void setTextColorCoordinate(int textColorCoordinate) { + this.textColorCoordinate = textColorCoordinate; + } + + /***************************************************/ + + public void setTextSizeTag(int textSizeTag) { + this.textSizeTag = textSizeTag; + } + + public void setTextColorTag(int textColorTag) { + this.textColorTag = textColorTag; + } + + public void setTextSizeLable(int textSizeLable) { + this.textSizeLable = textSizeLable; + } + + public void setTextColorLable(int textColorLable) { + this.textColorLable = textColorLable; + } + + public void setTextSpace(int textSpace) { + this.textSpace = textSpace; + } + + public void setTextLableSpace(int textLableSpace) { + this.textLableSpace = textLableSpace; + } + + public void setLableItemSpace(int lableItemSpace) { + this.lableItemSpace = lableItemSpace; + } + + public void setLableTopSpace(int lableTopSpace) { + this.lableTopSpace = lableTopSpace; + } + + /** + * 设置数据 + * + * @param dataList 柱状图数据 + * @param strXList X轴坐标数据 + */ + public void setData(List> dataList, List strXList) { + LogUtil.w(TAG, "柱状图设置数据" + dataList); + this.strList.clear(); + this.dataList.clear(); + if (dataList != null) + this.dataList.addAll(dataList); + if (strXList != null) + this.strList.addAll(strXList); + if (rectChart!=null) { + evaluatorByData(); + startDraw = false; + invalidate(); + } + } + + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/charts/bean/BarBean.java b/app/src/main/java/com/tairui/industrial_operation/widget/charts/bean/BarBean.java new file mode 100644 index 0000000..7bff24c --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/charts/bean/BarBean.java @@ -0,0 +1,69 @@ +package com.tairui.industrial_operation.widget.charts.bean; + +import android.graphics.RectF; +import android.graphics.Region; + +/** + * autour : openXu + * date : 2017/7/24 11:04 + * className : PieChartBean + * version : 1.0 + * description : 柱状图数据 + */ +public class BarBean { + + private float num; + private String name; + + //bar绘制矩形 + private RectF arcRect; + //触摸相关 + private Region region; //扇形区域--用于判断手指触摸点是否在此范围 + + @Override + public String toString() { + return "BarBean{" + + "num=" + num + + ", name='" + name + '\'' + + '}'; + } + + public BarBean() { + } + + public BarBean(float num, String name) { + this.num = num; + this.name = name; + } + public float getNum() { + return num; + } + + public void setNum(float num) { + this.num = num; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public RectF getArcRect() { + return arcRect; + } + + public void setArcRect(RectF arcRect) { + this.arcRect = arcRect; + } + + public Region getRegion() { + return region; + } + + public void setRegion(Region region) { + this.region = region; + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/charts/bean/ChartLable.java b/app/src/main/java/com/tairui/industrial_operation/widget/charts/bean/ChartLable.java new file mode 100644 index 0000000..268df3b --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/charts/bean/ChartLable.java @@ -0,0 +1,55 @@ +package com.tairui.industrial_operation.widget.charts.bean; + +/** + * autour : openXu + * date : 2017/10/11 14:08 + * className : ChartLable + * version : 1.0 + * description : 图表lable文字对象,包含文字大小和颜色 + */ +public class ChartLable { + + private String text; + private int textSize; + private int textColor; + + public ChartLable(String text, int textSize, int textColor) { + this.text = text; + this.textSize = textSize; + this.textColor = textColor; + } + public ChartLable() { + } + @Override + public String toString() { + return "ChartLable{" + + "text='" + text + '\'' + + ", textSize=" + textSize + + ", textColor=" + textColor + + '}'; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public int getTextSize() { + return textSize; + } + + public void setTextSize(int textSize) { + this.textSize = textSize; + } + + public int getTextColor() { + return textColor; + } + + public void setTextColor(int textColor) { + this.textColor = textColor; + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/charts/bean/PieChartBean.java b/app/src/main/java/com/tairui/industrial_operation/widget/charts/bean/PieChartBean.java new file mode 100644 index 0000000..6b8ecca --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/charts/bean/PieChartBean.java @@ -0,0 +1,133 @@ +package com.tairui.industrial_operation.widget.charts.bean; + +import java.util.List; + +import android.graphics.PointF; +import android.graphics.RectF; +import android.graphics.Region; + +/** + * autour : openXu + * date : 2017/7/24 11:04 + * className : PieChartBean + * version : 1.0 + * description : 饼状图数据 + */ +public class PieChartBean { + + private float num; + private String name; + + //触摸相关 + private Region region; //扇形区域--用于判断手指触摸点是否在此范围 + private RectF rectLable; //矩形区域--用于判断手指触摸点是否在此范围 + //扇形相关 + private RectF arcRect; + private float startAngle; + private float sweepAngle; + //tag线段 + private List tagLinePoints; + private String tagStr; + private PointF tagTextPoint; + +// //右侧lable +// private RectF colorRect; +// private PointF nameTextPoint; +// private PointF perTextPoint; +// private PointF dashPathPointStart; +// private PointF dashPathPointEnd; + + @Override + public String toString() { + return "RoseChartBean{" + + "num=" + num + + ", name='" + name + '\'' + + '}'; + } + + public PieChartBean(float num, String name) { + this.num = num; + this.name = name; + } + + public String getTagStr() { + return tagStr; + } + + public void setTagStr(String tagStr) { + this.tagStr = tagStr; + } + + + public PointF getTagTextPoint() { + return tagTextPoint; + } + + public void setTagTextPoint(PointF tagTextPoint) { + this.tagTextPoint = tagTextPoint; + } + + public List getTagLinePoints() { + return tagLinePoints; + } + + public void setTagLinePoints(List tagLinePoints) { + this.tagLinePoints = tagLinePoints; + } + + public RectF getArcRect() { + return arcRect; + } + + public void setArcRect(RectF arcRect) { + this.arcRect = arcRect; + } + + public float getStartAngle() { + return startAngle; + } + + public void setStartAngle(float startAngle) { + this.startAngle = startAngle; + } + + public float getSweepAngle() { + return sweepAngle; + } + + public void setSweepAngle(float sweepAngle) { + this.sweepAngle = sweepAngle; + } + + public RectF getRectLable() { + return rectLable; + } + + public void setRectLable(RectF rectLable) { + this.rectLable = rectLable; + } + + public Region getRegion() { + return region; + } + + public void setRegion(Region region) { + this.region = region; + } + + public float getNum() { + return num; + } + + public void setNum(float num) { + this.num = num; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/charts/dashboard/DashBoardItem.java b/app/src/main/java/com/tairui/industrial_operation/widget/charts/dashboard/DashBoardItem.java new file mode 100644 index 0000000..0e39984 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/charts/dashboard/DashBoardItem.java @@ -0,0 +1,67 @@ +package com.tairui.industrial_operation.widget.charts.dashboard; + +/** + * autour : openXu + * date : 2018/4/20 15:14 + * className : DashBoardItem + * version : 1.0 + * description : 请添加类说明 + */ +public class DashBoardItem { + + private int color; + private String lable; + private float num; + + private float angle; + + public DashBoardItem() { + } + public DashBoardItem(int color, String lable, float num) { + this.color = color; + this.lable = lable; + this.num = num; + } + + @Override + public String toString() { + return "DashBoardItem{" + + "color=" + color + + ", lable='" + lable + '\'' + + ", num=" + num + + ", angle=" + angle + + '}'; + } + + public float getAngle() { + return angle; + } + + public void setAngle(float angle) { + this.angle = angle; + } + + public int getColor() { + return color; + } + + public void setColor(int color) { + this.color = color; + } + + public String getLable() { + return lable; + } + + public void setLable(String lable) { + this.lable = lable; + } + + public float getNum() { + return num; + } + + public void setNum(float num) { + this.num = num; + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/charts/dashboard/DashBoardView.java b/app/src/main/java/com/tairui/industrial_operation/widget/charts/dashboard/DashBoardView.java new file mode 100644 index 0000000..6615d68 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/charts/dashboard/DashBoardView.java @@ -0,0 +1,270 @@ +package com.tairui.industrial_operation.widget.charts.dashboard; + +import java.util.ArrayList; +import java.util.List; + +import com.tairui.industrial_operation.R; +import com.tairui.industrial_operation.util.DensityUtils; +import com.tairui.industrial_operation.util.FontUtil; +import com.tairui.industrial_operation.util.LogUtil; +import com.tairui.industrial_operation.widget.charts.BaseChart; +import com.tairui.industrial_operation.widget.charts.anim.AngleEvaluator; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.DashPathEffect; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PathEffect; +import android.graphics.PointF; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.animation.AccelerateDecelerateInterpolator; + + +/** + * autour : openXu + * date : 2018/4/20 15:12 + * className : DashBoardView + * version : 1.0 + * description : 仪表 + */ +public class DashBoardView extends BaseChart { + + private List dataList; + + /**设置的属性*/ + + private int ringWidth = DensityUtils.dp2px(getContext(), 20);//圆环宽度 + + private int textSizeCoordinate = (int)getResources().getDimension(R.dimen.ts_barchart_x); + private int textSizeLable = (int)getResources().getDimension(R.dimen.ts_barchart_lable); + private int textColorCoordinate = getResources().getColor(R.color.text_color_light_gray); + private int textColorLable = getResources().getColor(R.color.text_color_def); + + + private int textXSpace = DensityUtils.dp2px(getContext(), 6); + private int textLableSpace = DensityUtils.dp2px(getContext(), 10); + //小圆圈半径 + private int lableCircleSize = DensityUtils.dp2px(getContext(), 4); + //指针半径 + private int centerPointSize = DensityUtils.dp2px(getContext(), 5); + + /**常量*/ + private final float startAngle = 180; + private final float endAngle = 360; + + /**计算*/ + private PointF arcCenter; + private float arcRaidus; //饼状图半径 + private float tagMaxW; + + + public DashBoardView(Context context) { + super(context, null); + } + public DashBoardView(Context context, AttributeSet attrs) { + super(context, attrs, 0); + } + public DashBoardView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public void init(Context context, AttributeSet attrs, int defStyleAttr) { + dataList = new ArrayList<>(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + setMeasuredDimension(widthSize, heightSize); + calculatData(); + } + + /***********************************设置属性set方法**********************************/ + public void setData(List dataList) { + this.dataList.clear(); + if(dataList!=null) + this.dataList.addAll(dataList); + calculatData(); + } + private float progress; + private float progressAnim; + public void setPro(float progress){ + this.progress = progress; + invalidate(); + } +/***********************************设置属性set方法over**********************************/ + + public void calculatData() { + total = 0; + float maxLableLength = 0; //lable最长的长度 + paintLabel.setTextSize(textSizeLable); + for(DashBoardItem bean : dataList){ + total += bean.getNum(); + float l = FontUtil.getFontlength(paintLabel, bean.getLable()); + maxLableLength = maxLableLength>l?maxLableLength:l; + } + //计算每个部分的角度 + float allAngle = endAngle - startAngle; + for(DashBoardItem bean : dataList){ + bean.setAngle(allAngle * (bean.getNum()/total*1.0f)); + } + + //lable所占用的宽度 + maxLableLength = maxLableLength + textLableSpace + lableCircleSize * 2; + + int widthSize = getMeasuredWidth(); + int heightSize = getMeasuredHeight(); + + paintLabel.setTextSize(textSizeCoordinate); + float xLableSize = FontUtil.getFontHeight(paint); + + arcCenter = new PointF( + getPaddingLeft()+(widthSize - getPaddingLeft() - maxLableLength -getPaddingRight())/2, + heightSize - xLableSize - textXSpace- getPaddingBottom() + ); + float raidusX = arcCenter.x - getPaddingLeft(); + float raidusY = arcCenter.y - getPaddingTop(); + arcRaidus = raidusX0){ + startDraw = false; + invalidate(); + } + } + + /**绘制图表基本框架*/ + @Override + public void drawDefult(Canvas canvas) { + /* paint.setStyle(Paint.Style.FILL); + paint.setColor(defColor); + canvas.drawCircle(centerPoint.x,centerPoint.y,chartRaidus, paint); + if(ringWidth>0){ //绘制空心圆圈 + paint.setColor(backColor); + canvas.drawCircle(centerPoint.x,centerPoint.y,chartRaidus-ringWidth, paint); + }*/ + } + /**绘制debug辅助线*/ + @Override + public void drawDebug(Canvas canvas) { + super.drawDebug(canvas); + paint.setColor(Color.RED); + canvas.drawRect(rectChart, paint); + } + @Override + public void drawChart(Canvas canvas) { +// paint.setStrokeWidth(ringWidth); + if(null==dataList || dataList.size()<=0) + return; + + paint.setStyle(Paint.Style.FILL);//设置空心 + float angle = startAngle; + + float lableTop = getPaddingTop(); + paintLabel.setTextSize(textSizeLable); + paintLabel.setColor(textColorLable); + float lableLead = FontUtil.getFontLeading(paintLabel); + float lableHeight = FontUtil.getFontHeight(paintLabel); + float lableCrc = (lableHeight - lableCircleSize*2)/2; +// canvas.drawArc(rectChart, 180, 180, false, paint); + for(DashBoardItem bean : dataList){ + paint.setColor(bean.getColor()); + LogUtil.w(TAG, "绘制扇形"+bean); + canvas.drawArc(rectChart, angle, bean.getAngle(), true, paint); + angle += bean.getAngle(); + + //绘制lable + canvas.drawCircle(rectChart.right+lableCircleSize, lableTop+lableCrc+lableCircleSize, lableCircleSize, paint); + canvas.drawText(bean.getLable(), rectChart.right+lableCircleSize*2+textLableSpace, lableTop + lableLead, paintLabel); + lableTop += lableHeight+DensityUtils.dp2px(getContext(),5); + } + + //覆盖 + paint.setColor(Color.WHITE); + canvas.drawCircle(arcCenter.x, arcCenter.y, arcRaidus - ringWidth, paint); + + //绘制x坐标 + paintLabel.setTextSize(textSizeCoordinate); + paintLabel.setColor(textColorCoordinate); + float xLead = FontUtil.getFontLeading(paintLabel); + float xLength = FontUtil.getFontlength(paintLabel, "0"); + + canvas.drawText("0", rectChart.left+(ringWidth-xLength)/2, + arcCenter.y + textXSpace+xLead, paintLabel); + xLength = FontUtil.getFontlength(paintLabel, "0分钟"); + canvas.drawText("0分钟", + rectChart.right-(Math.abs((ringWidth-xLength)/2))-(xLength dataList; + private List lableList; //需要绘制的lable集合 + + /**设置的属性*/ + private boolean showZeroPart = false; //如果某部分占比为0, 是否显示 + private int centerLableSpace; + //圆环宽度,如果值>0,则为空心圆环,内环为白色,可以在内环中绘制字 + private int ringWidth; + private int lineLenth = 0; // 设置为0 + private int outSpace = 0; // 设置为0 + private int textSpace = 0; // 设置为0 + + private PieChartLayout.TAG_TYPE tagType; //TAG展示类型 + private PieChartLayout.TAG_MODUL tagModul; //TAG展示位置 + private int tagTextSize; //tag文字大小 + private int tagTextColor; + + /**计算*/ + private int chartSize; //饼状图大小 + private int chartRaidus; //饼状图半径 + private float tagMaxW; + + private Paint paintSelected; + + //RGB颜色数组 + private int arrColorRgb[][]; + + + public PieChart(Context context) { + super(context, null); + } + public PieChart(Context context, AttributeSet attrs) { + super(context, attrs, 0); + } + public PieChart(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public void init(Context context, AttributeSet attrs, int defStyleAttr) { + dataList = new ArrayList<>(); + startAngle = -90; //开始的角度 + + paintSelected = new Paint(); + paintSelected.setColor(Color.LTGRAY); + paintSelected.setStyle(Paint.Style.STROKE);//设置空心 + paintSelected.setStrokeWidth(lineWidth*5); + paintSelected.setAntiAlias(true); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + initSome(); + setMeasuredDimension(widthSize, heightSize); + } + + private void initSome(){ + int widthSize = getMeasuredWidth(); + int heightSize = getMeasuredHeight(); + chartSize = widthSize>heightSize?heightSize:widthSize; + // 调整图表半径计算,让图表尽可能大 + chartRaidus = chartSize / 2; + centerPoint = new PointF(widthSize/2, heightSize/2); + if(widthSize dataList, List lableList){ + total = 0; + this.lableList = lableList; + this.dataList.clear(); + if(dataList!=null) + this.dataList.addAll(dataList); + for(PieChartBean bean : this.dataList){ + total += bean.getNum(); + } + if(centerPoint!=null && centerPoint.x>0){ + startDraw = false; + invalidate(); + } + } + /***********************************设置属性set方法over**********************************/ + /**绘制图表基本框架*/ + @Override + public void drawDefult(Canvas canvas) { + paint.setStyle(Paint.Style.FILL); + paint.setColor(defColor); + canvas.drawCircle(centerPoint.x,centerPoint.y,chartRaidus, paint); + if(ringWidth>0){ //绘制空心圆圈 + paint.setColor(backColor); + canvas.drawCircle(centerPoint.x,centerPoint.y,chartRaidus-ringWidth, paint); + } + } + /**绘制debug辅助线*/ + @Override + public void drawDebug(Canvas canvas) { + super.drawDebug(canvas); + canvas.drawCircle(centerPoint.x, centerPoint.y, chartRaidus, paint); + canvas.drawCircle(centerPoint.x, centerPoint.y, chartRaidus+lineLenth, paint); + canvas.drawCircle(centerPoint.x, centerPoint.y, chartRaidus+lineLenth+textSpace, paint); + canvas.drawCircle(centerPoint.x, centerPoint.y, chartRaidus+lineLenth+textSpace+(int)tagMaxW, paint); + canvas.drawCircle(centerPoint.x, centerPoint.y, chartRaidus+lineLenth+textSpace+(int)tagMaxW+outSpace, paint); + } + private int selectedIndex = -1; //被选中的索引 + /**绘制图表*/ + @Override + public void drawChart(Canvas canvas) { + int index = -1; + for(int i = 0; i < dataList.size(); i++){ + PieChartBean bean = dataList.get(i); + if(bean.getNum() == 0){ + if(showZeroPart) + index++; + continue; + } + index++; + paint.setARGB(255, arrColorRgb[index%arrColorRgb.length][0], arrColorRgb[index%arrColorRgb.length][1], arrColorRgb[index%arrColorRgb.length][2]); + paintLabel.setARGB(255, arrColorRgb[index%arrColorRgb.length][0], arrColorRgb[index%arrColorRgb.length][1], arrColorRgb[index%arrColorRgb.length][2]); + + if(selectedIndex == i){ + paint.setTypeface(Typeface.DEFAULT_BOLD); + paintLabel.setTypeface(Typeface.DEFAULT_BOLD); + }else{ + paint.setTypeface(Typeface.DEFAULT); + paintLabel.setTypeface(Typeface.DEFAULT); + } + + /**1、绘制扇形*/ + paint.setStyle(Paint.Style.FILL);//设置实心 + canvas.drawArc(bean.getArcRect(), bean.getStartAngle(), bean.getSweepAngle(), true, paint); + if(selectedIndex == i){ + //被选中的,绘制边界 + paintSelected.setStyle(Paint.Style.STROKE);//设置空心 + canvas.drawArc(bean.getArcRect(), bean.getStartAngle(), bean.getSweepAngle(), true, paintSelected); + } + if(MODUL_CHART == tagModul){ + /**2、绘制直线*/ + List tagLinePoints = bean.getTagLinePoints(); + if (tagLinePoints != null && tagLinePoints.size() > 0) { + for (int p = 1; p < tagLinePoints.size(); p++) { + canvas.drawLine(tagLinePoints.get(p - 1).x, tagLinePoints.get(p - 1).y, + tagLinePoints.get(p).x, tagLinePoints.get(p).y, paint); + } + } + /**3、绘制指示标签*/ + paintLabel.setTextSize(tagTextSize); + if (tagTextColor != 0) { + paintLabel.setColor(tagTextColor); + } + canvas.drawText(bean.getTagStr() + "", bean.getTagTextPoint().x, bean.getTagTextPoint().y, paintLabel); + } + + } + + //绘制中心内圆 + if(ringWidth>0){ + paint.setColor(backColor); + paint.setStyle(Paint.Style.FILL);//设置实心 + canvas.drawCircle(centerPoint.x, centerPoint.y, chartRaidus-ringWidth, paint); + } + + + /**4 绘制中间文字*/ + if(ringWidth>0 && lableList!=null &&lableList.size()>0){ + float textAllHeight = 0; + float textW, textH, textL; + for(ChartLable lable : lableList){ + paintLabel.setTextSize(lable.getTextSize()); + textH = FontUtil.getFontHeight(paintLabel); + textAllHeight += (textH+centerLableSpace); + } + textAllHeight -= centerLableSpace; + int top = (int)(centerPoint.y-textAllHeight/2); + for(ChartLable lable : lableList){ + paintLabel.setColor(lable.getTextColor()); + paintLabel.setTextSize(lable.getTextSize()); + textW = FontUtil.getFontlength(paintLabel, lable.getText()); + textH = FontUtil.getFontHeight(paintLabel); + textL = FontUtil.getFontLeading(paintLabel); + canvas.drawText(lable.getText(), centerPoint.x-textW/2, top + textL, paintLabel); + top += (textH+centerLableSpace); + } + } + + } + /**创建动画*/ + @Override + protected ValueAnimator initAnim() { + anim = ValueAnimator.ofObject(new AngleEvaluator(), 0f, 1.0f); + anim.setInterpolator(new AccelerateDecelerateInterpolator()); + return anim; + } + /**动画值变化之后计算数据*/ + @Override + protected void evaluatorData(ValueAnimator animation) { + float percentage = (float) animation.getAnimatedValue(); + evaluatorData(percentage); + } + + /**计算各种绘制坐标*/ + private void evaluatorData(float animPre){ + paintLabel.setTextSize(tagTextSize); + float tagTextLead = FontUtil.getFontLeading(paintLabel); + float tagTextHeight = FontUtil.getFontHeight(paintLabel); + float oneStartAngle = startAngle; + for(int i = 0; i < dataList.size(); i++) { + PieChartBean bean = dataList.get(i); + /* if(bean.getNum() == 0 && !showZeroPart){ + continue; + }*/ + /**1、绘制扇形*/ + float arcLeft = centerPoint.x - chartRaidus; //扇形半径 + float arcTop = centerPoint.y - chartRaidus; + float arcRight = centerPoint.x + chartRaidus; + float arcBottom = centerPoint.y + chartRaidus; + + // float percentage = 360.0f / total * bean.getNum(); + float percentage = (bean.getNum()==0?0:(360.0f / total * bean.getNum())*animPre); + bean.setArcRect(new RectF(arcLeft, arcTop, arcRight, arcBottom)); + bean.setStartAngle(oneStartAngle); + bean.setSweepAngle(percentage); + + /**2、计算扇形区域*/ + arcLeft = centerPoint.x - chartSize; + arcTop = centerPoint.y - chartSize; + arcRight = centerPoint.x + chartSize; + arcBottom = centerPoint.y + chartSize; + Path allPath = new Path(); + allPath.moveTo(centerPoint.x, centerPoint.y);//添加原始点 + float ovalX = centerPoint.x + (float) (chartRaidus * Math.cos(Math.toRadians(oneStartAngle))); + float ovalY = centerPoint.y + (float) (chartRaidus * Math.sin(Math.toRadians(oneStartAngle))); + allPath.lineTo(ovalX, ovalY); + RectF touchOval = new RectF(arcLeft, arcTop, arcRight, arcBottom); + allPath.addArc(touchOval, oneStartAngle, percentage); + allPath.lineTo(centerPoint.x, centerPoint.y); + allPath.close(); + RectF r = new RectF(); + allPath.computeBounds(r, true); + Region region = new Region(); + region.setPath(allPath, new Region((int) r.left, (int) r.top, (int) r.right, (int) r.bottom)); + bean.setRegion(region); + + if(MODUL_CHART == tagModul) { + /**3、绘制直线*/ + //确定直线的起始和结束的点的位置 + float startX = centerPoint.x + (float) (chartRaidus * Math.cos(Math.toRadians(oneStartAngle + percentage / 2))); + float startY = centerPoint.y + (float) (chartRaidus * Math.sin(Math.toRadians(oneStartAngle + percentage / 2))); + float endX = centerPoint.x + (float) ((chartRaidus + lineLenth - 20) * Math.cos(Math.toRadians(oneStartAngle + percentage / 2))); + float endY = centerPoint.y + (float) ((chartRaidus + lineLenth - 20) * Math.sin(Math.toRadians(oneStartAngle + percentage / 2))); + boolean isRight = true; + float lineAngle = oneStartAngle + percentage / 2; + if (lineAngle > 90 && lineAngle < 270) { + isRight = false; + } + // LogUtil.i(TAG, "直线坐标:start=("+startX+","+startY +") end=("+endX+","+endY+")"+" lineAngle="+lineAngle+" isRight="+isRight); + List tagLinePoints = new ArrayList<>(); + tagLinePoints.add(new PointF(startX, startY)); + tagLinePoints.add(new PointF(endX, endY)); + float textX = isRight ? (endX + 20) : (endX - 20); + tagLinePoints.add(new PointF(textX, endY)); + bean.setTagLinePoints(tagLinePoints); + + /**3、绘制指示标签*/ + String tagText = ""; + paintLabel.setTextSize(tagTextSize); + if (tagType == PieChartLayout.TAG_TYPE.TYPE_NUM) { + tagText = bean.getNum() + ""; + } else if (tagType == PieChartLayout.TAG_TYPE.TYPE_PERCENT) { + DecimalFormat decimalFormat = new DecimalFormat("0.0%"); + tagText = (total==0?"/":decimalFormat.format(((float) bean.getNum() / (float) total))); + } + float textW = FontUtil.getFontlength(paintLabel, tagText); + textX = isRight ? textX + textSpace : (textX - textW - textSpace); + float textY = endY - tagTextHeight / 2 + tagTextLead; + bean.setTagStr(tagText); + bean.setTagTextPoint(new PointF(textX, textY)); + } + + /*开始角度累加*/ + oneStartAngle += percentage; + } + + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/charts/piechart/PieChartLableView.java b/app/src/main/java/com/tairui/industrial_operation/widget/charts/piechart/PieChartLableView.java new file mode 100644 index 0000000..6f2e6b7 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/charts/piechart/PieChartLableView.java @@ -0,0 +1,284 @@ +package com.tairui.industrial_operation.widget.charts.piechart; + +import static com.tairui.industrial_operation.widget.charts.BaseChart.TOUCH_EVENT_TYPE.EVENT_Y; +import static com.tairui.industrial_operation.widget.charts.piechart.PieChartLayout.TAG_MODUL.MODUL_LABLE; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; + +import com.tairui.industrial_operation.util.DensityUtils; +import com.tairui.industrial_operation.util.FontUtil; +import com.tairui.industrial_operation.util.LogUtil; +import com.tairui.industrial_operation.widget.charts.BaseChart; +import com.tairui.industrial_operation.widget.charts.bean.PieChartBean; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PointF; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.MotionEvent; + +/** + * autour : openXu + * date : 2017/7/24 10:46 + * className : PieChartLableView + * version : 1.0 + * description : 占比饼状图表 + */ +public class PieChartLableView extends BaseChart { + + private List dataList; + + private boolean showZeroPart = false; //如果某部分占比为0, 是否显示 + + protected RectF rectLable; //图表矩形 + private int minTopPointY; //滑动到最上面时的y坐标 + private int top; //绘制的y坐标 + private int oneLableHeight; //右侧标签单个高度,需要计算 + + /**设置的属性*/ + private int rectW; + private int rectH; + private int rectRaidus; //矩形圆角 + private int rectSpace; //右侧标签上下间距 + private int leftSpace; + private int textSize = 90; //文字大小 + private int textColor; + private int arrColorRgb[][]; + + private PieChartLayout.TAG_TYPE tagType; //TAG展示类型 + private PieChartLayout.TAG_MODUL tagModul; //TAG展示位置 + + public PieChartLableView(Context context) { + super(context, null); + } + public PieChartLableView(Context context, AttributeSet attrs) { + super(context, attrs, 0); + } + public PieChartLableView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public void init(Context context, AttributeSet attrs, int defStyleAttr) { + touchEventType = EVENT_Y; + dataList = new ArrayList<>(); + setClickable(true); + } + /***********************************设置属性set方法**********************************/ + public void setArrColorRgb(int[][] arrColorRgb) { + this.arrColorRgb = arrColorRgb; + } + + public void setTextSize(int textSize) { + this.textSize = textSize; + } + public void setTextColor(int textColor) { + this.textColor = textColor; + } + public void setShowZeroPart(boolean showZeroPart) { + this.showZeroPart = showZeroPart; + } + public void setRectW(int rectW) { + this.rectW = rectW; + } + + public void setRectH(int rectH) { + this.rectH = rectH; + } + + public void setRectRaidus(int rectRaidus) { + this.rectRaidus = rectRaidus; + } + + public void setTagType(PieChartLayout.TAG_TYPE tagType) { + this.tagType = tagType; + } + + public void setTagModul(PieChartLayout.TAG_MODUL tagModul) { + this.tagModul = tagModul; + } + + public void setRectSpace(int rectSpace) { + this.rectSpace = rectSpace; + } + + public void setLeftSpace(int leftSpace) { + this.leftSpace = leftSpace; + } + /***********************************设置属性set方法over**********************************/ + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + centerPoint = new PointF(widthSize/2, heightSize/2); + rectLable = new RectF(0,0,widthSize, heightSize); + setMeasuredDimension(widthSize, heightSize); + evaluatorLable(); + } + + @Override + public void dispatchTouchEvent1(MotionEvent event) { + int move = (int)(event.getY() - lastTouchPoint.y); + if(top + mMoveLen <= minTopPointY){ + if(move<0){ + LogUtil.e(TAG, "已经到最下面了,还往上滑,不行"); + getParent().requestDisallowInterceptTouchEvent(false); + }else{ + getParent().requestDisallowInterceptTouchEvent(true); + } + }else if(top + mMoveLen>=rectSpace){ + if(move>0){ + LogUtil.w(TAG, "已经划到头了,还往下滑,不行"); + getParent().requestDisallowInterceptTouchEvent(false); + }else{ + getParent().requestDisallowInterceptTouchEvent(true); + } + }else{ + //请求所有父控件及祖宗控件不要拦截事件 + getParent().requestDisallowInterceptTouchEvent(true); + LogUtil.i(TAG, "正常请求事件"); + } + } + + @Override + protected void evaluatorFling(float fling) { + if(top + mMoveLen + fling <= minTopPointY){ + mMoveLen = minTopPointY-rectSpace; + }else if(top + mMoveLen+ fling>=rectSpace){ + mMoveLen = 0; + } else{ + mMoveLen += fling; + } + } + + /** + * 设置数据 + */ + public void setData(List dataList){ + this.dataList.clear(); + if(dataList!=null) + this.dataList.addAll(dataList); + evaluatorLable(); + startDraw = false; + invalidate(); + } + /**计算右侧标签相关坐标值*/ + private void evaluatorLable(){ + paintLabel.setTextSize(textSize); + oneLableHeight = (int) FontUtil.getFontHeight(paintLabel); + //字和矩形中高度的较大值 + oneLableHeight = oneLableHeight>rectH?oneLableHeight:rectH; + int allHeight = 0 ; + //总高度 + for(PieChartBean bean : dataList){ + if(bean.getNum() == 0 && !showZeroPart){ + continue; + } + allHeight += oneLableHeight+rectSpace; + } + if(allHeight>0) + allHeight -= rectSpace; + +// int allHeight = (oneLableHeight+rectSpace)*dataList.size() - rectSpace; + int contentH = getMeasuredHeight() - rectSpace*2; + touchEnable = allHeight>contentH; + if(touchEnable){ + //超出总高度了 + top = rectSpace; + minTopPointY = -allHeight - rectSpace + getMeasuredHeight(); + if(debug) + LogUtil.i(TAG, "边界"+minTopPointY+" "+getMeasuredHeight()); + }else{ + top = (getMeasuredHeight()-allHeight)/2; + } + } + + /**绘制图表基本框架*/ + @Override + public void drawDefult(Canvas canvas) { + } + /**绘制debug辅助线*/ + @Override + public void drawDebug(Canvas canvas) { + super.drawDebug(canvas); + } + /**绘制图表*/ + @Override + public void drawChart(Canvas canvas) { + + int topY = top + mMoveLen; + + paintLabel.setTextSize(textSize); + int lableH =(int) FontUtil.getFontHeight(paintLabel); + int lableL =(int) FontUtil.getFontLeading(paintLabel); + + paint.setStyle(Paint.Style.FILL); + + int index = -1; + for(int i = 0; i < dataList.size(); i++){ + PieChartBean bean = dataList.get(i); + if(bean.getNum() == 0 && !showZeroPart){ + continue; + } + index++; + paint.setARGB(255, arrColorRgb[index%arrColorRgb.length][0], arrColorRgb[index%arrColorRgb.length][1], arrColorRgb[index%arrColorRgb.length][2]); + paintLabel.setARGB(255, arrColorRgb[index%arrColorRgb.length][0], arrColorRgb[index%arrColorRgb.length][1], arrColorRgb[index%arrColorRgb.length][2]); + + int rectTop = topY+(oneLableHeight-rectH)/2; + RectF rect = new RectF(leftSpace, rectTop, leftSpace+rectW, rectTop+rectH); + canvas.drawRoundRect(rect, rectRaidus, rectRaidus, paint); + + + if(textColor!=0) { + paintLabel.setColor(textColor); + } + rectTop = topY+(oneLableHeight-lableH)/2; + canvas.drawText(bean.getName(), leftSpace+rectW+leftSpace , rectTop+lableL , paintLabel); + float textW = FontUtil.getFontlength(paintLabel, bean.getName()); + /**5、绘制指示标签*/ + if(MODUL_LABLE == tagModul){ + String tagText = ""; + if (tagType == PieChartLayout.TAG_TYPE.TYPE_NUM) { + tagText = bean.getNum() + ""; + } else if (tagType == PieChartLayout.TAG_TYPE.TYPE_PERCENT) { + DecimalFormat decimalFormat = new DecimalFormat("0.0%"); + tagText = decimalFormat.format(((float) bean.getNum() / (float) total)); + } + float tagW = FontUtil.getFontlength(paintLabel, tagText); + canvas.drawText(tagText, getMeasuredWidth()-tagW- DensityUtils.dp2px(getContext(),3), rectTop+lableL , paintLabel); + + /* DashPathEffect dashPathEffect = new DashPathEffect(new float[]{8,10,8,10}, 0); + paint.setColor(Color.RED); +// paint.setPathEffect(dashPathEffect); + paint.setStrokeWidth(4); + Path path = new Path(); + path.moveTo(leftSpace+rectW+leftSpace+textW, rectTop); + path.lineTo(getMeasuredWidth()-tagW- DensityUtil.dip2px(getContext(),3), rectTop); + LogUtil.i(TAG, "绘制虚线"+path); + canvas.drawPath(path,paint);*/ + } + topY += oneLableHeight + rectSpace; + } + + } + /**创建动画*/ + @Override + protected ValueAnimator initAnim() { + return null; + } + /**动画值变化之后计算数据*/ + @Override + protected void evaluatorData(ValueAnimator animation) { + + } + + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/charts/piechart/PieChartLayout.java b/app/src/main/java/com/tairui/industrial_operation/widget/charts/piechart/PieChartLayout.java new file mode 100644 index 0000000..ff19425 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/charts/piechart/PieChartLayout.java @@ -0,0 +1,291 @@ +package com.tairui.industrial_operation.widget.charts.piechart; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import com.tairui.industrial_operation.R; +import com.tairui.industrial_operation.util.DensityUtils; +import com.tairui.industrial_operation.util.LogUtil; +import com.tairui.industrial_operation.widget.charts.bean.ChartLable; +import com.tairui.industrial_operation.widget.charts.bean.PieChartBean; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.widget.LinearLayout; + +/** + * autour : openXu + * date : 2017/7/24 10:46 + * className : PieChartLayout + * version : 1.0 + * description : 占比饼状图表 + */ +public class PieChartLayout extends LinearLayout { + + private String TAG = "PieChartLayout"; + + protected boolean isLoading = true; + + protected List dataList; + protected List lableList; + + private PieChart chartView; + /**饼状图设置属性*/ + private boolean showZeroPart = false; //如果某部分占比为0, 是否显示 + private int centerLableSpace = DensityUtils.dp2px(getContext(), 1); //中间文字行距 + //圆环宽度,如果值>0,则为空心圆环,内环为白色,可以在内环中绘制字 + private int ringWidth = DensityUtils.dp2px(getContext(), 20); //圆环宽度 + private int lineLenth = DensityUtils.dp2px(getContext(), 20); //指示线长度 + private int outSpace = DensityUtils.dp2px(getContext(), 0); + private int textSpace = DensityUtils.dp2px(getContext(), 3); //tag指示文字与线的距离 + private int tagTextSize = (int)getResources().getDimension(R.dimen.text_size_level_small); //饼状图占比指示文字大小 + private int tagTextColor = getResources().getColor(R.color.text_color_light_gray); //文字颜色,如果为0,则根据扇形颜色一样; + private TAG_MODUL tagModul = TAG_MODUL.MODUL_LABLE; //TAG展示位置 + private TAG_TYPE tagType = TAG_TYPE.TYPE_NUM; //TAG展示类型 + + + private PieChartLableView lableView; + /**右侧lable设置属性*/ + private int rectW = DensityUtils.dp2px(getContext(), 12); //lable矩形宽高 + private int rectH = DensityUtils.dp2px(getContext(), 6); + private int rectRaidus = 4; //矩形圆角 + private int rectSpace = DensityUtils.dp2px(getContext(), 8); //右侧标签上下间距 + private int leftSpace = DensityUtils.dp2px(getContext(), 5); //右侧标签左右间距 + private int lableTextSize = (int)getResources().getDimension(R.dimen.text_size_level_small); //饼状图占比指示文字大小 + private int lableTextColor = getResources().getColor(R.color.text_color_light_gray); + + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + boolean result = super.dispatchTouchEvent(ev); + LogUtil.w(TAG, "dispatchTouchEvent分发事件 "+result); + return result; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + boolean result = super.onInterceptTouchEvent(ev); + LogUtil.e(TAG, "onInterceptTouchEvent拦截事件 "+result); + return result; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + boolean result = super.onTouchEvent(ev); + LogUtil.i(TAG, "onTouchEvent处理事件 "+result); + return result; + } + + //RGB颜色数组 + private int arrColorRgb[][] = { + {0, 122, 255}, // UIColorFromRGB(0xD95F5B), + {255, 78, 61}, // UIColorFromRGB(0x7189E6), + {42, 216, 72}, // UIColorFromRGB(0x5AB9C7), + {255, 164, 35}, // UIColorFromRGB(0xB096D5), + {107, 186, 151}, // UIColorFromRGB(0x6BBA97), + {91, 164, 231}, // UIColorFromRGB(0x5BA4E7), + {220, 170, 97},// UIColorFromRGB(0xDCAA61), + {125, 171, 88},// UIColorFromRGB(0x7DAB58), + {233, 200, 88},// UIColorFromRGB(0xE9C858), + {213, 150, 196},// UIColorFromRGB(0xd596c4) + {220, 127, 104},// UIColorFromRGB(0xDC7F68), + + +// {86, 138, 220}, // UIColorFromRGB(0x568ADC), +// {112, 173, 71}, //UIColorFromRGB(0x70AD47) +// {219, 69, 40}, // UIColorFromRGB(0xDB4528) +// {255, 193, 2}, // UIColorFromRGB(0xFFC102) +// {242, 141, 2}, // UIColorFromRGB(0xF28D02) +// {41, 182, 180}, // UIColorFromRGB(0x29B6B4) +// {187, 107, 201}, // UIColorFromRGB(0xbb6bc9) +// {124, 117, 214}, // UIColorFromRGB(0x7c75d6) + }; + + + /**tag类型*/ + public enum TAG_TYPE{ + TYPE_NUM, //数量 + TYPE_PERCENT, //百分比 + } + public enum TAG_MODUL{ + MODEUL_NULL, //不展示 + MODUL_CHART, //在扇形图上显示tag + MODUL_LABLE, //在lable后面显示tag + } + + public PieChartLayout(Context context) { + this(context, null); + } + public PieChartLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + public PieChartLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + private void init() { + this.dataList = new ArrayList<>(); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + for(int i = 0; i dataList, List lableList){ + this.lableList = lableList; + this.dataList.clear(); + if(dataList!=null){ + try{ + Field filedPer = clazz.getDeclaredField(per); + Field filedName = clazz.getDeclaredField(name); + filedPer.setAccessible(true); + filedName.setAccessible(true); + for(Object obj : dataList){ + String perStr = filedPer.get(obj).toString(); + PieChartBean bean = new PieChartBean(Float.parseFloat(perStr), (String)filedName.get(obj)); + this.dataList.add(bean); + } + }catch (Exception e){ + e.printStackTrace(); + } + } + setConfig(); + } + + + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/charts/rosechart/NightingaleRoseChart.java b/app/src/main/java/com/tairui/industrial_operation/widget/charts/rosechart/NightingaleRoseChart.java new file mode 100644 index 0000000..c4e8e31 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/charts/rosechart/NightingaleRoseChart.java @@ -0,0 +1,638 @@ +package com.tairui.industrial_operation.widget.charts.rosechart; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import com.tairui.industrial_operation.R; +import com.tairui.industrial_operation.util.DensityUtils; +import com.tairui.industrial_operation.util.FontUtil; +import com.tairui.industrial_operation.util.LogUtil; +import com.tairui.industrial_operation.widget.charts.anim.AngleEvaluator; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.DashPathEffect; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PathEffect; +import android.graphics.PointF; +import android.graphics.RectF; +import android.graphics.Region; +import android.graphics.Typeface; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.AccelerateDecelerateInterpolator; + +/** + * autour : openXu + * date : 2017/7/24 10:46 + * className : NightingaleRoseChart + * version : 1.0 + * description : 南丁格尔玫瑰图 + */ +public class NightingaleRoseChart extends View { + + private String TAG = "NightingaleRoseChart"; + private int ScrWidth,ScrHeight; + private int backColor = Color.WHITE; + + private PointF centerPoint; //chart中心点坐标 + private int startAngle = -90; //开始的角度 + private int chartRaidusInner = DensityUtils.dp2px(getContext(), 15); //内圈半径 + private int chartRaidusOuter; //最大部分扇形区域半径 + private int outSpace = DensityUtils.dp2px(getContext(), 50); //图表与边界距离 + private int lineLenth = DensityUtils.dp2px(getContext(), 8); + private int lineWidth = 1; //线宽度 + + private RectF rectChart, rectLable; + + private int rightLableTop; + private int oneLableHeight; //右侧标签单个高度 + private int rightRectItemW = DensityUtils.dp2px(getContext(), 16); + private int rightRectItemH = DensityUtils.dp2px(getContext(), 8); + private int rightRectSpace = DensityUtils.dp2px(getContext(), 8); //右侧标签上下间距 + private int textSpace = DensityUtils.dp2px(getContext(), 5); + + private int lableTextSize = (int)getResources().getDimension(R.dimen.text_size_level_mid); //右侧标注字体大小 + private int tagTextSize = (int)getResources().getDimension(R.dimen.text_size_level_small); + + private String nullStr = "暂无数据"; + private List dataList; + private float max; + + private Paint paintArc , paintSelected; + private Paint paintLabel; + private Paint mLinePaint, mlableLinePaint; + + private int selectedIndex = -1; //被选中的索引 + /**正在加载*/ + boolean isLoading = true; + /**是否在图表上显示指示lable*/ + boolean showChartLable = false; + /**是否在图表上显示指示num*/ + boolean showChartNum = true; + /**点击显示数量*/ + boolean showNumTouched = false; + /**右侧显示数量*/ + boolean showRightNum = false; + + //RGB颜色数组 + //#D95F5B红 #7189E6蓝 #5AB9C7蓝1 #B096D5紫 #6BBA97绿1 #DCAA61黄 #7DAB58绿2 #DC7F68橙 + private final int arrColorRgb[][] = { + {113, 137, 230}, // UIColorFromRGB(0xD95F5B), + {217, 95, 91}, // UIColorFromRGB(0x7189E6), + {90, 185, 199}, // UIColorFromRGB(0x5AB9C7), + {170, 150, 213}, // UIColorFromRGB(0xB096D5), + {107, 186, 151}, // UIColorFromRGB(0x6BBA97), + {91, 164, 231}, // UIColorFromRGB(0x5BA4E7), + {220, 170, 97},// UIColorFromRGB(0xDCAA61), + {125, 171, 88},// UIColorFromRGB(0x7DAB58), + {233, 200, 88},// UIColorFromRGB(0xE9C858), + {213, 150, 196},// UIColorFromRGB(0xd596c4) + {220, 127, 104}// UIColorFromRGB(0xDC7F68), + }; + + public void setLoading(boolean loading) { + isLoading = loading; + } + + public void setShowNumTouched(boolean showNumTouched) { + this.showNumTouched = showNumTouched; + } + public void setShowRightNum(boolean showRightNum) { + this.showRightNum = showRightNum; + } + public void setChartRaidusInner(int chartRaidusInner) { + this.chartRaidusInner = DensityUtils.dp2px(getContext(), chartRaidusInner); //内圈半径 + } + + public void setChartRaidusOuter(int chartRaidusOuter) { + this.chartRaidusOuter = DensityUtils.dp2px(getContext(), chartRaidusOuter); //图表与边界距离 + } + + public NightingaleRoseChart(Context context) { + this(context, null); + } + + public NightingaleRoseChart(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public NightingaleRoseChart(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context, attrs, defStyle); + } + + + public void init(Context context, AttributeSet attrs, int defStyleAttr) { + dataList = new ArrayList<>(); + DisplayMetrics dm = getResources().getDisplayMetrics(); + ScrHeight = dm.heightPixels; + ScrWidth = dm.widthPixels; + + //画笔初始化 + paintArc = new Paint(); + paintArc.setAntiAlias(true); + + paintLabel = new Paint(); + paintLabel.setAntiAlias(true); + + paintSelected = new Paint(); + paintSelected.setColor(Color.LTGRAY); + paintSelected.setStyle(Paint.Style.STROKE);//设置空心 + paintSelected.setStrokeWidth(lineWidth*5); + paintSelected.setAntiAlias(true); + + mLinePaint = new Paint(); + mLinePaint.setStyle(Paint.Style.FILL); + mLinePaint.setStrokeWidth(lineWidth); + mLinePaint.setAntiAlias(true); + + + mlableLinePaint = new Paint(); + mlableLinePaint.setStyle(Paint.Style.STROKE); + mlableLinePaint.setColor(Color.DKGRAY); + mlableLinePaint.setStrokeWidth(3); + // PathEffect是用来控制绘制轮廓(线条)的方式 + // 代码中的float数组,必须是偶数长度,且>=2,指定了多少长度的实线之后再画多少长度的空白 + // .如本代码中,绘制长度5的实线,再绘制长度5的空白,再绘制长度5的实线,再绘制长度5的空白,依次重复.1是偏移量,可以不用理会. + PathEffect effects = new DashPathEffect(new float[]{5,5,5,5},1); + mlableLinePaint.setPathEffect(effects); + + + } + + public void setData(Class clazz, String per, String name, List dataList){ + this.dataList.clear(); + max = 0; + LogUtil.i(TAG, "玫瑰图设置数据"+dataList); + if(dataList!=null){ + try{ + Field filedPer = clazz.getDeclaredField(per); + Field filedName = clazz.getDeclaredField(name); + filedPer.setAccessible(true); + filedName.setAccessible(true); + for(Object obj : dataList){ + String perStr = filedPer.get(obj).toString(); + this.dataList.add(new RoseChartBean(Float.parseFloat(perStr), (String)filedName.get(obj) )); + } + }catch (Exception e){ + e.printStackTrace(); + } + for(RoseChartBean bean : this.dataList){ + max = max>bean.getPer()?max:bean.getPer(); + } + if(getMeasuredHeight()>0){ + evaluatorLable(); + } + } + Log.i(TAG, "玫瑰图设置数据dataList"+this.dataList); + startDraw = false; + invalidate(); + } + + boolean startDraw = false; + public void setData(List dataList){ + this.dataList.clear(); + max = 0; + if(dataList!=null){ + this.dataList.addAll(dataList); + for(RoseChartBean bean : dataList){ + max = max>bean.getPer()?max:bean.getPer(); + } + evaluatorLable(); + } + startDraw = false; + invalidate(); + } + + /**计算右侧标签相关坐标值*/ + private void evaluatorLable(){ + paintLabel.setTextSize(lableTextSize); + oneLableHeight = (int) FontUtil.getFontHeight(paintLabel); + //字和矩形中高度的较大值 + oneLableHeight = oneLableHeight>rightRectItemH?oneLableHeight:rightRectItemH; + + int allHeight = (oneLableHeight+rightRectSpace)*dataList.size() +rightRectSpace; + LogUtil.e(TAG, "测量高度"+getMeasuredHeight()+" allHeight="+allHeight); + if(allHeight>getMeasuredHeight()){ + //超出总高度了 + oneLableHeight = getMeasuredHeight() / ((dataList==null || dataList.size()<=0)?1:dataList.size()); + rightLableTop = -1; + }else{ + rightLableTop = (getMeasuredHeight()-allHeight)/2; + } + } + + public void setShowChartLable(boolean showChartLable) { + this.showChartLable = showChartLable; + } + public void setShowChartNum(boolean showChartNum) { + this.showChartNum = showChartNum; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int width = MeasureSpec.getSize(widthMeasureSpec); + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + int height; + if (heightMode == MeasureSpec.EXACTLY) { + height = heightSize; + } else { + height = ScrWidth/3 * 2; //3分之2的位置画图,3分1的位置画标注 + } + int chartWidth = width/3*2; + int chartSize = chartWidth>height?height:chartWidth; + //外圈半径=size/2-最大部分lable线长度-边距 + chartRaidusOuter = chartSize/2-lineLenth-outSpace; + centerPoint = new PointF(chartWidth/2, height/2); + + rectChart = new RectF(0,0,chartWidth, height); + rectLable = new RectF(chartWidth,0,width, height); + setMeasuredDimension(width, height); + if(dataList.size()>0){ + evaluatorLable(); + } + LogUtil.i(TAG, "图表总宽高="+width+"*"+height); + LogUtil.i(TAG, "chart宽高="+chartWidth+"*"+height); + LogUtil.i(TAG, "chart直径="+chartSize); + LogUtil.i(TAG, "lineLenth="+lineLenth+" outSpace="+outSpace); + + LogUtil.i(TAG, "chartRaidusOuter="+chartRaidusOuter); + LogUtil.i(TAG, "centerPoint="+centerPoint.x+"*"+ centerPoint.y); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + switch (event.getAction()){ + case MotionEvent.ACTION_DOWN: + PointF point = new PointF(event.getX(),event.getY()); + sureSelectedIndex(point); + break; + case MotionEvent.ACTION_MOVE: + // LogUtil.w(TAG, "长按后的移动时间"); + point = new PointF(event.getX(),event.getY()); + sureSelectedIndex(point); + break; + case MotionEvent.ACTION_UP: +// selectedIndex = -1; + invalidate(); + break; + } + return super.onTouchEvent(event); + } + + private boolean sureSelectedIndex(PointF point){ + for(int i = 0; i < dataList.size(); i++) { + RoseChartBean bean = dataList.get(i); + if(bean.getRegion()!=null && bean.getRegion().contains((int)point.x, (int)point.y)){ +// LogUtil.e(TAG, "在第"+i+"个扇形区域"); + selectedIndex = i; + invalidate(); + return true; + } + if(bean.getRectLable()!=null && bean.getRectLable().contains((int)point.x, (int)point.y)){ +// LogUtil.e(TAG, "在第"+i+"个lable区域"); + selectedIndex = i; + invalidate(); + return true; + } + } + return false; + } + + public void onDraw(Canvas canvas){ + + //画布背景 + canvas.drawColor(backColor); + + if(isLoading){ + paintLabel.setTextSize(lableTextSize); + float NullTextLead = FontUtil.getFontLeading(paintLabel); + float NullTextHeight = FontUtil.getFontHeight(paintLabel); + float textY = centerPoint.y-NullTextHeight/2+NullTextLead; + paintLabel.setColor(getContext().getResources().getColor(R.color.text_color_def)); + canvas.drawText("loading...", centerPoint.x- FontUtil.getFontlength(paintLabel, "loading...")/2, textY, paintLabel); + return; + } + if(dataList==null || dataList.size()<=0) { +// paintArc.setStyle(Paint.Style.STROKE);//设置空心 +// canvas.drawCircle(centerPoint.x, centerPoint.y, chartRaidusOuter, paintArc); + paintLabel.setTextSize(lableTextSize); + float NullTextLead = FontUtil.getFontLeading(paintLabel); + float NullTextHeight = FontUtil.getFontHeight(paintLabel); + float textY = centerPoint.y-NullTextHeight/2+NullTextLead; + canvas.drawText(nullStr, centerPoint.x- FontUtil.getFontlength(paintLabel, nullStr)/2, textY, paintLabel); + return; + } + +// drawDebug(canvas); + + if(!startDraw){ + startDraw = true; + startAnimation(); + }else{ + drawChart(canvas); + } + } + + private void drawDebug(Canvas canvas){ + paintArc.setStyle(Paint.Style.STROKE);//设置空心 + //绘制边界--chart区域 + paintArc.setColor(Color.YELLOW); + canvas.drawRect(rectChart, paintArc); + //绘制边界--标签区域 + paintArc.setColor(Color.RED); + canvas.drawRect(rectLable, paintArc); + //绘制边界--chart圆边界 + paintArc.setColor(Color.BLUE); + canvas.drawCircle(centerPoint.x, centerPoint.y, chartRaidusOuter+outSpace+lineLenth, paintArc); + //绘制边界--chart lable边缘 + paintArc.setColor(Color.GRAY); + //绘制边界--chart 扇形边缘 + canvas.drawCircle(centerPoint.x, centerPoint.y, chartRaidusOuter+lineLenth, paintArc); + paintArc.setColor(Color.LTGRAY); + canvas.drawCircle(centerPoint.x, centerPoint.y, chartRaidusOuter, paintArc); + } + + public int STYLE_1 = 1; + public int STYLE_2 = 2; + private int STYLE = STYLE_2; + public void setAnimStyle(int style){ + STYLE = style; + } + private long duration = 1000; + public void setAnimDuration(long duration){ + this.duration = duration; + } + ValueAnimator anim; + private void startAnimation() { + if(anim!=null){ + anim.cancel(); + } + Log.w(TAG, "开始动画"); + //将百分比转换为扇形半径长度 + float percentage = 360.0f / dataList.size(); + anim = ValueAnimator.ofObject(new AngleEvaluator(), 0f, percentage); + anim.setInterpolator(new AccelerateDecelerateInterpolator()); + anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float percentage = (float) animation.getAnimatedValue(); + evaluatorData(percentage); + invalidate(); + } + }); + anim.setDuration(duration); + anim.start(); + } + + /** + * 计算各种绘制坐标 + * @param percentage 当前扇形度数 + */ + private void evaluatorData(float percentage){ + paintLabel.setTextSize(tagTextSize); + float chartTextLead = FontUtil.getFontLeading(paintLabel); + float chartTextHeight = FontUtil.getFontHeight(paintLabel); + paintLabel.setTextSize(lableTextSize); + float lableTextLead = FontUtil.getFontLeading(paintLabel); + float lableTextHeight = FontUtil.getFontHeight(paintLabel); + float radius; //每部分扇形半径 + for(int i = 0; i < dataList.size(); i++){ + RoseChartBean bean = dataList.get(i); + /**1、绘制扇形*/ + //将百分比转换为扇区的半径 + radius = chartRaidusInner+(chartRaidusOuter-chartRaidusInner)* (bean.getPer()*1.0f / max*1.0f); + float arcLeft = centerPoint.x - radius; + float arcTop = centerPoint.y - radius ; + float arcRight = centerPoint.x + radius ; + float arcBottom = centerPoint.y + radius ; + + float oneStartAngle = startAngle + i * percentage; + if(STYLE == STYLE_1){ + oneStartAngle = startAngle + i * percentage; + }else{ + //将百分比转换为扇形半径长度 + float partPercentage = 360.0f / dataList.size(); + oneStartAngle = startAngle + i * partPercentage; + } + bean.setArcRect(new RectF(arcLeft ,arcTop, arcRight, arcBottom)); + bean.setStartAngle(oneStartAngle); + bean.setSweepAngle(percentage); + /**计算扇形区域*/ + float Allradius = chartRaidusOuter+ lineLenth + outSpace; + arcLeft = centerPoint.x - Allradius; + arcTop = centerPoint.y - Allradius; + arcRight = centerPoint.x + Allradius; + arcBottom = centerPoint.y + Allradius; + Path allPath = new Path(); + allPath.moveTo(centerPoint.x, centerPoint.y);//添加原始点 + float ovalX = centerPoint.x+(float) (radius* Math.cos(Math.toRadians(oneStartAngle))); + float ovalY = centerPoint.y+(float) (radius* Math.sin(Math.toRadians(oneStartAngle))); + allPath.lineTo(ovalX, ovalY); + RectF touchOval = new RectF(arcLeft, arcTop, arcRight, arcBottom); + allPath.addArc(touchOval, oneStartAngle, percentage); + allPath.lineTo(centerPoint.x, centerPoint.y); + allPath.close(); + RectF r = new RectF(); + allPath.computeBounds(r, true); + Region region = new Region(); + region.setPath(allPath, new Region((int)r.left, (int) r.top, (int) r.right,(int)r.bottom)); + bean.setRegion(region); + + /**2、绘制直线*/ + //确定直线的起始和结束的点的位置 + float startX = centerPoint.x + (float) (radius * Math.cos(Math.toRadians(oneStartAngle + percentage / 2))); + float startY = centerPoint.y + (float) (radius * Math.sin(Math.toRadians(oneStartAngle + percentage / 2))); + float endX = centerPoint.x + (float) ((chartRaidusOuter + lineLenth) * Math.cos(Math.toRadians(oneStartAngle + percentage / 2))); + float endY = centerPoint.y + (float) ((chartRaidusOuter + lineLenth) * Math.sin(Math.toRadians(oneStartAngle + percentage / 2))); + boolean isRight = true; + float lineAngle = startAngle + i * percentage + percentage / 2; + if (lineAngle > 90 && lineAngle < 270) { + isRight = false; + } + +// LogUtil.i(TAG, "直线坐标:start=("+startX+","+startY +") end=("+endX+","+endY+")"+" lineAngle="+lineAngle+" isRight="+isRight); + List tagLinePoints = new ArrayList<>(); + tagLinePoints.add(new PointF(startX, startY)); + tagLinePoints.add(new PointF(endX, endY)); + float textX = isRight ? (endX + 20) : (endX - 20); + tagLinePoints.add(new PointF(textX, endY)); + bean.setTagLinePoints(tagLinePoints); + + /**3、绘制指示标签*/ + paintLabel.setTextSize(tagTextSize); + + String lableText = ""; + if(showChartLable){ + lableText = bean.getName(); + }else if(showChartNum){ + lableText = bean.getPer()+""; + } + if(showNumTouched){ + lableText = bean.getPer()+""; + } + + //标签字体长度 + float textW = FontUtil.getFontlength(paintLabel, lableText); + //字体绘制X坐标 + textX = isRight ? textX : (textX - textW); + float textY = endY - chartTextHeight / 2 + chartTextLead; + bean.setTagTextPoint(new PointF(textX, textY)); + + /**4、绘制右侧item矩形*/ + float rectL, rectT, rectR, centerY, DashPathL; + + if(rightLableTop<0){ + //超出总高度了 + bean.setRectLable(new RectF(rectLable.left, i * oneLableHeight, rectLable.right, (i+1) * oneLableHeight)); + + rectL = rectLable.left+textSpace; + rectT = i * oneLableHeight + (oneLableHeight-rightRectItemH)/2; + rectR = rectL + rightRectItemW; + centerY = i * oneLableHeight + oneLableHeight/2.0f; + RectF colorRect = new RectF(rectL, rectT, rectR, rectT+ rightRectItemH); + bean.setColorRect(colorRect); + }else{ + rectT = rightLableTop +rightRectSpace+ i * (oneLableHeight+rightRectSpace); + bean.setRectLable(new RectF(rectLable.left, rectT, rectLable.right, rectT+oneLableHeight)); + + centerY = rectT + oneLableHeight/2.0f; + + rectL = rectLable.left+textSpace; + rectT = rectT + (oneLableHeight-rightRectItemH)/2; + rectR = rectL + rightRectItemW; + RectF colorRect = new RectF(rectL, rectT, rectR, rectT+ rightRectItemH); + bean.setColorRect(colorRect); + } + + /**5、绘制指示标签*/ + paintLabel.setTextSize(lableTextSize); + //标签字体长度 + textW = FontUtil.getFontlength(paintLabel, bean.getName()); + //字体绘制X坐标 + rectL = rectR+textSpace; + textY = centerY -lableTextHeight/2 +lableTextLead; + bean.setNameTextPoint(new PointF(rectL, textY)); + DashPathL = rectL + textW + textSpace; + + /**6.绘制占比*/ + textW = FontUtil.getFontlength(paintLabel, bean.getPer()+""); + rectL = rectLable.right - textW - textSpace; + bean.setPerTextPoint(new PointF(rectL, textY)); + /**7、绘制虚线*/ + if(DashPathL<(rectL-textSpace)){ + bean.setDashPathPointStart(new PointF(DashPathL, centerY)); + bean.setDashPathPointEnd(new PointF(rectL-textSpace, centerY)); + } + + } + } + private void drawChart(Canvas canvas){ + paintArc.setStyle(Paint.Style.FILL);//设置实心 + + for(int i = 0; i < dataList.size(); i++){ + paintArc.setARGB(255, arrColorRgb[i%arrColorRgb.length][0], arrColorRgb[i%arrColorRgb.length][1], arrColorRgb[i%arrColorRgb.length][2]); + mLinePaint.setARGB(255, arrColorRgb[i%arrColorRgb.length][0], + arrColorRgb[i%arrColorRgb.length][1], arrColorRgb[i%arrColorRgb.length][2]); + paintLabel.setARGB(255, arrColorRgb[i%arrColorRgb.length][0], + arrColorRgb[i%arrColorRgb.length][1], arrColorRgb[i%arrColorRgb.length][2]); + if(selectedIndex == i){ + paintLabel.setTypeface(Typeface.DEFAULT_BOLD); + mLinePaint.setTypeface(Typeface.DEFAULT_BOLD); + }else{ + paintLabel.setTypeface(Typeface.DEFAULT); + mLinePaint.setTypeface(Typeface.DEFAULT); + } + paintLabel.setTextSize(tagTextSize); + + RoseChartBean bean = dataList.get(i); + + /**1、绘制扇形*/ + + canvas.drawArc(bean.getArcRect(), bean.getStartAngle(), bean.getSweepAngle(), true, paintArc); +// canvas.drawPath(allPath,mLinePaint); + if(selectedIndex == i){ + //被选中的,绘制边界 + paintSelected.setStyle(Paint.Style.STROKE);//设置空心 + canvas.drawArc(bean.getArcRect(), bean.getStartAngle(), bean.getSweepAngle(), true, paintSelected); + } + if(showChartLable || showChartNum){ + /**2、绘制直线*/ + List tagLinePoints = bean.getTagLinePoints(); + if (tagLinePoints != null && tagLinePoints.size() > 0) { + for (int p = 1; p < tagLinePoints.size(); p++) { + canvas.drawLine(tagLinePoints.get(p - 1).x, tagLinePoints.get(p - 1).y, + tagLinePoints.get(p).x, tagLinePoints.get(p).y, mLinePaint); + } + } + if(showChartLable){ + /**3、绘制指示标签*/ + canvas.drawText(bean.getName(), bean.getTagTextPoint().x, bean.getTagTextPoint().y, paintLabel); + }else if(showChartNum){ + /**3、绘制指示标签*/ + canvas.drawText(bean.getPer()+"", bean.getTagTextPoint().x, bean.getTagTextPoint().y, paintLabel); + } + + } + + if(showNumTouched && selectedIndex == i) { + /**2、绘制直线*/ + List tagLinePoints = bean.getTagLinePoints(); + if (tagLinePoints != null && tagLinePoints.size() > 0) { + for (int p = 1; p < tagLinePoints.size(); p++) { + canvas.drawLine(tagLinePoints.get(p - 1).x, tagLinePoints.get(p - 1).y, + tagLinePoints.get(p).x, tagLinePoints.get(p).y, mLinePaint); + } + } + /**3、绘制数量*/ + canvas.drawText(bean.getPer()+"", bean.getTagTextPoint().x, bean.getTagTextPoint().y, paintLabel); + LogUtil.i(TAG, "绘制bean="+bean.getName()+" "+bean.getPer()); + } + + + /**4、绘制右侧item矩形*/ + canvas.drawRoundRect(bean.getColorRect(), 8, 8, paintArc);//第二个参数是x半径,第三个参数是y半径 +// LogUtil.i(TAG, "绘制lable矩形"+bean.getColorRect()); + if(selectedIndex == i){ + canvas.drawRoundRect(bean.getColorRect(), 8, 8, paintSelected); + } + + + /**5、绘制指示标签*/ + paintLabel.setTextSize(lableTextSize); + paintLabel.setColor(getContext().getResources().getColor(R.color.text_color_def)); + canvas.drawText(bean.getName(), bean.getNameTextPoint().x, bean.getNameTextPoint().y , paintLabel); + + if(showRightNum){ + /**6.绘制占比*/ + canvas.drawText(bean.getPer()+"", bean.getPerTextPoint().x, bean.getPerTextPoint().y , paintLabel); + /**7、绘制虚线*/ + if(bean.getDashPathPointStart()!=null){ + Path path = new Path(); + path.moveTo(bean.getDashPathPointStart().x,bean.getDashPathPointStart().y); + path.lineTo( bean.getDashPathPointEnd().x,bean.getDashPathPointEnd().y); + canvas.drawPath(path, mlableLinePaint); + } + } + + } + + //绘制中心内圆 + paintArc.setColor(backColor); + canvas.drawCircle(centerPoint.x, centerPoint.y, chartRaidusInner, paintArc); + } + + + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/charts/rosechart/RoseChartBean.java b/app/src/main/java/com/tairui/industrial_operation/widget/charts/rosechart/RoseChartBean.java new file mode 100644 index 0000000..10dab2e --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/charts/rosechart/RoseChartBean.java @@ -0,0 +1,162 @@ +package com.tairui.industrial_operation.widget.charts.rosechart; + +import java.util.List; + +import android.graphics.PointF; +import android.graphics.RectF; +import android.graphics.Region; + +/** + * autour : openXu + * date : 2017/7/24 11:04 + * className : RoseChartBean + * version : 1.0 + * description : 南丁格尔玫瑰图数据 + */ +public class RoseChartBean { + + private float per; + private String name; + //触摸相关 + private Region region; //扇形区域--用于判断手指触摸点是否在此范围 + private RectF rectLable; //矩形区域--用于判断手指触摸点是否在此范围 + //扇形相关 + private RectF arcRect; + private float startAngle; + private float sweepAngle; + //tag线段 + private List tagLinePoints; + private PointF tagTextPoint; + + //右侧lable + private RectF colorRect; + private PointF nameTextPoint; + private PointF perTextPoint; + private PointF dashPathPointStart; + private PointF dashPathPointEnd; + + @Override + public String toString() { + return "RoseChartBean{" + + "per=" + per + + ", name='" + name + '\'' + + '}'; + } + + public RoseChartBean(float per, String name) { + this.per = per; + this.name = name; + } + + public PointF getDashPathPointStart() { + return dashPathPointStart; + } + + public void setDashPathPointStart(PointF dashPathPointStart) { + this.dashPathPointStart = dashPathPointStart; + } + + public PointF getDashPathPointEnd() { + return dashPathPointEnd; + } + + public void setDashPathPointEnd(PointF dashPathPointEnd) { + this.dashPathPointEnd = dashPathPointEnd; + } + + public PointF getNameTextPoint() { + return nameTextPoint; + } + + public void setNameTextPoint(PointF nameTextPoint) { + this.nameTextPoint = nameTextPoint; + } + + public PointF getPerTextPoint() { + return perTextPoint; + } + + public void setPerTextPoint(PointF perTextPoint) { + this.perTextPoint = perTextPoint; + } + + public RectF getColorRect() { + return colorRect; + } + + public void setColorRect(RectF colorRect) { + this.colorRect = colorRect; + } + + public PointF getTagTextPoint() { + return tagTextPoint; + } + + public void setTagTextPoint(PointF tagTextPoint) { + this.tagTextPoint = tagTextPoint; + } + + public List getTagLinePoints() { + return tagLinePoints; + } + + public void setTagLinePoints(List tagLinePoints) { + this.tagLinePoints = tagLinePoints; + } + + public RectF getArcRect() { + return arcRect; + } + + public void setArcRect(RectF arcRect) { + this.arcRect = arcRect; + } + + public float getStartAngle() { + return startAngle; + } + + public void setStartAngle(float startAngle) { + this.startAngle = startAngle; + } + + public float getSweepAngle() { + return sweepAngle; + } + + public void setSweepAngle(float sweepAngle) { + this.sweepAngle = sweepAngle; + } + + public RectF getRectLable() { + return rectLable; + } + + public void setRectLable(RectF rectLable) { + this.rectLable = rectLable; + } + + public Region getRegion() { + return region; + } + + public void setRegion(Region region) { + this.region = region; + } + + public float getPer() { + return per; + } + + public void setPer(float per) { + this.per = per; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/flowlayout/FlowLayout.java b/app/src/main/java/com/tairui/industrial_operation/widget/flowlayout/FlowLayout.java new file mode 100644 index 0000000..9333c65 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/flowlayout/FlowLayout.java @@ -0,0 +1,251 @@ +package com.tairui.industrial_operation.widget.flowlayout; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +import com.tairui.industrial_operation.R; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.util.LayoutDirection; +import android.view.View; +import android.view.ViewGroup; +import androidx.core.text.TextUtilsCompat; + + +public class FlowLayout extends ViewGroup { + private static final String TAG = "FlowLayout"; + protected static final int LEFT = -1; + protected static final int CENTER = 0; + protected static final int RIGHT = 1; + + // 记录所有行 + protected List> mAllViews = new ArrayList>(); + // 记录所有行高 + protected List mLineHeight = new ArrayList(); + // 记录所有行宽 + protected List mLineWidth = new ArrayList(); + // 临时记录每行的view + protected List lineViews = new ArrayList<>(); + protected int mGravity; + // 最大行数 + private int maxLine = -1; + + /** + * 设置最大行数 + * + * @param maxLine 最大行数 + */ + public void setMaxLine(int maxLine) { + this.maxLine = maxLine; + } + + /** + * 获取当前总行数 + * + * @return + */ + public int getTotalLines() { + return mAllViews.size(); + } + + public FlowLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TagFlowLayout); + mGravity = ta.getInt(R.styleable.TagFlowLayout_tag_gravity, LEFT); + int layoutDirection = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()); + if (layoutDirection == LayoutDirection.RTL) { + if (mGravity == LEFT) { + mGravity = RIGHT; + } else { + mGravity = LEFT; + } + } + ta.recycle(); + } + + public FlowLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public FlowLayout(Context context) { + this(context, null); + } + + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + + mAllViews.clear();//记录所有行的view + mLineHeight.clear();//记录每一行的高度 + mLineWidth.clear();//记录每一行的宽度 + lineViews.clear();//记录每一行的view + + int sizeWidth = MeasureSpec.getSize(widthMeasureSpec); + int modeWidth = MeasureSpec.getMode(widthMeasureSpec); + int sizeHeight = MeasureSpec.getSize(heightMeasureSpec); + int modeHeight = MeasureSpec.getMode(heightMeasureSpec); + + // wrap_content 最终宽高 + int width = 0; + int height = 0; + + //当前已用行宽高 + int lineWidth = 0; + int lineHeight = 0; + + int cCount = getChildCount(); + + for (int i = 0; i < cCount; i++) { + View child = getChildAt(i); + + if (child.getVisibility() == View.GONE) { + continue; + } + //测量子view + measureChild(child, widthMeasureSpec, heightMeasureSpec); + MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); + //子View宽高 + int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; + int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; + + if (lineWidth + childWidth > sizeWidth - getPaddingLeft() - getPaddingRight()) { + // +1是因为后面还有最后一行 + if (maxLine > 0 && mAllViews.size() + 1 >= maxLine) { + // 超过最大行数跳出循环 + break; + } + // 需要换行 + width = Math.max(width, lineWidth);// 记录最大行宽 + height += lineHeight;// 累加包裹内容所需的高度 + + //换行,保存上一行数据 + mLineHeight.add(lineHeight); + mLineWidth.add(lineWidth); + mAllViews.add(lineViews); + + //重置新行变量 + lineWidth = 0;//重新赋值行宽 + lineHeight = 0;//重新赋值行高 + lineViews = new ArrayList(); + } + //记录当前行数据 + lineWidth += childWidth;//累加行宽 + lineHeight = Math.max(lineHeight, childHeight);//取当前行最大高度作为行高 + lineViews.add(child); + + + } + + //添加最后一行数据 + //包裹内容所需的最大宽度 + width = Math.max(lineWidth, width); + height += lineHeight; + mLineHeight.add(lineHeight); + mLineWidth.add(lineWidth); + mAllViews.add(lineViews); + + setMeasuredDimension( + //父控件宽高确定则用确定的,否则用测量后的 + modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width + getPaddingLeft() + getPaddingRight(), + modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height + getPaddingTop() + getPaddingBottom()// + ); + + } + + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + //总宽 + int width = getWidth(); + //当前已用行宽高 + int lineHeight = 0; + //下面是对每一行的View进行布局 + int left = getPaddingLeft(); + int top = getPaddingTop(); + int lineNum = mAllViews.size(); + for (int i = 0; i < lineNum; i++) { + //获取当前行和行高 + lineViews = mAllViews.get(i); + lineHeight = mLineHeight.get(i); + + // set gravity + int currentLineWidth = this.mLineWidth.get(i); + switch (this.mGravity) { + case LEFT: + left = getPaddingLeft(); + break; + case CENTER: + left = (width - currentLineWidth) / 2 + getPaddingLeft(); + break; + case RIGHT: + // 适配了rtl,需要补偿一个padding值 ,从右边向左开始布局 + left = width - (currentLineWidth + getPaddingLeft()) - getPaddingRight(); + // 适配了rtl,需要把lineViews里面的数组倒序排,从右边开始存放view + Collections.reverse(lineViews); + break; + default: + break; + } + //开始一行行地布局子view + for (int j = 0; j < lineViews.size(); j++) { + View child = lineViews.get(j); + if (child.getVisibility() == View.GONE) { + continue; + } + + MarginLayoutParams lp = (MarginLayoutParams) child + .getLayoutParams(); + + int lc = left + lp.leftMargin; + int tc = top + lp.topMargin; + int rc = lc + child.getMeasuredWidth(); + int bc = tc + child.getMeasuredHeight(); + + child.layout(lc, tc, rc, bc); + + //更新下一个view添加到当前行的left + left += child.getMeasuredWidth() + lp.leftMargin + + lp.rightMargin; + } + //更新下一个view添加到下一行的top + top += lineHeight; + } + } + + @Override + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return new MarginLayoutParams(getContext(), attrs); + } + + @Override + protected LayoutParams generateDefaultLayoutParams() { + return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + } + + @Override + protected LayoutParams generateLayoutParams(LayoutParams p) { + return new MarginLayoutParams(p); + } + + /** + * 获取指定行数内的item个数 + * + * @return 每行的个数之和 + * @lineNum 总行数 + */ + public int getTotalByLine(int lineNum) { + int count = 0; + if (lineNum <= mAllViews.size()) { + for (int i = 0; i < lineNum; i++) { + List line = mAllViews.get(i); + count += line.size(); + } + } + + return count; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/flowlayout/TagAdapter.java b/app/src/main/java/com/tairui/industrial_operation/widget/flowlayout/TagAdapter.java new file mode 100644 index 0000000..16a1d16 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/flowlayout/TagAdapter.java @@ -0,0 +1,91 @@ +package com.tairui.industrial_operation.widget.flowlayout; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import android.util.Log; +import android.view.View; + +public abstract class TagAdapter { + private List mTagDatas; + private OnDataChangedListener mOnDataChangedListener; + @Deprecated + private HashSet mCheckedPosList = new HashSet(); + + public TagAdapter(List datas) { + mTagDatas = datas; + } + + @Deprecated + public TagAdapter(T[] datas) { + mTagDatas = new ArrayList(Arrays.asList(datas)); + } + + interface OnDataChangedListener { + void onChanged(); + } + + public void setData(List datas) { + mTagDatas = datas; + notifyDataChanged(); + } + + void setOnDataChangedListener(OnDataChangedListener listener) { + mOnDataChangedListener = listener; + } + + @Deprecated + public void setSelectedList(int... poses) { + Set set = new HashSet<>(); + for (int pos : poses) { + set.add(pos); + } + setSelectedList(set); + } + + @Deprecated + public void setSelectedList(Set set) { + mCheckedPosList.clear(); + if (set != null) { + mCheckedPosList.addAll(set); + } + notifyDataChanged(); + } + + @Deprecated + HashSet getPreCheckedList() { + return mCheckedPosList; + } + + public int getCount() { + return mTagDatas == null ? 0 : mTagDatas.size(); + } + + public void notifyDataChanged() { + if (mOnDataChangedListener != null) { + mOnDataChangedListener.onChanged(); + } + } + + public T getItem(int position) { + return mTagDatas.get(position); + } + + public abstract View getView(FlowLayout parent, int position, T t); + + public void onSelected(int position, View view) { + Log.d("zhy", "onSelected " + position); + } + + public void unSelected(int position, View view) { + Log.d("zhy", "unSelected " + position); + } + + public boolean setSelected(int position, T t) { + return false; + } + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/flowlayout/TagFlowLayout.java b/app/src/main/java/com/tairui/industrial_operation/widget/flowlayout/TagFlowLayout.java new file mode 100644 index 0000000..7cfff75 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/flowlayout/TagFlowLayout.java @@ -0,0 +1,247 @@ +package com.tairui.industrial_operation.widget.flowlayout; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import com.tairui.industrial_operation.R; + +import android.content.Context; +import android.content.res.TypedArray; +import android.os.Bundle; +import android.os.Parcelable; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; + + +/** + * Created by zhy on 15/9/10. + */ +public class TagFlowLayout extends FlowLayout implements TagAdapter.OnDataChangedListener { + + private TagAdapter mTagAdapter; + private int mSelectedMax = -1;//-1为不限制数量 + private static final String TAG = "TagFlowLayout"; + + private Set mSelectedView = new HashSet(); + + private OnSelectListener mOnSelectListener; + private OnTagClickListener mOnTagClickListener; + + public interface OnSelectListener { + void onSelected(Set selectPosSet); + } + + public interface OnTagClickListener { + boolean onTagClick(View view, int position, FlowLayout parent); + } + + public TagFlowLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TagFlowLayout); + mSelectedMax = ta.getInt(R.styleable.TagFlowLayout_max_select, -1); + ta.recycle(); + } + + public TagFlowLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public TagFlowLayout(Context context) { + this(context, null); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int cCount = getChildCount(); + for (int i = 0; i < cCount; i++) { + TagView tagView = (TagView) getChildAt(i); + if (tagView.getVisibility() == View.GONE) { + continue; + } + if (tagView.getTagView().getVisibility() == View.GONE) { + tagView.setVisibility(View.GONE); + } + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + public void setOnSelectListener(OnSelectListener onSelectListener) { + mOnSelectListener = onSelectListener; + } + + public void setOnTagClickListener(OnTagClickListener onTagClickListener) { + mOnTagClickListener = onTagClickListener; + } + + public void setAdapter(TagAdapter adapter) { + mTagAdapter = adapter; + mTagAdapter.setOnDataChangedListener(this); + mSelectedView.clear(); + changeAdapter(); + } + + @SuppressWarnings("ResourceType") + private void changeAdapter() { + removeAllViews(); + TagAdapter adapter = mTagAdapter; + TagView tagViewContainer = null; + HashSet preCheckedList = mTagAdapter.getPreCheckedList(); + for (int i = 0; i < adapter.getCount(); i++) { + View tagView = adapter.getView(this, i, adapter.getItem(i)); + + tagViewContainer = new TagView(getContext()); + tagView.setDuplicateParentStateEnabled(true); + if (tagView.getLayoutParams() != null) { + tagViewContainer.setLayoutParams(tagView.getLayoutParams()); + + } else { + ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + lp.setMargins(dip2px(getContext(), 5), + dip2px(getContext(), 5), + dip2px(getContext(), 5), + dip2px(getContext(), 5)); + tagViewContainer.setLayoutParams(lp); + } + ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + tagView.setLayoutParams(lp); + tagViewContainer.addView(tagView); + addView(tagViewContainer); + + if (preCheckedList.contains(i)) { + setChildChecked(i, tagViewContainer); + } + + if (mTagAdapter.setSelected(i, adapter.getItem(i))) { + setChildChecked(i, tagViewContainer); + } + tagView.setClickable(false); + final TagView finalTagViewContainer = tagViewContainer; + final int position = i; + tagViewContainer.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + doSelect(finalTagViewContainer, position); + if (mOnTagClickListener != null) { + mOnTagClickListener.onTagClick(finalTagViewContainer, position, + TagFlowLayout.this); + } + } + }); + } + mSelectedView.addAll(preCheckedList); + } + + public void setMaxSelectCount(int count) { + if (mSelectedView.size() > count) { + Log.w(TAG, "you has already select more than " + count + " views , so it will be clear ."); + mSelectedView.clear(); + } + mSelectedMax = count; + } + + public Set getSelectedList() { + return new HashSet(mSelectedView); + } + + private void setChildChecked(int position, TagView view) { + view.setChecked(true); + mTagAdapter.onSelected(position, view.getTagView()); + } + + private void setChildUnChecked(int position, TagView view) { + view.setChecked(false); + mTagAdapter.unSelected(position, view.getTagView()); + } + + private void doSelect(TagView child, int position) { + if (!child.isChecked()) { + //处理max_select=1的情况 + if (mSelectedMax == 1 && mSelectedView.size() == 1) { + Iterator iterator = mSelectedView.iterator(); + Integer preIndex = iterator.next(); + TagView pre = (TagView) getChildAt(preIndex); + setChildUnChecked(preIndex, pre); + setChildChecked(position, child); + + mSelectedView.remove(preIndex); + mSelectedView.add(position); + } else { + if (mSelectedMax > 0 && mSelectedView.size() >= mSelectedMax) { + return; + } + setChildChecked(position, child); + mSelectedView.add(position); + } + } else { + setChildUnChecked(position, child); + mSelectedView.remove(position); + } + if (mOnSelectListener != null) { + mOnSelectListener.onSelected(new HashSet(mSelectedView)); + } + } + + public TagAdapter getAdapter() { + return mTagAdapter; + } + + private static final String KEY_CHOOSE_POS = "key_choose_pos"; + private static final String KEY_DEFAULT = "key_default"; + + @Override + protected Parcelable onSaveInstanceState() { + Bundle bundle = new Bundle(); + bundle.putParcelable(KEY_DEFAULT, super.onSaveInstanceState()); + + String selectPos = ""; + if (mSelectedView.size() > 0) { + for (int key : mSelectedView) { + selectPos += key + "|"; + } + selectPos = selectPos.substring(0, selectPos.length() - 1); + } + bundle.putString(KEY_CHOOSE_POS, selectPos); + return bundle; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + if (state instanceof Bundle) { + Bundle bundle = (Bundle) state; + String mSelectPos = bundle.getString(KEY_CHOOSE_POS); + if (!TextUtils.isEmpty(mSelectPos)) { + String[] split = mSelectPos.split("\\|"); + for (String pos : split) { + int index = Integer.parseInt(pos); + mSelectedView.add(index); + + TagView tagView = (TagView) getChildAt(index); + if (tagView != null) { + setChildChecked(index, tagView); + } + } + + } + super.onRestoreInstanceState(bundle.getParcelable(KEY_DEFAULT)); + return; + } + super.onRestoreInstanceState(state); + } + + @Override + public void onChanged() { + mSelectedView.clear(); + changeAdapter(); + } + + public static int dip2px(Context context, float dpValue) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/flowlayout/TagView.java b/app/src/main/java/com/tairui/industrial_operation/widget/flowlayout/TagView.java new file mode 100644 index 0000000..f7342dd --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/flowlayout/TagView.java @@ -0,0 +1,72 @@ +package com.tairui.industrial_operation.widget.flowlayout; + +import android.content.Context; +import android.view.View; +import android.widget.Checkable; +import android.widget.FrameLayout; + +/** + * Created by zhy on 15/9/10. + */ +public class TagView extends FrameLayout implements Checkable +{ + private boolean isChecked; + private static final int[] CHECK_STATE = new int[]{android.R.attr.state_checked}; + + public TagView(Context context) + { + super(context); + } + + public View getTagView() + { + return getChildAt(0); + } + + @Override + public int[] onCreateDrawableState(int extraSpace) + { + int[] states = super.onCreateDrawableState(extraSpace + 1); + if (isChecked()) + { + mergeDrawableStates(states, CHECK_STATE); + } + return states; + } + + + /** + * Change the checked state of the view + * + * @param checked The new checked state + */ + @Override + public void setChecked(boolean checked) + { + if (this.isChecked != checked) + { + this.isChecked = checked; + refreshDrawableState(); + } + } + + /** + * @return The current checked state of the view + */ + @Override + public boolean isChecked() + { + return isChecked; + } + + /** + * Change the checked state of the view to the inverse of its current state + */ + @Override + public void toggle() + { + setChecked(!isChecked); + } + + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/loading/FinishDrawListener.java b/app/src/main/java/com/tairui/industrial_operation/widget/loading/FinishDrawListener.java new file mode 100644 index 0000000..2e69983 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/loading/FinishDrawListener.java @@ -0,0 +1,16 @@ +package com.tairui.industrial_operation.widget.loading; + +import android.view.View; + +/** + * Created by Luo_xiasuhuei321@163.com on 2016/11/6. + * desc: + */ +public interface FinishDrawListener { + /** + * 分发绘制完成事件 + * + * @param v 绘制完成的View + */ + void dispatchFinishEvent(View v); +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/loading/LVCircularRing.java b/app/src/main/java/com/tairui/industrial_operation/widget/loading/LVCircularRing.java new file mode 100644 index 0000000..52a4122 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/loading/LVCircularRing.java @@ -0,0 +1,136 @@ +package com.tairui.industrial_operation.widget.loading; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.View; +import android.view.animation.LinearInterpolator; +import androidx.annotation.ColorInt; + +/** + * Created by Luo on 2016/9/23. + * desc: + */ +public class LVCircularRing extends View { + public final String TAG = getClass().getSimpleName(); + private float mWidth = 0f; + private float mPadding = 0f; + private float startAngle = 0f; + private Paint mPaint; + private int color = Color.argb(100, 255, 255, 255); + private Paint mPaint2; + + public LVCircularRing(Context context) { + this(context, null); + } + + public LVCircularRing(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public LVCircularRing(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initPaint(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + if (getMeasuredWidth() > getHeight()) + mWidth = getMeasuredHeight(); + else + mWidth = getMeasuredWidth(); + mPadding = 5; + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + +// mPaint.setColor(Color.argb(100, 255, 255, 255)); + mPaint2.setColor(color); + canvas.drawCircle(mWidth / 2, mWidth / 2, mWidth / 2 - mPadding, mPaint2); + mPaint.setColor(Color.WHITE); + RectF rectF = new RectF(mPadding, mPadding, mWidth - mPadding, mWidth - mPadding); + canvas.drawArc(rectF, startAngle, 100 + , false, mPaint);//第四个参数是否显示半径 + } + + + private void initPaint() { + mPaint = new Paint(); + mPaint.setAntiAlias(true); + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setColor(color); + mPaint.setStrokeWidth(8); + + mPaint2 = new Paint(); + mPaint2.setAntiAlias(true); + mPaint2.setStyle(Paint.Style.STROKE); + mPaint2.setStrokeWidth(8); + mPaint2.setColor(color); + } + + public void startAnim() { + stopAnim(); + startViewAnim(0f, 1f, 1000); + } + + public void stopAnim() { + if (valueAnimator != null) { + clearAnimation(); + valueAnimator.setRepeatCount(1); + valueAnimator.cancel(); + valueAnimator.end(); + } + } + + ValueAnimator valueAnimator; + + private ValueAnimator startViewAnim(float startF, final float endF, long time) { + valueAnimator = ValueAnimator.ofFloat(startF, endF); + + valueAnimator.setDuration(time); + valueAnimator.setInterpolator(new LinearInterpolator()); + valueAnimator.setRepeatCount(ValueAnimator.INFINITE);//无限循环 + valueAnimator.setRepeatMode(ValueAnimator.RESTART);// + + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + + float value = (float) valueAnimator.getAnimatedValue(); + startAngle = 360 * value; + + invalidate(); + } + }); + valueAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + } + }); + if (!valueAnimator.isRunning()) { + valueAnimator.start(); + } + + return valueAnimator; + } + + public void setColor(@ColorInt int color) { + this.color = color; + mPaint.setColor(color); + mPaint2.setColor(color); + } +} + + + diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/loading/LoadCircleView.java b/app/src/main/java/com/tairui/industrial_operation/widget/loading/LoadCircleView.java new file mode 100644 index 0000000..21ed5d4 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/loading/LoadCircleView.java @@ -0,0 +1,100 @@ +package com.tairui.industrial_operation.widget.loading; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.View; + +/** + * Created by xiasuhuei321 on 2017/5/15. + * author:luo + * e-mail:xiasuhuei321@163.com + */ + +public class LoadCircleView extends View { + public final String TAG = getClass().getSimpleName(); + + private float mPadding = 0f; + private RectF rectF; + private Context mContext; + private Paint mPaint; + private int mWidth = 0; + private int currentLineIndex = 0; + + public LoadCircleView(Context context) { + this(context, null); + } + + public LoadCircleView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public LoadCircleView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mContext = context; + init(); + } + + public void init() { + mPaint = new Paint(); + mPaint.setAntiAlias(true); + mPaint.setStyle(Paint.Style.STROKE); +// mPaint.setColor(Color.BLACK); + mPaint.setStrokeWidth(8); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); + int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); + + int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); + + if (widthSpecMode != MeasureSpec.AT_MOST && heightSpecMode != MeasureSpec.AT_MOST) { + mWidth = widthSpecSize >= heightSpecSize ? widthSpecSize : heightSpecSize; + } else if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode != MeasureSpec.AT_MOST) { + mWidth = heightSpecSize; + } else if (widthSpecMode != MeasureSpec.AT_MOST) { + mWidth = widthSpecSize; + } else { + mWidth = SizeUtils.dip2px(mContext, 50); + } + setMeasuredDimension(mWidth, mWidth); + mPadding = 8; +// rectF = new RectF(mPadding, mPadding, mWidth - mPadding, mWidth - mPadding); + } + + @Override + protected void onDraw(Canvas canvas) { + // 圆心坐标 (center,center) + int center = mWidth >> 1; + int radius = (mWidth >> 1) - 8; + if (currentLineIndex >= 12) + currentLineIndex = 0; +// canvas.rotate(currentLineIndex * 30, center, center); + // 画12根线 + for (int i = 0; i < 12; i++) { + if (i < currentLineIndex + 4 && i >= currentLineIndex) { + mPaint.setColor(Color.GRAY); + } else if (currentLineIndex > 8 && i < currentLineIndex + 4 - 12) { + mPaint.setColor(Color.GRAY); + } else { + mPaint.setColor(Color.WHITE); + } + +// canvas.drawLine(center, (float) (center + 1.0 / 4 * center), +// center, (float) (center + 1.0 / 2 * radius), mPaint); + canvas.drawLine(center, (float) (center + 1.0 / 2 * radius), + center, 2 * radius, mPaint); + canvas.rotate(30, center, center); + } + currentLineIndex++; + postInvalidateDelayed(50); + } + + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/loading/LoadingDialog.java b/app/src/main/java/com/tairui/industrial_operation/widget/loading/LoadingDialog.java new file mode 100644 index 0000000..2a9751f --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/loading/LoadingDialog.java @@ -0,0 +1,518 @@ +package com.tairui.industrial_operation.widget.loading; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; + +import com.tairui.industrial_operation.R; +import com.tairui.industrial_operation.widget.loading.manager.StyleManager; + +import android.annotation.SuppressLint; +import android.app.Dialog; +import android.content.Context; +import android.os.Handler; +import android.os.Message; +import android.text.TextUtils; +import android.util.Log; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; +import androidx.annotation.ColorInt; + +/** + * desc:加载等待的Dialog + */ +public class LoadingDialog implements FinishDrawListener { + // private final String TAG = "LoadingDialog"; + public static final int STYLE_RING = 0; + private static final int STYLE_LINE = 1; + // private Context mContext; + + private LVCircularRing mLoadingView; + private Dialog mLoadingDialog; + private LinearLayout layout; + private TextView loadingText; + private RightDiaView mSuccessView; + private WrongDiaView mFailedView; + + private String loadSuccessStr; + private String loadFailedStr; + private List viewList; + + private boolean interceptBack = true; + private boolean openSuccessAnim = true; + private boolean openFailedAnim = true; + private int speed = 1; + private long time = 1000; + private int loadStyle = STYLE_RING; + + private static StyleManager s = StyleManager.getDefault(); + private LoadCircleView mCircleLoadView; + + private MyHandler myHandler; + + public enum Speed { + SPEED_ONE, + SPEED_TWO + } + + private OnFinshListener onFinshListener; + private DismissListener dismissListener; + + @SuppressLint("InflateParams") + public LoadingDialog(Context context) { + // mContext = context; + // 首先得到整个View + View view = LayoutInflater.from(context).inflate( + R.layout.view_loading_dialog, null); + initView(view); + // 创建自定义样式的Dialog + mLoadingDialog = new Dialog(context, R.style.loading_dialog); + // mLoadingDialog = new Dialog(context, R.style.loading_dialog) { + // @Override + // public void onBackPressed() { + // if (interceptBack) { + // return; + // } + // LoadingDialog.this.close(); + // } + // }; + myHandler = new MyHandler(LoadingDialog.this); + // 设置返回键无效 + mLoadingDialog.setCancelable(!interceptBack); + mLoadingDialog.setContentView(layout, new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT)); + + mLoadingDialog.setOnDismissListener(dialog -> { + // if (dismissListener != null) { + // dismissListener.dimiss(); + // } + dialog.dismiss(); + }); + initStyle(); + } + + private void initView(View view) { + layout = view.findViewById(R.id.dialog_view); + mLoadingView = view.findViewById(R.id.lv_circularring); + loadingText = view.findViewById(R.id.loading_text); + mSuccessView = view.findViewById(R.id.rdv_right); + mFailedView = view.findViewById(R.id.wv_wrong); + mCircleLoadView = view.findViewById(R.id.lcv_circleload); + initData(); + } + + private void initData() { + viewList = new ArrayList<>(); + viewList.add(mLoadingView); + viewList.add(mSuccessView); + viewList.add(mFailedView); + viewList.add(mCircleLoadView); + + mSuccessView.setOnDrawFinishListener(this); + mFailedView.setOnDrawFinishListener(this); + } + + @Override + public void dispatchFinishEvent(View v) { + if (v instanceof WrongDiaView) { + myHandler.sendEmptyMessageDelayed(2, time); + } else { + myHandler.sendEmptyMessageDelayed(1, time); + } + } + + private void hideAll() { + for (View v : viewList) { + if (v.getVisibility() != View.GONE) { + v.setVisibility(View.GONE); + } + } + } + + private void setParams(int size) { + if (size < 0) { + return; + } + ViewGroup.LayoutParams successParams = mSuccessView.getLayoutParams(); + successParams.height = size; + successParams.width = size; + mSuccessView.setLayoutParams(successParams); + + ViewGroup.LayoutParams failedParams = mFailedView.getLayoutParams(); + failedParams.height = size; + failedParams.width = size; + mFailedView.setLayoutParams(failedParams); + + ViewGroup.LayoutParams loadingParams = mLoadingView.getLayoutParams(); + loadingParams.height = size; + loadingParams.width = size; + } + + // 会在最后将所有消息移除 + // @SuppressLint("HandlerLeak") + // private Handler handler = new Handler() { + // + // @Override + // public void handleMessage(Message msg) { + // close(); + // if (onFinshListener != null) { + // onFinshListener.onFinish(); + // } + // } + // }; + + private static class MyHandler extends Handler { + private WeakReference weakReference; + + MyHandler(LoadingDialog loadingDialog) { + weakReference = new WeakReference<>(loadingDialog); + } + + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + LoadingDialog dialog = weakReference.get(); + if (dialog != null) { + dialog.close(); + if (null != dialog.onFinshListener) { + dialog.onFinshListener.onFinish(); + } + } + } + } + + private void initStyle() { + if (s != null) { + setInterceptBack(s.isInterceptBack()); + setRepeatCount(s.getRepeatTime()); + setParams(s.getContentSize()); + setTextSize(s.getTextSize()); + setShowTime(s.getShowTime()); + if (!s.isOpenAnim()) { + closeFailedAnim(); + closeSuccessAnim(); + } + setLoadingText(s.getLoadText()); + setSuccessText(s.getSuccessText()); + setFailedText(s.getFailedText()); + setLoadStyle(s.getLoadStyle()); + } + } + + //----------------------------------对外提供的api------------------------------// + + /** + * please invoke show() method at last,because it's + * return value is void + * 请在最后调用show,因此show返回值为void会使链式api断开 + */ + public void show() { + hideAll(); + if (loadStyle == STYLE_RING) { + mLoadingView.setVisibility(View.VISIBLE); + mCircleLoadView.setVisibility(View.GONE); + mLoadingDialog.show(); + mLoadingView.startAnim(); + Log.i("show", "style_ring"); + } else if (loadStyle == STYLE_LINE) { + mCircleLoadView.setVisibility(View.VISIBLE); + mLoadingView.setVisibility(View.GONE); + mLoadingDialog.show(); + Log.i("show", "style_line"); + } + } + + /** + * set load style + * 设置load的样式,目前支持转圈圈和菊花转圈圈 + * + * @param style 样式 + */ + public LoadingDialog setLoadStyle(int style) { + if (style >= 3) { + throw new IllegalArgumentException("Your style is wrong: style = " + style); + } + this.loadStyle = style; + return this; + } + + /** + * 让这个dialog消失,在拦截back事件的情况下一定要调用这个方法! + * 在调用了该方法之后如需再次使用loadingDialog,请新创建一个 + * LoadingDialog对象。 + */ + public void close() { + myHandler.removeCallbacksAndMessages(null); + if (mLoadingDialog != null) { + mLoadingView.stopAnim(); + mLoadingDialog.dismiss(); + } + } + + /** + * 设置加载时的文字提示 + * + * @param msg 文字 + * + * @return 这个对象 + */ + public LoadingDialog setLoadingText(String msg) { + if (!TextUtils.isEmpty(msg)) { + loadingText.setVisibility(View.VISIBLE); + loadingText.setText(msg); + } else { + loadingText.setVisibility(View.GONE); + } + return this; + } + + /** + * 设置加载成功的文字提示 + * + * @param msg 文字 + * + * @return 这个对象 + */ + public LoadingDialog setSuccessText(String msg) { + loadSuccessStr = msg; + return this; + } + + /** + * 设置加载失败的文字提示 + * + * @param msg 文字 + * + * @return 这个对象 + */ + public LoadingDialog setFailedText(String msg) { + loadFailedStr = msg; + return this; + } + + /** + * when you need a successful feedback,please invoke + * this method in success's callback + * 当你需要一个成功的反馈的时候,在加载成功的回调中调用此方法 + */ + public void loadSuccess() { + mLoadingView.stopAnim(); + hideAll(); + mSuccessView.setDrawDynamic(openSuccessAnim); + mSuccessView.setVisibility(View.VISIBLE); + if (loadSuccessStr == null) { + loadingText.setVisibility(View.GONE); + } else { + loadingText.setVisibility(View.VISIBLE); + loadingText.setText(loadSuccessStr); + } + } + + /** + * when you need a fail feedback,please invoke this + * method in failed callback + * 当你需要一个失败的反馈的时候,在加载失败的回调中调用此方法 + */ + public void loadFailed() { + mLoadingView.stopAnim(); + hideAll(); + mFailedView.setDrawDynamic(openFailedAnim); + mFailedView.setVisibility(View.VISIBLE); + if (loadFailedStr == null) { + loadingText.setVisibility(View.GONE); + } else { + loadingText.setVisibility(View.VISIBLE); + loadingText.setText(loadFailedStr); + } + } + + /** + * 关闭动态绘制 + */ + public LoadingDialog closeSuccessAnim() { + this.openSuccessAnim = false; + return this; + } + + /** + * 关闭动态绘制 + */ + public LoadingDialog closeFailedAnim() { + this.openFailedAnim = false; + return this; + } + + /** + * 设置是否拦截back,默认会拦截 + * + * @param interceptBack true拦截back,false不拦截 + * + * @return 这个对象 + */ + public LoadingDialog setInterceptBack(boolean interceptBack) { + this.interceptBack = interceptBack; + mLoadingDialog.setCancelable(!interceptBack); + return this; + } + + /** + * 当前dialog是否拦截back事件 + * + * @return 如果拦截返回true,反之false + */ + public boolean getInterceptBack() { + return interceptBack; + } + + /** + * 使用该方法改变成功和失败绘制的速度 + * + * @param speed 绘制速度 + * + * @return 这个对象 + */ + public LoadingDialog setLoadSpeed(Speed speed) { + if (speed == Speed.SPEED_ONE) { + this.speed = 1; + mSuccessView.setSpeed(1); + mFailedView.setSpeed(1); + } else { + this.speed = 2; + mSuccessView.setSpeed(2); + mFailedView.setSpeed(2); + } + return this; + } + + /** + * 返回当前绘制的速度 + * + * @return 速度 + */ + public int getSpeed() { + return this.speed; + } + + /** + * 此方法改变成功失败绘制的颜色,此方法增加了处理的复杂性,暂时不公开此方法。 + * 而且暂时没有做到方便调用,真的要用的话十分的麻烦,暂时隐藏,后续不确定是否公开。 + */ + private LoadingDialog setDrawColor(@ColorInt int color) { + mFailedView.setDrawColor(color); + mSuccessView.setDrawColor(color); + loadingText.setTextColor(color); + mLoadingView.setColor(color); + return this; + } + + /** + * 设置中间弹框的尺寸 + * + * @param size 尺寸,单位px + * + * @return 这个对象 + */ + public LoadingDialog setSize(int size) { + // int dip = SizeUtils.px2dip(mContext, size); + if (size <= 50) { + return this; + } + setParams(size); + return this; + } + + /** + * 设置重新绘制的次数,默认只绘制一次,如果你设置这个 + * 数值为1,那么在绘制一次过后,还会再次绘制一次。 + * + * @param count 绘制次数 + * + * @return 这个对象 + */ + public LoadingDialog setRepeatCount(int count) { + mFailedView.setRepeatTime(count); + mSuccessView.setRepeatTime(count); + return this; + } + + public boolean isShow() { + if (null != mLoadingDialog) { + return mLoadingDialog.isShowing(); + } else { + return false; + } + } + + /** + * 设置反馈展示时间 + * + * @param time 时间 + * + * @return 这个对象 + */ + public LoadingDialog setShowTime(long time) { + if (time < 0) { + return this; + } + this.time = time; + return this; + } + + /** + * set the size of load text size + * 设置加载字体大小 + * + * @param size 尺寸,单位sp,来将sp转换为对应的px值 + * + * @return 这个对象 + */ + public LoadingDialog setTextSize(float size) { + if (size < 0) { + return this; + } + loadingText.setTextSize(TypedValue.COMPLEX_UNIT_SP, size); + return this; + } + + public static void initStyle(StyleManager style) { + if (style != null) { + s = style; + } + } + + /** + * dispatch draw finish event + * 传递绘制完成的事件 + * + * @param onFinshListener 回调接口 + */ + public void setOnFinishListener(OnFinshListener onFinshListener) { + this.onFinshListener = onFinshListener; + } + + /** + * 设置 dismiss 监听 + * + * @param dismissListener dismiss callback + */ + public LoadingDialog setDimissListener(DismissListener dismissListener) { + this.dismissListener = dismissListener; + return this; + } + + /** + * 监听器 + */ + public interface OnFinshListener { + void onFinish(); + } + + public interface DismissListener { + void dimiss(); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/loading/RightDiaView.java b/app/src/main/java/com/tairui/industrial_operation/widget/loading/RightDiaView.java new file mode 100644 index 0000000..d5791ac --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/loading/RightDiaView.java @@ -0,0 +1,214 @@ +package com.tairui.industrial_operation.widget.loading; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.View; + +/** + * Created by Luo_xiasuhuei321@163.com on 2016/11/5. + * desc: + */ + +public class RightDiaView extends View { + private final String TAG = this.getClass().getSimpleName(); + + private FinishDrawListener listener; + + private Context mContext; + private int mWidth = 0; + private float mPadding = 0f; + private Paint mPaint; + private RectF rectF; + + private int line1_x; + private int line1_y; + + private int line2_x; + private int line2_y; + + private int times = 0; + private boolean drawEveryTime = true; + private int speed = 1; + + public RightDiaView(Context context) { + this(context, null); + } + + public RightDiaView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public RightDiaView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); + int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); + + int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); + + if (widthSpecMode != MeasureSpec.AT_MOST && heightSpecMode != MeasureSpec.AT_MOST) { + mWidth = widthSpecSize >= heightSpecSize ? widthSpecSize : heightSpecSize; + } else if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode != MeasureSpec.AT_MOST) { + mWidth = heightSpecSize; + } else if (widthSpecMode != MeasureSpec.AT_MOST) { + mWidth = widthSpecSize; + } else { + mWidth = SizeUtils.dip2px(mContext, 80); + } + setMeasuredDimension(mWidth, mWidth); + mPadding = 8; + rectF = new RectF(mPadding, mPadding, mWidth - mPadding, mWidth - mPadding); + } + + private void init(Context context) { + mPaint = new Paint(); + //抗锯齿 + mPaint.setAntiAlias(true); + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setColor(Color.WHITE); + mPaint.setStrokeWidth(8); + mContext = context; + } + + int progress = 0; + + @Override + protected void onDraw(Canvas canvas) { + if (drawEveryTime) + drawDynamic(canvas); + else { + drawStatic(canvas); + if (listener != null) + listener.dispatchFinishEvent(this); + } + } + + private int count = 0; + + private void drawDynamic(Canvas canvas) { + if (progress < 100) + progress += speed; + //根据进度画圆弧 + canvas.drawArc(rectF, 235, 360 * progress / 100, false, mPaint); + + int center = mWidth / 2; + int center1 = center - mWidth / 5; + + int radius = mWidth / 2 - 8; + + //绘制对勾 + if (progress == 100) { + if (line1_x < radius / 3) { + line1_x += speed; + line1_y += speed; + } + //画第一根线 + canvas.drawLine(center1, center, center1 + line1_x, center + line1_y, mPaint); + + if (line1_x >= radius / 3 && line2_x == 0 && line2_y == 0) { + line2_x = line1_x; + line2_y = line1_y; + line1_x += speed; + line1_y += speed; + } + + if (line1_x >= radius / 3 && line2_x <= radius && line2_y <= center - radius / 3) { + line2_x += speed; + line2_y -= speed; + } + //画第二根线 + canvas.drawLine(center1 + line1_x - 1, center + line1_y - 4, + center1 + line2_x, center + line2_y, mPaint); + } + + if (line2_x > radius && progress >= 100 && line1_x != radius / 3) { + //1.只分发一次绘制完成的事件 + //2.只在最后一次绘制时分发 + if (count == 0 && times == 0 && listener != null) { + listener.dispatchFinishEvent(this); + count++; + } + + times--; + if (times >= 0) { + reDraw(); + invalidate(); + } else { + return; + } + } + + invalidate(); + } + + private void drawStatic(Canvas canvas) { + canvas.drawArc(rectF, 0, 360, false, mPaint); + + int center = mWidth / 2; + int center1 = center - mWidth / 5; + int radius = mWidth / 2 - 8; + canvas.drawLine(center1, center, + center1 + radius / 3, center + radius / 3, mPaint); + canvas.drawLine(center1 + radius / 3 - 1, center + radius / 3 - 4, + center1 + radius, center - radius / 3, mPaint); + } + + + private void reDraw() { + line1_x = 0; + line2_x = 0; + line1_y = 0; + line2_y = 0; + progress = 0; + } + + //---------------------------对外提供的api-------------------------// + + /** + * 设置重复绘制的次数,只在drawEveryTime = true时有效 + * + * @param times 重复次数,例如设置1,除了第一次绘制还会额外重绘一次 + */ + protected void setRepeatTime(int times) { + if (drawEveryTime) + this.times = times; + } + + /** + * 动态画出还是直接画出 + */ + protected void setDrawDynamic(boolean drawEveryTime) { + this.drawEveryTime = drawEveryTime; + } + + /** + * 调整绘制的速度,最小值默认为1 + * + * @param speed 速度 + */ + protected void setSpeed(int speed) { + if (speed <= 0 && speed >= 3) { + throw new IllegalArgumentException("support speed >0 & < 3, the speed you set is: " + speed); + } else { + this.speed = speed; + } + } + + protected void setDrawColor(int color) { + mPaint.setColor(color); + } + + public void setOnDrawFinishListener(FinishDrawListener f) { + this.listener = f; + } +} + diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/loading/SizeUtils.java b/app/src/main/java/com/tairui/industrial_operation/widget/loading/SizeUtils.java new file mode 100644 index 0000000..d71afd2 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/loading/SizeUtils.java @@ -0,0 +1,43 @@ +package com.tairui.industrial_operation.widget.loading; + +import android.content.Context; + +/** + * Created by Luo_xiasuhuei321@163.com on 2016/11/6. + * desc: + */ + +public class SizeUtils { + /** + * dp转px + */ + public static int dip2px(Context context, float dpValue) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + /** + * px转dp + */ + public static int px2dip(Context context, float pxValue) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (pxValue / scale + 0.5f); + } + + /** + * px转sp + */ + public static int px2sp(Context context, float spValue) { + final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; + return (int) (spValue / fontScale + 0.5f); + } + + /** + * sp转px + */ + public static int sp2px(Context context, float spValue) { + final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; + return (int) (spValue * fontScale + 0.5f); + } + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/loading/WrongDiaView.java b/app/src/main/java/com/tairui/industrial_operation/widget/loading/WrongDiaView.java new file mode 100644 index 0000000..5acbac5 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/loading/WrongDiaView.java @@ -0,0 +1,223 @@ +package com.tairui.industrial_operation.widget.loading; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.View; + + +/** + * Created by Luo_xiasuhuei321@163.com on 2016/11/6. + * desc: + */ +public class WrongDiaView extends View { + private final String TAG = this.getClass().getSimpleName(); + + private FinishDrawListener listener; + + private Context mContext; + private int mWidth = 0; + private Paint mPaint; + private RectF rectF; + + private int line1_x; + private int line1_y; + + private int line2_x; + private int line2_y; + + private int times = 0; + private boolean drawEveryTime = true; + private int speed = 1; + private int count = 0; +// private int color; + + public WrongDiaView(Context context) { + this(context, null); + } + + public WrongDiaView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public WrongDiaView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); +// initAttr(context, attrs, defStyleAttr); + initPaint(context); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); + int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); + + int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); + + if (widthSpecMode != MeasureSpec.AT_MOST && heightSpecMode != MeasureSpec.AT_MOST) { + mWidth = widthSpecSize >= heightSpecSize ? widthSpecSize : heightSpecSize; + } else if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode != MeasureSpec.AT_MOST) { + mWidth = heightSpecSize; + } else if (widthSpecMode != MeasureSpec.AT_MOST) { + mWidth = widthSpecSize; + } else { + mWidth = SizeUtils.dip2px(mContext, 80); + } + setMeasuredDimension(mWidth, mWidth); + float mPadding = 8; + rectF = new RectF(mPadding, mPadding, mWidth - mPadding, mWidth - mPadding); + } + +// private void initAttr(Context context, AttributeSet attrs, int defStyleAttr) { +// TypedArray a = context.getTheme() +// .obtainStyledAttributes(attrs, R.styleable.WrongDiaView, defStyleAttr, 0); +// for (int i = 0; i < a.getIndexCount(); i++) { +// int attr = a.getIndex(i); +// if (attr == R.styleable.RightDiaView_speed) { +// speed = a.getInt(attr, 1); +// } +// if (attr == R.styleable.RightDiaView_strokeColor) { +// color = a.getColor(attr, Color.WHITE); +// } +// } +// a.recycle(); +// } + + private void initPaint(Context context) { + mContext = context; + mPaint = new Paint(); + //抗锯齿 + mPaint.setAntiAlias(true); + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setColor(Color.WHITE); + mPaint.setStrokeWidth(8); + } + + int progress = 0; + + @Override + protected void onDraw(Canvas canvas) { + if (drawEveryTime) + drawDynamic(canvas); + else { + drawStatic(canvas); + if (listener != null) + listener.dispatchFinishEvent(this); + } + } + + private void drawDynamic(Canvas canvas) { + if (progress < 100) + progress += speed; + //根据进度画圆弧 + canvas.drawArc(rectF, 235, 360 * progress / 100, false, mPaint); + + int line1_start = 3 * mWidth / 10; + int line2_startX = 7 * mWidth / 10; + + + //绘制× + if (progress == 100) { + if ((line1_x + line1_start) <= line2_startX) { + line1_x += speed; + line1_y += speed; + } + //画第一根线 + canvas.drawLine(line1_start, line1_start, + line1_start + line1_x, line1_start + line1_y, mPaint); + + if (line1_x == 2 * mWidth / 5) { + line1_x++; + line1_y++; + } + + if (line1_x >= 2 * mWidth / 5 && (line2_startX - line2_y) >= line1_start) { + line2_x -= speed; + line2_y += speed; + } + //画第二根线 + canvas.drawLine(line2_startX, line1_start, + line2_startX + line2_x, line1_start + line2_y, mPaint); + + if ((line2_startX - line2_y) < line1_start) { + //1.只分发一次绘制完成的事件 + //2.只在最后一次绘制时分发 + if (count == 0 && times == 0 && listener != null) { + listener.dispatchFinishEvent(this); + count++; + } + times--; + if (times >= 0) { + reDraw(); + invalidate(); + } else { + return; + } + } + } + invalidate(); + } + + private void drawStatic(Canvas canvas) { + canvas.drawArc(rectF, 0, 360, false, mPaint); + + int line1_start = 3 * mWidth / 10; + int line2_startX = 7 * mWidth / 10; + + canvas.drawLine(line1_start, line1_start, + line1_start + 2 * mWidth / 5, line1_start + 2 * mWidth / 5, mPaint); + canvas.drawLine(line1_start + 2 * mWidth / 5, line1_start, + line1_start, line1_start + 2 * mWidth / 5, mPaint); + } + + private void reDraw() { + line1_x = 0; + line2_x = 0; + line1_y = 0; + line2_y = 0; + progress = 0; + } + + //---------------------------对外提供的api-------------------------// + + /** + * 设置重复绘制的次数,只在drawEveryTime = true时有效 + * + * @param times 重复次数,例如设置1,除了第一次绘制还会额外重绘一次 + */ + protected void setRepeatTime(int times) { + if (drawEveryTime) + this.times = times; + } + + /** + * 动态画出还是直接画出 + */ + protected void setDrawDynamic(boolean drawEveryTime) { + this.drawEveryTime = drawEveryTime; + } + + /** + * 设置速度 + */ + protected void setSpeed(int speed) { + if (speed <= 0 && speed >= 3) { + throw new IllegalArgumentException("how can u set this speed?? " + speed + " do not " + + "use reflect to use this method!u can see the LoadingDialog class for how to" + + "set the speed"); + } else { + this.speed = speed; + } + } + + protected void setDrawColor(int color) { + mPaint.setColor(color); + } + + public void setOnDrawFinishListener(FinishDrawListener f) { + this.listener = f; + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/loading/manager/StyleManager.java b/app/src/main/java/com/tairui/industrial_operation/widget/loading/manager/StyleManager.java new file mode 100644 index 0000000..c5f5f86 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/loading/manager/StyleManager.java @@ -0,0 +1,242 @@ +package com.tairui.industrial_operation.widget.loading.manager; + +import com.tairui.industrial_operation.widget.loading.LoadingDialog; + +/** + * Created by Luo_xiasuhuei321@163.com on 2016/11/12. + * desc:用于设置全局的loading样式 + */ + +public class StyleManager { + + public static StyleManager getDefault() { + return new StyleManager(true, 0, LoadingDialog.Speed.SPEED_TWO, -1, -1, 1000L, + true, "加载中...", "加载成功", "加载失败"); + } + + public StyleManager() { + } + + public StyleManager(boolean open, int repeatTime, LoadingDialog.Speed speed, + int contentSize, int textSize, long showTime, boolean interceptBack, + String loadText, String successText, String failedText) { + this.openAnim = open; + this.repeatTime = repeatTime; + this.speed = speed; + this.contentSize = contentSize; + this.textSize = textSize; + this.showTime = showTime; + this.interceptBack = interceptBack; + this.loadText = loadText; + this.successText = successText; + this.failedText = failedText; + + } + + public StyleManager(boolean open, int repeatTime, LoadingDialog.Speed speed, + int contentSize, int textSize, long showTime, boolean interceptBack, + String loadText, String successText, String failedText, int loadStyle) { + this.openAnim = open; + this.repeatTime = repeatTime; + this.speed = speed; + this.contentSize = contentSize; + this.textSize = textSize; + this.showTime = showTime; + this.interceptBack = interceptBack; + this.loadText = loadText; + this.successText = successText; + this.failedText = failedText; + this.loadStyle = loadStyle; + } + + /** + * 是否开启绘制 + */ + private boolean openAnim = true; + + /** + * 重绘次数 + */ + private int repeatTime; + + private LoadingDialog.Speed speed = LoadingDialog.Speed.SPEED_TWO; + + /** + * 反馈的尺寸,单位px + */ + private int contentSize = -1; + + /** + * 文字的尺寸,单位px + */ + private int textSize = -1; + + /** + * loading的反馈展示的时间,单位ms + */ + private long showTime = -1; + + private boolean interceptBack = true; + + private String loadText = "加载中..."; + + private String successText = "加载成功"; + + private String failedText = "加载失败"; + + private int loadStyle = LoadingDialog.STYLE_RING; + + public StyleManager setLoadStyle(int loadStyle) { + this.loadStyle = loadStyle; + return this; + } + + public int getLoadStyle() { + return loadStyle; + } + + public boolean isOpenAnim() { + return openAnim; + } + + public String getFailedText() { + return failedText; + } + + public String getSuccessText() { + return successText; + } + + public String getLoadText() { + return loadText; + } + + public boolean isInterceptBack() { + return interceptBack; + } + + public long getShowTime() { + return showTime; + } + + public int getTextSize() { + return textSize; + } + + public int getContentSize() { + return contentSize; + } + + public LoadingDialog.Speed getSpeed() { + return speed; + } + + public int getRepeatTime() { + return repeatTime; + } + + /** + * 是否开启动态绘制 + * + * @param openAnim true开启,false关闭 + * @return this + */ + public StyleManager Anim(boolean openAnim) { + this.openAnim = openAnim; + return this; + } + + /** + * 重复次数 + * + * @param times 次数 + * @return this + */ + public StyleManager repeatTime(int times) { + this.repeatTime = times; + return this; + } + + public StyleManager speed(LoadingDialog.Speed s) { + this.speed = s; + return this; + } + + /** + * 设置loading的大小 + * + * @param size 尺寸,单位px + * @return this + */ + public StyleManager contentSize(int size) { + this.contentSize = size; + return this; + } + + /** + * 设置loading 文字的大小 + * + * @param size 尺寸,单位px + * @return this + */ + public StyleManager textSize(int size) { + this.textSize = size; + return this; + } + + /** + * 设置展示的事件,如果开启绘制则从绘制完毕开始计算 + * + * @param showTime 事件 + * @return this + */ + public StyleManager showTime(long showTime) { + this.showTime = showTime; + return this; + } + + /** + * 设置是否拦截back,默认拦截 + * + * @param interceptBack true拦截,false不拦截 + * @return this + */ + public StyleManager intercept(boolean interceptBack) { + this.interceptBack = interceptBack; + return this; + } + + /** + * 设置loading时的文字 + * + * @param text 文字 + * @return this + */ + public StyleManager loadText(String text) { + this.loadText = text; + return this; + } + + /** + * 设置success时的文字 + * + * @param text 文字 + * @return this + */ + public StyleManager successText(String text) { + this.successText = text; + return this; + } + + /** + * 设置failed时的文字 + * + * @param text 文字 + * @return this + */ + public StyleManager failedText(String text) { + this.failedText = text; + return this; + } + +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/statusbar/OSUtils.java b/app/src/main/java/com/tairui/industrial_operation/widget/statusbar/OSUtils.java new file mode 100644 index 0000000..b01fa37 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/statusbar/OSUtils.java @@ -0,0 +1,123 @@ +package com.tairui.industrial_operation.widget.statusbar; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +import android.os.Build; +import android.text.TextUtils; + +/** + * Created by xiezonglin on 2018/12/20. + */ + +public class OSUtils { + + public static final String ROM_MIUI = "MIUI"; + public static final String ROM_EMUI = "EMUI"; + public static final String ROM_FLYME = "FLYME"; + public static final String ROM_OPPO = "OPPO"; + public static final String ROM_SMARTISAN = "SMARTISAN"; + public static final String ROM_VIVO = "VIVO"; + public static final String ROM_QIKU = "QIKU"; + + private static final String KEY_VERSION_MIUI = "ro.miui.ui.version.name"; + private static final String KEY_VERSION_EMUI = "ro.build.version.emui"; + private static final String KEY_VERSION_OPPO = "ro.build.version.opporom"; + private static final String KEY_VERSION_SMARTISAN = "ro.smartisan.version"; + private static final String KEY_VERSION_VIVO = "ro.vivo.os.version"; + + private static String sName; + private static String sVersion; + + public static boolean isEmui() { + return check(ROM_EMUI); + } + + public static boolean isMiui() { + return check(ROM_MIUI); + } + + public static boolean isVivo() { + return check(ROM_VIVO); + } + + public static boolean isOppo() { + return check(ROM_OPPO); + } + + public static boolean isFlyme() { + return check(ROM_FLYME); + } + + public static boolean is360() { + return check(ROM_QIKU) || check("360"); + } + + public static boolean isSmartisan() { + return check(ROM_SMARTISAN); + } + + public static String getName() { + if (sName == null) { + check(""); + } + return sName; + } + + public static String getVersion() { + if (sVersion == null) { + check(""); + } + return sVersion; + } + + public static boolean check(String rom) { + if (sName != null) { + return sName.equals(rom); + } + + if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_MIUI))) { + sName = ROM_MIUI; + } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_EMUI))) { + sName = ROM_EMUI; + } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_OPPO))) { + sName = ROM_OPPO; + } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_VIVO))) { + sName = ROM_VIVO; + } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_SMARTISAN))) { + sName = ROM_SMARTISAN; + } else { + sVersion = Build.DISPLAY; + if (sVersion.toUpperCase().contains(ROM_FLYME)) { + sName = ROM_FLYME; + } else { + sVersion = Build.UNKNOWN; + sName = Build.MANUFACTURER.toUpperCase(); + } + } + return sName.equals(rom); + } + + public static String getProp(String name) { + String line = null; + BufferedReader input = null; + try { + Process p = Runtime.getRuntime().exec("getprop " + name); + input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024); + line = input.readLine(); + input.close(); + } catch (IOException ex) { + return null; + } finally { + if (input != null) { + try { + input.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return line; + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/statusbar/StatusBarHeightView.java b/app/src/main/java/com/tairui/industrial_operation/widget/statusbar/StatusBarHeightView.java new file mode 100644 index 0000000..58e9a2a --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/statusbar/StatusBarHeightView.java @@ -0,0 +1,67 @@ +package com.tairui.industrial_operation.widget.statusbar; + +import com.tairui.industrial_operation.R; + +import android.content.Context; +import android.content.res.TypedArray; +import android.os.Build; +import android.util.AttributeSet; +import android.widget.LinearLayout; +import androidx.annotation.Nullable; + +/** + * Created by xiezonglin on 2018/12/20. + */ + +public class StatusBarHeightView extends LinearLayout { + private int statusBarHeight; + private int type; + + public StatusBarHeightView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(attrs); + } + + public StatusBarHeightView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(attrs); + } + + public StatusBarHeightView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr); + init(attrs); + + + } + + private void init(@Nullable AttributeSet attrs) { + + int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + if (resourceId > 0) { + statusBarHeight = getResources().getDimensionPixelSize(resourceId); + } + } else { + //低版本 直接设置0 + statusBarHeight = 0; + } + if (attrs != null) { + TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.StatusBarHeightView); + type = typedArray.getInt(R.styleable.StatusBarHeightView_use_type, 0); + typedArray.recycle(); + } + if (type == 1) { + setPadding(getPaddingLeft(), statusBarHeight, getPaddingRight(), getPaddingBottom()); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (type == 0) { + setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), + statusBarHeight); + } else { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/statusbar/StatusBarUtil.java b/app/src/main/java/com/tairui/industrial_operation/widget/statusbar/StatusBarUtil.java new file mode 100644 index 0000000..e8eae78 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/statusbar/StatusBarUtil.java @@ -0,0 +1,217 @@ +package com.tairui.industrial_operation.widget.statusbar; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; +import android.graphics.Color; +import android.os.Build; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import androidx.annotation.IntDef; + +/** + * Created by xiezonglin on 2018/12/20. + */ + +public class StatusBarUtil { + + public final static int TYPE_MIUI = 0; + public final static int TYPE_FLYME = 1; + public final static int TYPE_M = 3;//6.0 + + @IntDef({TYPE_MIUI, + TYPE_FLYME, + TYPE_M}) + @Retention(RetentionPolicy.SOURCE) + @interface ViewType { + } + + /** + * 修改状态栏颜色,支持4.4以上版本 + * + * @param colorId 颜色 + */ + public static void setStatusBarColor(Activity activity, int colorId) { + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Window window = activity.getWindow(); + window.setStatusBarColor(colorId); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + //使用SystemBarTintManager,需要先将状态栏设置为透明 + setTranslucentStatus(activity); + SystemBarTintManager systemBarTintManager = new SystemBarTintManager(activity); + systemBarTintManager.setStatusBarTintEnabled(true);//显示状态栏 + systemBarTintManager.setStatusBarTintColor(colorId);//设置状态栏颜色 + } + } + + /** + * 设置状态栏透明 + */ + @TargetApi(19) + public static void setTranslucentStatus(Activity activity) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + //5.x开始需要把颜色设置透明,否则导航栏会呈现系统默认的浅灰色 + Window window = activity.getWindow(); + View decorView = window.getDecorView(); + //两个 flag 要结合使用,表示让应用的主体内容占用系统状态栏的空间 + int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; + decorView.setSystemUiVisibility(option); + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + window.setStatusBarColor(Color.TRANSPARENT); + //导航栏颜色也可以正常设置 + //window.setNavigationBarColor(Color.TRANSPARENT); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + Window window = activity.getWindow(); + WindowManager.LayoutParams attributes = window.getAttributes(); + int flagTranslucentStatus = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; + attributes.flags |= flagTranslucentStatus; + //int flagTranslucentNavigation = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; + //attributes.flags |= flagTranslucentNavigation; + window.setAttributes(attributes); + } + } + + + /** + * 代码实现android:fitsSystemWindows + * + * @param activity + */ + public static void setRootViewFitsSystemWindows(Activity activity, boolean fitSystemWindows) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + ViewGroup winContent = (ViewGroup) activity.findViewById(android.R.id.content); + if (winContent.getChildCount() > 0) { + ViewGroup rootView = (ViewGroup) winContent.getChildAt(0); + if (rootView != null) { + rootView.setFitsSystemWindows(fitSystemWindows); + } + } + } + + } + + + /** + * 设置状态栏深色浅色切换 + */ + public static boolean setStatusBarDarkTheme(Activity activity, boolean dark) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + setStatusBarFontIconDark(activity, TYPE_M, dark); + } else if (OSUtils.isMiui()) { + setStatusBarFontIconDark(activity, TYPE_MIUI, dark); + } else if (OSUtils.isFlyme()) { + setStatusBarFontIconDark(activity, TYPE_FLYME, dark); + } else {//其他情况 + return false; + } + + return true; + } + return false; + } + + /** + * 设置 状态栏深色浅色切换 + */ + public static boolean setStatusBarFontIconDark(Activity activity, @ViewType int type, boolean dark) { + switch (type) { + case TYPE_MIUI: + return setMiuiUI(activity, dark); + case TYPE_FLYME: + return setFlymeUI(activity, dark); + case TYPE_M: + default: + return setCommonUI(activity, dark); + } + } + + //设置6.0 状态栏深色浅色切换 + public static boolean setCommonUI(Activity activity, boolean dark) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + View decorView = activity.getWindow().getDecorView(); + if (decorView != null) { + int vis = decorView.getSystemUiVisibility(); + if (dark) { + vis |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + } else { + vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + } + if (decorView.getSystemUiVisibility() != vis) { + decorView.setSystemUiVisibility(vis); + } + return true; + } + } + return false; + + } + + //设置Flyme 状态栏深色浅色切换 + public static boolean setFlymeUI(Activity activity, boolean dark) { + try { + Window window = activity.getWindow(); + WindowManager.LayoutParams lp = window.getAttributes(); + Field darkFlag = WindowManager.LayoutParams.class.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON"); + Field meizuFlags = WindowManager.LayoutParams.class.getDeclaredField("meizuFlags"); + darkFlag.setAccessible(true); + meizuFlags.setAccessible(true); + int bit = darkFlag.getInt(null); + int value = meizuFlags.getInt(lp); + if (dark) { + value |= bit; + } else { + value &= ~bit; + } + meizuFlags.setInt(lp, value); + window.setAttributes(lp); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + //设置MIUI 状态栏深色浅色切换 + public static boolean setMiuiUI(Activity activity, boolean dark) { + try { + Window window = activity.getWindow(); + Class clazz = activity.getWindow().getClass(); + @SuppressLint("PrivateApi") Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams"); + Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE"); + int darkModeFlag = field.getInt(layoutParams); + Method extraFlagField = clazz.getDeclaredMethod("setExtraFlags", int.class, int.class); + extraFlagField.setAccessible(true); + if (dark) { //状态栏亮色且黑色字体 + extraFlagField.invoke(window, darkModeFlag, darkModeFlag); + } else { + extraFlagField.invoke(window, 0, darkModeFlag); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + //获取状态栏高度 + public static int getStatusBarHeight(Context context) { + int result = 0; + int resourceId = context.getResources().getIdentifier( + "status_bar_height", "dimen", "android"); + if (resourceId > 0) { + result = context.getResources().getDimensionPixelSize(resourceId); + } + return result; + } +} diff --git a/app/src/main/java/com/tairui/industrial_operation/widget/statusbar/SystemBarTintManager.java b/app/src/main/java/com/tairui/industrial_operation/widget/statusbar/SystemBarTintManager.java new file mode 100644 index 0000000..3f80589 --- /dev/null +++ b/app/src/main/java/com/tairui/industrial_operation/widget/statusbar/SystemBarTintManager.java @@ -0,0 +1,548 @@ +package com.tairui.industrial_operation.widget.statusbar; + +import java.lang.reflect.Method; + +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.FrameLayout.LayoutParams; + +/** + * Created by xiezonglin on 2018/12/20. + */ + +public class SystemBarTintManager { + + static { + // Android allows a system property to override the presence of the navigation bar. + // Used by the emulator. + // See https://github.com/android/platform_frameworks_base/blob/master/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java#L1076 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + try { + Class c = Class.forName("android.os.SystemProperties"); + Method m = c.getDeclaredMethod("get", String.class); + m.setAccessible(true); + sNavBarOverride = (String) m.invoke(null, "qemu.hw.mainkeys"); + } catch (Throwable e) { + sNavBarOverride = null; + } + } + } + + + /** + * The default system bar tint color value. + */ + public static final int DEFAULT_TINT_COLOR = 0x99000000; + + private static String sNavBarOverride; + + private final SystemBarConfig mConfig; + private boolean mStatusBarAvailable; + private boolean mNavBarAvailable; + private boolean mStatusBarTintEnabled; + private boolean mNavBarTintEnabled; + private View mStatusBarTintView; + private View mNavBarTintView; + + /** + * Constructor. Call this in the host activity onCreate method after its + * content view has been set. You should always create new instances when + * the host activity is recreated. + * + * @param activity The host activity. + */ + @TargetApi(19) + public SystemBarTintManager(Activity activity) { + + Window win = activity.getWindow(); + ViewGroup decorViewGroup = (ViewGroup) win.getDecorView(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + // check theme attrs + int[] attrs = {android.R.attr.windowTranslucentStatus, + android.R.attr.windowTranslucentNavigation}; + TypedArray a = activity.obtainStyledAttributes(attrs); + try { + mStatusBarAvailable = a.getBoolean(0, false); + mNavBarAvailable = a.getBoolean(1, false); + } finally { + a.recycle(); + } + + // check window flags + WindowManager.LayoutParams winParams = win.getAttributes(); + int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; + if ((winParams.flags & bits) != 0) { + mStatusBarAvailable = true; + } + bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; + if ((winParams.flags & bits) != 0) { + mNavBarAvailable = true; + } + } + + mConfig = new SystemBarConfig(activity, mStatusBarAvailable, mNavBarAvailable); + // device might not have virtual navigation keys + if (!mConfig.hasNavigtionBar()) { + mNavBarAvailable = false; + } + + if (mStatusBarAvailable) { + setupStatusBarView(activity, decorViewGroup); + } + if (mNavBarAvailable) { + setupNavBarView(activity, decorViewGroup); + } + + } + + /** + * Enable tinting of the system status bar. + * + * If the platform is running Jelly Bean or earlier, or translucent system + * UI modes have not been enabled in either the theme or via window flags, + * then this method does nothing. + * + * @param enabled True to enable tinting, false to disable it (default). + */ + public void setStatusBarTintEnabled(boolean enabled) { + mStatusBarTintEnabled = enabled; + if (mStatusBarAvailable) { + mStatusBarTintView.setVisibility(enabled ? View.VISIBLE : View.GONE); + } + } + + /** + * Enable tinting of the system navigation bar. + * + * If the platform does not have soft navigation keys, is running Jelly Bean + * or earlier, or translucent system UI modes have not been enabled in either + * the theme or via window flags, then this method does nothing. + * + * @param enabled True to enable tinting, false to disable it (default). + */ + public void setNavigationBarTintEnabled(boolean enabled) { + mNavBarTintEnabled = enabled; + if (mNavBarAvailable) { + mNavBarTintView.setVisibility(enabled ? View.VISIBLE : View.GONE); + } + } + + /** + * Apply the specified color tint to all system UI bars. + * + * @param color The color of the background tint. + */ + public void setTintColor(int color) { + setStatusBarTintColor(color); + setNavigationBarTintColor(color); + } + + /** + * Apply the specified drawable or color resource to all system UI bars. + * + * @param res The identifier of the resource. + */ + public void setTintResource(int res) { + setStatusBarTintResource(res); + setNavigationBarTintResource(res); + } + + /** + * Apply the specified drawable to all system UI bars. + * + * @param drawable The drawable to use as the background, or null to remove it. + */ + public void setTintDrawable(Drawable drawable) { + setStatusBarTintDrawable(drawable); + setNavigationBarTintDrawable(drawable); + } + + /** + * Apply the specified alpha to all system UI bars. + * + * @param alpha The alpha to use + */ + public void setTintAlpha(float alpha) { + setStatusBarAlpha(alpha); + setNavigationBarAlpha(alpha); + } + + /** + * Apply the specified color tint to the system status bar. + * + * @param color The color of the background tint. + */ + public void setStatusBarTintColor(int color) { + if (mStatusBarAvailable) { + mStatusBarTintView.setBackgroundColor(color); + } + } + + /** + * Apply the specified drawable or color resource to the system status bar. + * + * @param res The identifier of the resource. + */ + public void setStatusBarTintResource(int res) { + if (mStatusBarAvailable) { + mStatusBarTintView.setBackgroundResource(res); + } + } + + /** + * Apply the specified drawable to the system status bar. + * + * @param drawable The drawable to use as the background, or null to remove it. + */ + @SuppressWarnings("deprecation") + public void setStatusBarTintDrawable(Drawable drawable) { + if (mStatusBarAvailable) { + mStatusBarTintView.setBackgroundDrawable(drawable); + } + } + + /** + * Apply the specified alpha to the system status bar. + * + * @param alpha The alpha to use + */ + @TargetApi(11) + public void setStatusBarAlpha(float alpha) { + if (mStatusBarAvailable && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + mStatusBarTintView.setAlpha(alpha); + } + } + + /** + * Apply the specified color tint to the system navigation bar. + * + * @param color The color of the background tint. + */ + public void setNavigationBarTintColor(int color) { + if (mNavBarAvailable) { + mNavBarTintView.setBackgroundColor(color); + } + } + + /** + * Apply the specified drawable or color resource to the system navigation bar. + * + * @param res The identifier of the resource. + */ + public void setNavigationBarTintResource(int res) { + if (mNavBarAvailable) { + mNavBarTintView.setBackgroundResource(res); + } + } + + /** + * Apply the specified drawable to the system navigation bar. + * + * @param drawable The drawable to use as the background, or null to remove it. + */ + @SuppressWarnings("deprecation") + public void setNavigationBarTintDrawable(Drawable drawable) { + if (mNavBarAvailable) { + mNavBarTintView.setBackgroundDrawable(drawable); + } + } + + /** + * Apply the specified alpha to the system navigation bar. + * + * @param alpha The alpha to use + */ + @TargetApi(11) + public void setNavigationBarAlpha(float alpha) { + if (mNavBarAvailable && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + mNavBarTintView.setAlpha(alpha); + } + } + + /** + * Get the system bar configuration. + * + * @return The system bar configuration for the current device configuration. + */ + public SystemBarConfig getConfig() { + return mConfig; + } + + /** + * Is tinting enabled for the system status bar? + * + * @return True if enabled, False otherwise. + */ + public boolean isStatusBarTintEnabled() { + return mStatusBarTintEnabled; + } + + /** + * Is tinting enabled for the system navigation bar? + * + * @return True if enabled, False otherwise. + */ + public boolean isNavBarTintEnabled() { + return mNavBarTintEnabled; + } + + private void setupStatusBarView(Context context, ViewGroup decorViewGroup) { + mStatusBarTintView = new View(context); + LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mConfig.getStatusBarHeight()); + params.gravity = Gravity.TOP; + if (mNavBarAvailable && !mConfig.isNavigationAtBottom()) { + params.rightMargin = mConfig.getNavigationBarWidth(); + } + mStatusBarTintView.setLayoutParams(params); + mStatusBarTintView.setBackgroundColor(DEFAULT_TINT_COLOR); + mStatusBarTintView.setVisibility(View.GONE); + decorViewGroup.addView(mStatusBarTintView); + } + + private void setupNavBarView(Context context, ViewGroup decorViewGroup) { + mNavBarTintView = new View(context); + LayoutParams params; + if (mConfig.isNavigationAtBottom()) { + params = new LayoutParams(LayoutParams.MATCH_PARENT, mConfig.getNavigationBarHeight()); + params.gravity = Gravity.BOTTOM; + } else { + params = new LayoutParams(mConfig.getNavigationBarWidth(), LayoutParams.MATCH_PARENT); + params.gravity = Gravity.RIGHT; + } + mNavBarTintView.setLayoutParams(params); + mNavBarTintView.setBackgroundColor(DEFAULT_TINT_COLOR); + mNavBarTintView.setVisibility(View.GONE); + decorViewGroup.addView(mNavBarTintView); + } + + /** + * Class which describes system bar sizing and other characteristics for the current + * device configuration. + * + */ + public static class SystemBarConfig { + + private static final String STATUS_BAR_HEIGHT_RES_NAME = "status_bar_height"; + private static final String NAV_BAR_HEIGHT_RES_NAME = "navigation_bar_height"; + private static final String NAV_BAR_HEIGHT_LANDSCAPE_RES_NAME = "navigation_bar_height_landscape"; + private static final String NAV_BAR_WIDTH_RES_NAME = "navigation_bar_width"; + private static final String SHOW_NAV_BAR_RES_NAME = "config_showNavigationBar"; + + private final boolean mTranslucentStatusBar; + private final boolean mTranslucentNavBar; + private final int mStatusBarHeight; + private final int mActionBarHeight; + private final boolean mHasNavigationBar; + private final int mNavigationBarHeight; + private final int mNavigationBarWidth; + private final boolean mInPortrait; + private final float mSmallestWidthDp; + + private SystemBarConfig(Activity activity, boolean translucentStatusBar, boolean traslucentNavBar) { + Resources res = activity.getResources(); + mInPortrait = (res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT); + mSmallestWidthDp = getSmallestWidthDp(activity); + mStatusBarHeight = getInternalDimensionSize(res, STATUS_BAR_HEIGHT_RES_NAME); + mActionBarHeight = getActionBarHeight(activity); + mNavigationBarHeight = getNavigationBarHeight(activity); + mNavigationBarWidth = getNavigationBarWidth(activity); + mHasNavigationBar = (mNavigationBarHeight > 0); + mTranslucentStatusBar = translucentStatusBar; + mTranslucentNavBar = traslucentNavBar; + } + + @TargetApi(14) + private int getActionBarHeight(Context context) { + int result = 0; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + TypedValue tv = new TypedValue(); + context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true); + result = TypedValue.complexToDimensionPixelSize(tv.data, context.getResources().getDisplayMetrics()); + } + return result; + } + + @TargetApi(14) + private int getNavigationBarHeight(Context context) { + Resources res = context.getResources(); + int result = 0; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + if (hasNavBar(context)) { + String key; + if (mInPortrait) { + key = NAV_BAR_HEIGHT_RES_NAME; + } else { + key = NAV_BAR_HEIGHT_LANDSCAPE_RES_NAME; + } + return getInternalDimensionSize(res, key); + } + } + return result; + } + + @TargetApi(14) + private int getNavigationBarWidth(Context context) { + Resources res = context.getResources(); + int result = 0; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + if (hasNavBar(context)) { + return getInternalDimensionSize(res, NAV_BAR_WIDTH_RES_NAME); + } + } + return result; + } + + @TargetApi(14) + private boolean hasNavBar(Context context) { + Resources res = context.getResources(); + int resourceId = res.getIdentifier(SHOW_NAV_BAR_RES_NAME, "bool", "android"); + if (resourceId != 0) { + boolean hasNav = res.getBoolean(resourceId); + // check override flag (see static block) + if ("1".equals(sNavBarOverride)) { + hasNav = false; + } else if ("0".equals(sNavBarOverride)) { + hasNav = true; + } + return hasNav; + } else { // fallback + return !ViewConfiguration.get(context).hasPermanentMenuKey(); + } + } + + private int getInternalDimensionSize(Resources res, String key) { + int result = 0; + int resourceId = res.getIdentifier(key, "dimen", "android"); + if (resourceId > 0) { + result = res.getDimensionPixelSize(resourceId); + } + return result; + } + + @SuppressLint("NewApi") + private float getSmallestWidthDp(Activity activity) { + DisplayMetrics metrics = new DisplayMetrics(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + activity.getWindowManager().getDefaultDisplay().getRealMetrics(metrics); + } else { + // this is not correct, but we don't really care pre-kitkat + activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); + } + float widthDp = metrics.widthPixels / metrics.density; + float heightDp = metrics.heightPixels / metrics.density; + return Math.min(widthDp, heightDp); + } + + /** + * Should a navigation bar appear at the bottom of the screen in the current + * device configuration? A navigation bar may appear on the right side of + * the screen in certain configurations. + * + * @return True if navigation should appear at the bottom of the screen, False otherwise. + */ + public boolean isNavigationAtBottom() { + return (mSmallestWidthDp >= 600 || mInPortrait); + } + + /** + * Get the height of the system status bar. + * + * @return The height of the status bar (in pixels). + */ + public int getStatusBarHeight() { + return mStatusBarHeight; + } + + /** + * Get the height of the action bar. + * + * @return The height of the action bar (in pixels). + */ + public int getActionBarHeight() { + return mActionBarHeight; + } + + /** + * Does this device have a system navigation bar? + * + * @return True if this device uses soft key navigation, False otherwise. + */ + public boolean hasNavigtionBar() { + return mHasNavigationBar; + } + + /** + * Get the height of the system navigation bar. + * + * @return The height of the navigation bar (in pixels). If the device does not have + * soft navigation keys, this will always return 0. + */ + public int getNavigationBarHeight() { + return mNavigationBarHeight; + } + + /** + * Get the width of the system navigation bar when it is placed vertically on the screen. + * + * @return The width of the navigation bar (in pixels). If the device does not have + * soft navigation keys, this will always return 0. + */ + public int getNavigationBarWidth() { + return mNavigationBarWidth; + } + + /** + * Get the layout inset for any system UI that appears at the top of the screen. + * + * @param withActionBar True to include the height of the action bar, False otherwise. + * @return The layout inset (in pixels). + */ + public int getPixelInsetTop(boolean withActionBar) { + return (mTranslucentStatusBar ? mStatusBarHeight : 0) + (withActionBar ? mActionBarHeight : 0); + } + + /** + * Get the layout inset for any system UI that appears at the bottom of the screen. + * + * @return The layout inset (in pixels). + */ + public int getPixelInsetBottom() { + if (mTranslucentNavBar && isNavigationAtBottom()) { + return mNavigationBarHeight; + } else { + return 0; + } + } + + /** + * Get the layout inset for any system UI that appears at the right of the screen. + * + * @return The layout inset (in pixels). + */ + public int getPixelInsetRight() { + if (mTranslucentNavBar && !isNavigationAtBottom()) { + return mNavigationBarWidth; + } else { + return 0; + } + } + + } + +} diff --git a/app/src/main/res/drawable/bg_checkbox.xml b/app/src/main/res/drawable/bg_checkbox.xml new file mode 100644 index 0000000..65055a4 --- /dev/null +++ b/app/src/main/res/drawable/bg_checkbox.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_container_blue_raduis_30.xml b/app/src/main/res/drawable/bg_container_blue_raduis_30.xml new file mode 100644 index 0000000..e6a7b24 --- /dev/null +++ b/app/src/main/res/drawable/bg_container_blue_raduis_30.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_container_f5_green_border_raduis_10.xml b/app/src/main/res/drawable/bg_container_f5_green_border_raduis_10.xml new file mode 100644 index 0000000..5590059 --- /dev/null +++ b/app/src/main/res/drawable/bg_container_f5_green_border_raduis_10.xml @@ -0,0 +1,15 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_container_f5_raduis_10.xml b/app/src/main/res/drawable/bg_container_f5_raduis_10.xml new file mode 100644 index 0000000..27cbd0d --- /dev/null +++ b/app/src/main/res/drawable/bg_container_f5_raduis_10.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_container_green_disable_raduis_30.xml b/app/src/main/res/drawable/bg_container_green_disable_raduis_30.xml new file mode 100644 index 0000000..be14f51 --- /dev/null +++ b/app/src/main/res/drawable/bg_container_green_disable_raduis_30.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_container_green_highlight_raduis_30.xml b/app/src/main/res/drawable/bg_container_green_highlight_raduis_30.xml new file mode 100644 index 0000000..b13274e --- /dev/null +++ b/app/src/main/res/drawable/bg_container_green_highlight_raduis_30.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_container_green_raduis_30.xml b/app/src/main/res/drawable/bg_container_green_raduis_30.xml new file mode 100644 index 0000000..d3b7814 --- /dev/null +++ b/app/src/main/res/drawable/bg_container_green_raduis_30.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_container_home_raduis.xml b/app/src/main/res/drawable/bg_container_home_raduis.xml new file mode 100644 index 0000000..03de444 --- /dev/null +++ b/app/src/main/res/drawable/bg_container_home_raduis.xml @@ -0,0 +1,16 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_container_light_gray_gradient_10.xml b/app/src/main/res/drawable/bg_container_light_gray_gradient_10.xml new file mode 100644 index 0000000..4b50d6d --- /dev/null +++ b/app/src/main/res/drawable/bg_container_light_gray_gradient_10.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_container_light_gray_raduis_10.xml b/app/src/main/res/drawable/bg_container_light_gray_raduis_10.xml new file mode 100644 index 0000000..05253c3 --- /dev/null +++ b/app/src/main/res/drawable/bg_container_light_gray_raduis_10.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_container_toast.xml b/app/src/main/res/drawable/bg_container_toast.xml new file mode 100644 index 0000000..41de98c --- /dev/null +++ b/app/src/main/res/drawable/bg_container_toast.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_container_white_bottom_raduis_10.xml b/app/src/main/res/drawable/bg_container_white_bottom_raduis_10.xml new file mode 100644 index 0000000..7207205 --- /dev/null +++ b/app/src/main/res/drawable/bg_container_white_bottom_raduis_10.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_container_white_raduis_10.xml b/app/src/main/res/drawable/bg_container_white_raduis_10.xml new file mode 100644 index 0000000..05337be --- /dev/null +++ b/app/src/main/res/drawable/bg_container_white_raduis_10.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_container_white_raduis_10_border.xml b/app/src/main/res/drawable/bg_container_white_raduis_10_border.xml new file mode 100644 index 0000000..38354bd --- /dev/null +++ b/app/src/main/res/drawable/bg_container_white_raduis_10_border.xml @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_container_white_raduis_15.xml b/app/src/main/res/drawable/bg_container_white_raduis_15.xml new file mode 100644 index 0000000..3b91be5 --- /dev/null +++ b/app/src/main/res/drawable/bg_container_white_raduis_15.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_container_white_raduis_30.xml b/app/src/main/res/drawable/bg_container_white_raduis_30.xml new file mode 100644 index 0000000..533f9ca --- /dev/null +++ b/app/src/main/res/drawable/bg_container_white_raduis_30.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_container_white_raduis_5.xml b/app/src/main/res/drawable/bg_container_white_raduis_5.xml new file mode 100644 index 0000000..97e0ca3 --- /dev/null +++ b/app/src/main/res/drawable/bg_container_white_raduis_5.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_gradient_home.xml b/app/src/main/res/drawable/bg_gradient_home.xml new file mode 100644 index 0000000..c078d60 --- /dev/null +++ b/app/src/main/res/drawable/bg_gradient_home.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_gradient_smart_farm.xml b/app/src/main/res/drawable/bg_gradient_smart_farm.xml new file mode 100644 index 0000000..f3092f7 --- /dev/null +++ b/app/src/main/res/drawable/bg_gradient_smart_farm.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/checkbox_checked.xml b/app/src/main/res/drawable/checkbox_checked.xml new file mode 100644 index 0000000..59cb530 --- /dev/null +++ b/app/src/main/res/drawable/checkbox_checked.xml @@ -0,0 +1,4 @@ + + + diff --git a/app/src/main/res/drawable/checkbox_unchecked.xml b/app/src/main/res/drawable/checkbox_unchecked.xml new file mode 100644 index 0000000..db3dadc --- /dev/null +++ b/app/src/main/res/drawable/checkbox_unchecked.xml @@ -0,0 +1,4 @@ + + + diff --git a/app/src/main/res/drawable/container_login_btn_selector.xml b/app/src/main/res/drawable/container_login_btn_selector.xml new file mode 100644 index 0000000..e26148b --- /dev/null +++ b/app/src/main/res/drawable/container_login_btn_selector.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/container_timer_normal.xml b/app/src/main/res/drawable/container_timer_normal.xml new file mode 100644 index 0000000..2fc73ed --- /dev/null +++ b/app/src/main/res/drawable/container_timer_normal.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/container_timer_press.xml b/app/src/main/res/drawable/container_timer_press.xml new file mode 100644 index 0000000..ebe4479 --- /dev/null +++ b/app/src/main/res/drawable/container_timer_press.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/container_timer_selector.xml b/app/src/main/res/drawable/container_timer_selector.xml new file mode 100644 index 0000000..f8a2e0f --- /dev/null +++ b/app/src/main/res/drawable/container_timer_selector.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/dialog_bg.xml b/app/src/main/res/drawable/dialog_bg.xml new file mode 100644 index 0000000..cd21b0f --- /dev/null +++ b/app/src/main/res/drawable/dialog_bg.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_arrow_right_black.png b/app/src/main/res/drawable/ic_arrow_right_black.png new file mode 100644 index 0000000..d894963 Binary files /dev/null and b/app/src/main/res/drawable/ic_arrow_right_black.png differ diff --git a/app/src/main/res/drawable/vector_drawable_arrow_right_black.xml b/app/src/main/res/drawable/vector_drawable_arrow_right_black.xml new file mode 100644 index 0000000..ac469d4 --- /dev/null +++ b/app/src/main/res/drawable/vector_drawable_arrow_right_black.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/vector_drawable_back_black.xml b/app/src/main/res/drawable/vector_drawable_back_black.xml new file mode 100644 index 0000000..22f150b --- /dev/null +++ b/app/src/main/res/drawable/vector_drawable_back_black.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/vector_drawable_back_white.xml b/app/src/main/res/drawable/vector_drawable_back_white.xml new file mode 100644 index 0000000..cbb94ae --- /dev/null +++ b/app/src/main/res/drawable/vector_drawable_back_white.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_field_monitoring.xml b/app/src/main/res/layout/activity_field_monitoring.xml new file mode 100644 index 0000000..e71484b --- /dev/null +++ b/app/src/main/res/layout/activity_field_monitoring.xml @@ -0,0 +1,323 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000..a5a00c2 --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + +