diff --git a/API.md b/API.md index f73cf8c1..20933ec4 100644 --- a/API.md +++ b/API.md @@ -10,11 +10,7 @@ > startActivity 方法优化 - public void startActivity(Class cls) - - public void startActivityFinish(Class cls) - - public void startActivityFinish(Intent intent) + public void startActivity(Class clazz) > startActivityForResult 方法优化 @@ -89,7 +85,7 @@ > 跳转到其他Activity - public void startActivity(Class cls) + public void startActivity(Class clazz) > 销毁当前 Fragment 所在的 Activity diff --git a/AndroidBug.md b/AndroidBug.md index 4a647e83..8fb2074b 100644 --- a/AndroidBug.md +++ b/AndroidBug.md @@ -68,7 +68,7 @@ #### 修复 Android 9.0 限制 Http 明文请求的问题 > Android P 限制了明文流量的网络请求,非加密的流量请求都会被系统禁止掉。 -如果当前应用的请求是 htttp 请求,而非 https ,这样就会导系统禁止当前应用进行该请求,如果 WebView 的 url 用 http 协议,同样会出现加载失败,https 不受影响 +如果当前应用的请求是 http 请求,而非 https ,这样就会导系统禁止当前应用进行该请求,如果 WebView 的 url 用 http 协议,同样会出现加载失败,https 不受影响 > 在 res 下新建一个 xml 目录,然后创建一个名为:network_security_config.xml 文件 ,该文件内容如下 diff --git a/AndroidProject.apk b/AndroidProject.apk index 546a4aea..d1ca696f 100644 Binary files a/AndroidProject.apk and b/AndroidProject.apk differ diff --git a/README.md b/README.md index a92ede28..be0d38c9 100644 --- a/README.md +++ b/README.md @@ -12,31 +12,41 @@ #### 常用界面 -![](picture/activity/1.png) ![](picture/activity/2.png) ![](picture/activity/3.png) +![](picture/activity/1.jpg) ![](picture/activity/2.jpg) ![](picture/activity/3.jpg) -![](picture/activity/4.png) ![](picture/activity/5.png) ![](picture/activity/6.png) +![](picture/activity/4.jpg) ![](picture/activity/5.jpg) ![](picture/activity/6.jpg) -![](picture/activity/7.png) ![](picture/activity/8.png) ![](picture/activity/9.png) +![](picture/activity/7.jpg) ![](picture/activity/8.jpg) ![](picture/activity/9.jpg) -![](picture/activity/11.png) ![](picture/activity/12.png) ![](picture/activity/13.png) +![](picture/activity/10.jpg) ![](picture/activity/11.jpg) ![](picture/activity/12.jpg) -![](picture/activity/14.png) ![](picture/activity/15.png) ![](picture/activity/16.png) +![](picture/activity/13.jpg) ![](picture/activity/14.jpg) ![](picture/activity/15.jpg) -![](picture/activity/17.png) ![](picture/activity/18.png) ![](picture/activity/19.png) +![](picture/activity/16.jpg) ![](picture/activity/17.jpg) ![](picture/activity/18.jpg) -![](picture/activity/20.png) ![](picture/activity/21.png) ![](picture/activity/22.png) +![](picture/activity/19.jpg) ![](picture/activity/20.jpg) ![](picture/activity/21.jpg) + +![](picture/activity/22.jpg) ![](picture/activity/23.jpg) ![](picture/activity/24.jpg) + +![](picture/activity/25.jpg) ![](picture/activity/26.jpg) ![](picture/activity/27.jpg) + +![](picture/activity/28.jpg) ![](picture/activity/29.jpg) ![](picture/activity/30.jpg) + +![](picture/activity/31.jpg) #### 常用对话框 -![](picture/dialog/1.png) ![](picture/dialog/2.png) ![](picture/dialog/3.png) +![](picture/dialog/1.jpg) ![](picture/dialog/2.jpg) ![](picture/dialog/3.jpg) -![](picture/dialog/4.png) ![](picture/dialog/5.png) ![](picture/dialog/6.png) +![](picture/dialog/4.jpg) ![](picture/dialog/5.jpg) ![](picture/dialog/6.jpg) -![](picture/dialog/7.png) ![](picture/dialog/8.png) ![](picture/dialog/9.png) +![](picture/dialog/7.jpg) ![](picture/dialog/8.jpg) ![](picture/dialog/9.jpg) -![](picture/dialog/10.png) ![](picture/dialog/11.png) ![](picture/dialog/12.png) +![](picture/dialog/10.jpg) ![](picture/dialog/11.jpg) ![](picture/dialog/12.jpg) -![](picture/dialog/13.png) ![](picture/dialog/14.png) ![](picture/dialog/15.png) +![](picture/dialog/13.jpg) ![](picture/dialog/14.jpg) ![](picture/dialog/15.jpg) + +![](picture/dialog/16.jpg) ![](picture/dialog/17.jpg) ![](picture/dialog/18.jpg) #### 动图欣赏 @@ -46,29 +56,27 @@ #### 集成框架 -* 权限请求框架:[https://github.com/getActivity/XXPermissions](https://github.com/getActivity/XXPermissions) - -* 标题栏:[https://github.com/getActivity/TitleBar](https://github.com/getActivity/TitleBar) - -* 吐司工具类:[https://github.com/getActivity/ToastUtils](https://github.com/getActivity/ToastUtils) - * 状态栏沉浸:[https://github.com/gyf-dev/ImmersionBar](https://github.com/gyf-dev/ImmersionBar) -* 缩放 ImageView:[https://github.com/chrisbanes/PhotoView](https://github.com/chrisbanes/PhotoView) +* 手势 ImageView:[https://github.com/chrisbanes/PhotoView](https://github.com/chrisbanes/PhotoView) * ViewPager 指示器:[https://github.com/romandanylyk/PageIndicatorView](https://github.com/romandanylyk/PageIndicatorView) -* ButterKnife 注解:[https://github.com/JakeWharton/butterknife](https://github.com/JakeWharton/butterknife) - -* EventBus 事件:[https://github.com/greenrobot/EventBus](https://github.com/greenrobot/EventBus) +* View 注解:[https://github.com/JakeWharton/butterknife](https://github.com/JakeWharton/butterknife) * 内存泄漏捕捉:[https://github.com/square/leakcanary](https://github.com/square/leakcanary) * 本地异常捕捉:[https://github.com/Ereza/CustomActivityOnCrash](https://github.com/Ereza/CustomActivityOnCrash) +* 动画解析库:[https://github.com/airbnb/lottie-android](https://github.com/airbnb/lottie-android) + +* 上拉刷新下拉加载:[https://github.com/scwang90/SmartRefreshLayout](https://github.com/scwang90/SmartRefreshLayout) + +* 侧滑框架:[https://github.com/luckybilly/SmartSwipe](https://github.com/luckybilly/SmartSwipe) + #### 模板项目亮点,[查看详细](ProjectDetails.md) -* 必备优秀框架:危险权限处理,标题栏控件,吐司工具类,圆形ImageView +* 必备优秀框架:危险权限处理,标题栏控件,吐司工具类,圆形 ImageView * 常用页面模板:启动界面,主页界面,登录界面,注册界面,关于界面,浏览器界面 @@ -96,15 +104,19 @@ #### 作者的其他开源项目 -* 权限框架:[XXPermissions](https://github.com/getActivity/XXPermissions) +* 网络框架:[EasyHttp](https://github.com/getActivity/EasyHttp) (已集成) + +* 日志框架:[Logcat](https://github.com/getActivity/Logcat) (已集成) + +* 权限框架:[XXPermissions](https://github.com/getActivity/XXPermissions) (已集成) -* 吐司框架:[ToastUtils](https://github.com/getActivity/ToastUtils) +* 吐司框架:[ToastUtils](https://github.com/getActivity/ToastUtils) (已集成) -* 国际化框架:[MultiLanguages](https://github.com/getActivity/MultiLanguages) +* 标题栏框架:[TitleBar](https://github.com/getActivity/TitleBar) (已集成) -* 标题栏框架:[TitleBar](https://github.com/getActivity/TitleBar) +* 悬浮窗框架:[XToast](https://github.com/getActivity/XToast) (未集成) -* 悬浮窗框架:[XToast](https://github.com/getActivity/XToast) +* 国际化框架:[MultiLanguages](https://github.com/getActivity/MultiLanguages) (未集成) #### Android技术讨论Q群:78797078 @@ -130,4 +142,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -``` +``` \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 82253098..b2db700b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,22 +1,13 @@ apply plugin: 'com.android.application' +apply plugin: 'com.xuexiang.xaop' +apply from: "../config.gradle" android { - compileSdkVersion rootProject.ext.compileVersion - - // 使用 JDK 1.8 - compileOptions { - targetCompatibility JavaVersion.VERSION_1_8 - sourceCompatibility JavaVersion.VERSION_1_8 - } defaultConfig { + // 无痛修改包名:https://www.jianshu.com/p/17327e191d2e applicationId "com.hjq.demo" - minSdkVersion 14 - targetSdkVersion rootProject.ext.targetVersion - versionCode 10 - versionName "1.0" - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" // 仅保留中文语种的资源 resConfig 'zh' @@ -24,37 +15,28 @@ android { // 仅保留 xxhdpi 图片资源(目前主流分辨率 1920 * 1080) resConfig 'xxhdpi' - // 仅保留两种架构的 so 库 + // 仅保留两种架构的 so 库,根据 Bugly 统计得出 ndk { - // armeabi:已经淘汰(0%) - // armeabi-v7a:曾经主流的架构平台(20%) - // arm64-v8a:目前主流架构平台(80%) + // armeabi:万金油架构平台(占用率:0%) + // armeabi-v7a:曾经主流的架构平台(占用率:10%) + // arm64-v8a:目前主流架构平台(占用率:90%) abiFilters "armeabi-v7a", "arm64-v8a" } // 开启 Dex 分包 //multiDexEnabled true + // 使用矢量图支持库(为了兼容 API 21 以下) + // 阿里巴巴矢量图库:https://www.iconfont.cn/ + vectorDrawables.useSupportLibrary = true + // 混淆配置 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-app.pro' - - javaCompileOptions { - annotationProcessorOptions { - // EventBus Apt 索引类生成位置 - arguments = [ eventBusIndex : applicationId + '.MyEventBusIndex' ] - } - } } // APK 签名的那些事:https://www.jianshu.com/p/a1f8e5896aa2 signingConfigs { - debug { - storeFile file(StoreFile) - storePassword StorePassword - keyAlias KeyAlias - keyPassword KeyPassword - } - release { + config { storeFile file(StoreFile) storePassword StorePassword keyAlias KeyAlias @@ -63,51 +45,47 @@ android { } buildTypes { - release { - // 移除无用的资源文件 - shrinkResources true - // ZipAlign 优化 - zipAlignEnabled true - // 设置混淆 - minifyEnabled true - // 正式环境签名 - signingConfig signingConfigs.release - // 正式环境下的 BuglyId - buildConfigField "String", "BUGLY_ID", "\"请自行替换 Bugly 上面的 AppID\"" - } debug { + debuggable true + jniDebuggable true // 移除无用的资源文件 shrinkResources false // ZipAlign 优化 zipAlignEnabled false // 设置混淆 minifyEnabled false - // 开发环境签名 - signingConfig signingConfigs.debug // 开发环境下的 BuglyId buildConfigField "String", "BUGLY_ID", "\"请自行替换 Bugly 上面的 AppID\"" + signingConfig signingConfigs.config + } + + release { + debuggable false + jniDebuggable false + // 移除无用的资源文件 + shrinkResources true + // ZipAlign 优化 + zipAlignEnabled true + // 设置混淆 + minifyEnabled true + // 正式环境下的 BuglyId + buildConfigField "String", "BUGLY_ID", "\"请自行替换 Bugly 上面的 AppID\"" + signingConfig signingConfigs.config } } // 默认渠道名 flavorDimensions "default" // 友盟多渠道打包 - productFlavors { + android.productFlavors { tencent {} // 应用宝 baidu {} // 百度 xiaomi {} // 小米 huawei {} // 华为 productFlavors.all { flavor -> - flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name] - } - } - - // JNI 目录 - sourceSets { - main { - jniLibs.srcDirs = ['libs'] + flavor.manifestPlaceholders = [channel: name] } } @@ -116,97 +94,107 @@ android { // Apk 输出配置 variant.outputs.all { output -> - def appName = "AndroidProject" - if (variant.buildType.name == 'debug') { - outputFileName = appName + '_v' + versionName + '_' + variant.buildType.name + '.apk' - } else { - outputFileName = appName + '_v' + versionName + '_' + new Date().format("yyyyMMdd") + '_' + variant.productFlavors[0].name + '_' + variant.buildType.name + '.apk' + outputFileName = rootProject.getName() + '_v' + variant.versionName + '_' + variant.buildType.name + if (variant.buildType.name == buildTypes.release.getName()) { + outputFileName += '_' + new Date().format("yyyyMMdd") } - } - - // AndroidManifest 输出配置 - variant.outputs[0].processManifest.doLast { - def manifestFile = "${manifestOutputDirectory}/AndroidManifest.xml" - def updatedContent = new File(manifestFile).getText('UTF-8') - .replaceAll("UMENG_APPKEY_VALUE", "5cb16d93570df399fd0014e2") // 友盟 AppKey - .replaceAll("QQ_APPID_VALUE", "100424468") // QQ AppId - .replaceAll("QQ_APPKEY_VALUE", "c7394704798a158208a74ab60104f0ba") // QQ Key - .replaceAll("WX_APPID_VALUE", "wxdc1e388c3822c80b") // 微信 AppId - .replaceAll("WX_APPKEY_VALUE", "3baf1193c85774b3fd9d18447d76cab0") // 微信 Key - new File(manifestFile).write(updatedContent, 'UTF-8') + outputFileName += '.apk' } } } // api 与 implementation 的区别:https://www.jianshu.com/p/8962d6ba936e dependencies { - // 依赖 libs 目录下所有 jar 包 - implementation fileTree(include: ['*.jar'], dir: 'libs') - // 依赖 libs 目录下所有 aar 包 - implementation fileTree(include: ['*.aar'], dir: 'libs') // 基础库(不包任何第三方框架) implementation project(':base') // 自定义 View implementation project(':widget') - // Glide 隔离 - implementation project(':image') // 友盟隔离 implementation project(':umeng') - // 谷歌 Support 包 - implementation "androidx.appcompat:appcompat:$rootProject.ext.appcompatVersion" - implementation "com.google.android.material:material:$rootProject.ext.materialVersion" - // Dex 分包,解决 64k 方法问题 //implementation 'androidx.multidex:multidex:2.0.1' - // ButterKnife 注解库:https://github.com/JakeWharton/butterknife - implementation 'com.jakewharton:butterknife:10.1.0' - annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0' - - // EventBus 事件总线:https://github.com/greenrobot/EventBus - implementation "org.greenrobot:eventbus:3.1.1" - annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1' - - // 状态栏沉浸:https://github.com/gyf-dev/ImmersionBar - implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + // 网络请求框架:https://github.com/getActivity/EasyHttp + implementation 'com.hjq:http:5.0' // 权限请求框架:https://github.com/getActivity/XXPermissions - implementation 'com.hjq:xxpermissions:6.0' + implementation 'com.hjq:xxpermissions:6.2' - // 标题栏:https://github.com/getActivity/TitleBar + // 标题栏框架:https://github.com/getActivity/TitleBar implementation 'com.hjq:titlebar:6.0' - // 吐司工具类:https://github.com/getActivity/ToastUtils + // 吐司框架:https://github.com/getActivity/ToastUtils implementation 'com.hjq:toast:8.0' - // 支持放大缩放的 ImageView:https://github.com/chrisbanes/PhotoView + // OkHttp 框架:https://github.com/square/okhttp + implementation 'com.squareup.okhttp3:okhttp:4.2.2' + + // JSON 解析框架:https://github.com/google/gson + implementation 'com.google.code.gson:gson:2.8.6' + + // AOP 插件库:https://mvnrepository.com/artifact/org.aspectj/aspectjrt + implementation 'org.aspectj:aspectjrt:1.9.5' + + // ButterKnife 注解库:https://github.com/JakeWharton/butterknife + if (project.hasProperty('android.injected.invoked.from.ide')) { + // 开发时使用运行时注解,提升 IDE 编译速度 + implementation 'com.jakewharton:butterknife-reflect:10.2.0' + } else { + // 打包时使用编译时注解,提升 APP 运行速度 + implementation 'com.jakewharton:butterknife:10.2.0' + annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.0' + } + + // 图片加载框架:https://github.com/bumptech/glide + implementation 'com.github.bumptech.glide:glide:4.10.0' + annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0' + + // 状态栏沉浸:https://github.com/gyf-dev/ImmersionBar + implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + + // 手势 ImageView:https://github.com/chrisbanes/PhotoView implementation 'com.github.chrisbanes:PhotoView:2.3.0' // ViewPager 指示器:https://github.com/romandanylyk/PageIndicatorView implementation 'com.romandanylyk:pageindicatorview:1.0.3' // Bugly 异常捕捉:https://bugly.qq.com/docs/user-guide/instruction-manual-android/?v=20190418140644 - implementation 'com.tencent.bugly:crashreport:3.0.1' + implementation 'com.tencent.bugly:crashreport:3.1.0' implementation 'com.tencent.bugly:nativecrashreport:3.7.1' // 本地异常捕捉框架:https://github.com/Ereza/CustomActivityOnCrash implementation 'cat.ereza:customactivityoncrash:2.2.0' - // 内存泄漏捕捉:https://github.com/square/leakcanary - debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4' - releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4' + // 动画解析库:https://github.com/airbnb/lottie-android + // 动画资源:https://lottiefiles.com/ + implementation 'com.airbnb.android:lottie:3.2.0' + + // 上拉刷新下拉加载:https://github.com/scwang90/SmartRefreshLayout + implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0' + // SmartRefreshLayout 刷新样式集合库 + implementation 'com.scwang.smartrefresh:SmartRefreshHeader:1.1.0' - // 网络请求(待发布):https://github.com/getActivity/EasyHttp + // 侧滑框架:https://github.com/luckybilly/SmartSwipe + implementation 'com.billy.android:smart-swipe:1.1.2' + implementation 'com.billy.android:smart-swipe-x:1.1.0' + + // 日志调试:https://github.com/getActivity/Logcat + debugImplementation 'com.hjq:logcat:3.2' + + // 内存泄漏:https://github.com/square/leakcanary + debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0' // 国际化:https://github.com/getActivity/MultiLanguages // 悬浮窗:https://github.com/getActivity/XToast - // 上拉刷新下拉加载:https://github.com/scwang90/SmartRefreshLayout // 工具类:https://github.com/Blankj/AndroidUtilCode // 轮播图:https://github.com/bingoogolapple/BGABanner-Android // 二维码:https://github.com/bingoogolapple/BGAQRCode-Android - // 第三方支付:https://github.com/getActivity/RxPay - // Log 打印:https://github.com/elvishew/XLog + // 屏幕适配:https://github.com/JessYanCoding/AndroidAutoSize + // 跑马灯:https://github.com/sunfusheng/MarqueeView + // 第三方支付:https://github.com/Cuieney/RxPay + // Log 打印:https://github.com/JakeWharton/timber + // 图片裁剪:https://github.com/Yalantis/uCrop // 图片压缩:https://github.com/Curzibn/Luban // 对象存储:https://github.com/leavesC/DoKV // 数据注入:https://github.com/JumeiRdGroup/Parceler diff --git a/app/proguard-app.pro b/app/proguard-app.pro index ae479810..9ab5a35a 100644 --- a/app/proguard-app.pro +++ b/app/proguard-app.pro @@ -1,5 +1,5 @@ # 忽略警告 --ignorewarning +#-ignorewarning # 混淆保护自己项目的部分代码以及引用的第三方jar包 #-libraryjars libs/umeng-analytics-v5.2.4.jar @@ -11,7 +11,7 @@ #-keep class com.hjq.toast.** {*;} # 权限请求框架 -#-keep class com.hjq.permissions.** {*;} +-keep class com.hjq.permissions.** {*;} # 不混淆 WebView 的 JS 接口 -keepattributes *JavascriptInterface* @@ -34,6 +34,46 @@ (java.lang.Throwable); } +# Glide +-keep public class * implements com.bumptech.glide.module.GlideModule +-keep public class * extends com.bumptech.glide.module.AppGlideModule +-keep public enum com.bumptech.glide.load.ImageHeaderParser$** { + **[] $VALUES; + public *; +} + +# for DexGuard only +# -keepresourcexmlelements manifest/application/meta-data@value=GlideModule + # Bugly -dontwarn com.tencent.bugly.** --keep public class com.tencent.bugly.**{*;} \ No newline at end of file +-keep public class com.tencent.bugly.**{*;} + +# AOP +-adaptclassstrings +-keepattributes InnerClasses, EnclosingMethod, Signature, *Annotation* + +-keepnames @org.aspectj.lang.annotation.Aspect class * { + ajc* ; +} + +# OkHttp3 +-keepattributes Signature +-keepattributes *Annotation* +-keep class okhttp3.** { *; } +-keep interface okhttp3.** { *; } +-dontwarn okhttp3.** +-dontwarn okio.** + +# 保护 IRequestApi 类字段名不被混淆 +-keepclassmembernames class * implements com.hjq.http.config.IRequestApi { + ; +} +# 保护 Bean 类不被混淆(请注意修改包名路径) +-keepclassmembernames class com.hjq.demo.http.response.** { + ; +} +# 保护模型类的字段不被混淆(请注意修改包名路径) +-keepclassmembernames class com.hjq.demo.http.model.HttpData { + ; +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b9ed121f..88c2ec98 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -24,13 +24,21 @@ android:allowBackup="false" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" + android:supportsRtl="false" android:networkSecurityConfig="@xml/network_security_config" + android:requestLegacyExternalStorage="true" android:roundIcon="@mipmap/ic_launcher" android:theme="@style/AppTheme" android:usesCleartextTraffic="true" + tools:ignore="AllowBackup,GoogleAppIndexingWarning,UnusedAttribute" tools:targetApi="n"> - + + + + - + + + + + android:launchMode="singleTop" + android:screenOrientation="portrait"> @@ -76,12 +93,14 @@ @@ -89,6 +108,7 @@ android:name=".ui.activity.SettingActivity" android:configChanges="orientation|screenSize|keyboardHidden" android:label="@string/setting_title" + android:launchMode="singleTop" android:screenOrientation="portrait" /> @@ -96,6 +116,7 @@ android:name=".ui.activity.PasswordForgetActivity" android:configChanges="orientation|screenSize|keyboardHidden" android:label="@string/password_forget_title" + android:launchMode="singleTop" android:screenOrientation="portrait" /> @@ -103,6 +124,7 @@ android:name=".ui.activity.PasswordResetActivity" android:configChanges="orientation|screenSize|keyboardHidden" android:label="@string/password_reset_title" + android:launchMode="singleTop" android:screenOrientation="portrait" /> @@ -110,6 +132,7 @@ android:name=".ui.activity.PhoneVerifyActivity" android:configChanges="orientation|screenSize|keyboardHidden" android:label="@string/phone_verify_title" + android:launchMode="singleTop" android:screenOrientation="portrait" /> @@ -117,6 +140,7 @@ android:name=".ui.activity.PhoneResetActivity" android:configChanges="orientation|screenSize|keyboardHidden" android:label="@string/phone_reset_title" + android:launchMode="singleTop" android:screenOrientation="portrait" /> @@ -124,6 +148,7 @@ android:name=".ui.activity.AboutActivity" android:configChanges="orientation|screenSize|keyboardHidden" android:label="@string/about_title" + android:launchMode="singleTop" android:screenOrientation="portrait" /> @@ -131,13 +156,15 @@ android:name=".ui.activity.PersonalDataActivity" android:configChanges="orientation|screenSize|keyboardHidden" android:label="@string/personal_data_title" + android:launchMode="singleTop" android:screenOrientation="portrait" /> @@ -145,24 +172,34 @@ android:name=".ui.activity.PhotoActivity" android:configChanges="orientation|screenSize|keyboardHidden" android:label="@string/photo_title" + android:launchMode="singleTop" + android:screenOrientation="portrait" /> + + + diff --git a/app/src/main/java/com/hjq/demo/action/StatusAction.java b/app/src/main/java/com/hjq/demo/action/StatusAction.java new file mode 100644 index 00000000..0f32dce9 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/action/StatusAction.java @@ -0,0 +1,96 @@ +package com.hjq.demo.action; + +import android.app.Application; +import android.graphics.drawable.Drawable; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.view.View; + +import androidx.annotation.DrawableRes; +import androidx.annotation.RawRes; +import androidx.annotation.StringRes; +import androidx.core.content.ContextCompat; + +import com.hjq.demo.R; +import com.hjq.demo.helper.ActivityStackManager; +import com.hjq.demo.widget.HintLayout; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/08 + * desc : 界面状态提示 + */ +public interface StatusAction { + + /** + * 获取提示布局 + */ + HintLayout getHintLayout(); + + /** + * 显示加载中 + */ + default void showLoading() { + showLoading(R.raw.loading); + } + + default void showLoading(@RawRes int id) { + HintLayout layout = getHintLayout(); + layout.show(); + layout.setAnim(id); + layout.setHint(""); + layout.setOnClickListener(null); + } + + /** + * 显示加载完成 + */ + default void showComplete() { + HintLayout layout = getHintLayout(); + if (layout != null && layout.isShow()) { + layout.hide(); + } + } + + /** + * 显示空提示 + */ + default void showEmpty() { + showLayout(R.drawable.ic_hint_empty, R.string.hint_layout_no_data, null); + } + + /** + * 显示错误提示 + */ + default void showError(View.OnClickListener listener) { + Application application = ActivityStackManager.getInstance().getApplication(); + if (application != null) { + ConnectivityManager manager = ContextCompat.getSystemService(application, ConnectivityManager.class); + if (manager != null) { + NetworkInfo info = manager.getActiveNetworkInfo(); + // 判断网络是否连接 + if (info == null || !info.isConnected()) { + showLayout(R.drawable.ic_hint_nerwork, R.string.hint_layout_error_network, listener); + return; + } + } + } + showLayout(R.drawable.ic_hint_error, R.string.hint_layout_error_request, listener); + } + + /** + * 显示自定义提示 + */ + default void showLayout(@DrawableRes int drawableId, @StringRes int stringId, View.OnClickListener listener) { + showLayout(ContextCompat.getDrawable(ActivityStackManager.getInstance().getTopActivity(), drawableId), ActivityStackManager.getInstance().getTopActivity().getString(stringId), listener); + } + + default void showLayout(Drawable drawable, CharSequence hint, View.OnClickListener listener) { + HintLayout layout = getHintLayout(); + layout.show(); + layout.setIcon(drawable); + layout.setHint(hint); + layout.setOnClickListener(listener); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/action/SwipeAction.java b/app/src/main/java/com/hjq/demo/action/SwipeAction.java new file mode 100644 index 00000000..605475dc --- /dev/null +++ b/app/src/main/java/com/hjq/demo/action/SwipeAction.java @@ -0,0 +1,18 @@ +package com.hjq.demo.action; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/08 + * desc : 侧滑意图 + */ +public interface SwipeAction { + + /** + * 是否使用侧滑 + */ + default boolean isSwipeEnable() { + // 默认开启 + return true; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/action/TitleBarAction.java b/app/src/main/java/com/hjq/demo/action/TitleBarAction.java new file mode 100644 index 00000000..c5dd0a6b --- /dev/null +++ b/app/src/main/java/com/hjq/demo/action/TitleBarAction.java @@ -0,0 +1,173 @@ +package com.hjq.demo.action; + +import android.graphics.drawable.Drawable; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; + +import com.hjq.bar.OnTitleBarListener; +import com.hjq.bar.TitleBar; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/08 + * desc : 标题栏意图 + */ +public interface TitleBarAction extends OnTitleBarListener { + + @Nullable + TitleBar getTitleBar(); + + /** + * 左项被点击 + * + * @param v 被点击的左项View + */ + @Override + default void onLeftClick(View v) {} + + /** + * 标题被点击 + * + * @param v 被点击的标题View + */ + @Override + default void onTitleClick(View v){} + + /** + * 右项被点击 + * + * @param v 被点击的右项View + */ + @Override + default void onRightClick(View v) {} + + /** + * 设置标题栏的标题 + */ + default void setTitle(@StringRes int id) { + if (getTitleBar() != null) { + setTitle(getTitleBar().getResources().getString(id)); + } + } + + /** + * 设置标题栏的标题 + */ + default void setTitle(CharSequence title) { + if (getTitleBar() != null) { + getTitleBar().setTitle(title); + } + } + + /** + * 设置标题栏的左标题 + */ + default void setLeftTitle(int id) { + if (getTitleBar() != null) { + getTitleBar().setLeftTitle(id); + } + } + + default void setLeftTitle(CharSequence text) { + if (getTitleBar() != null) { + getTitleBar().setLeftTitle(text); + } + } + + default CharSequence getLeftTitle() { + if (getTitleBar() != null) { + return getTitleBar().getLeftTitle(); + } + return ""; + } + + /** + * 设置标题栏的右标题 + */ + default void setRightTitle(int id) { + if (getTitleBar() != null) { + getTitleBar().setRightTitle(id); + } + } + + default void setRightTitle(CharSequence text) { + if (getTitleBar() != null) { + getTitleBar().setRightTitle(text); + } + } + + default CharSequence getRightTitle() { + if (getTitleBar() != null) { + return getTitleBar().getRightTitle(); + } + return ""; + } + + /** + * 设置标题栏的左图标 + */ + default void setLeftIcon(int id) { + if (getTitleBar() != null) { + getTitleBar().setLeftIcon(id); + } + } + + default void setLeftIcon(Drawable drawable) { + if (getTitleBar() != null) { + getTitleBar().setLeftIcon(drawable); + } + } + + @Nullable + default Drawable getLeftIcon() { + if (getTitleBar() != null) { + return getTitleBar().getLeftIcon(); + } + return null; + } + + /** + * 设置标题栏的右图标 + */ + default void setRightIcon(int id) { + if (getTitleBar() != null) { + getTitleBar().setRightIcon(id); + } + } + + default void setRightIcon(Drawable drawable) { + if (getTitleBar() != null) { + getTitleBar().setRightIcon(drawable); + } + } + + @Nullable + default Drawable getRightIcon() { + if (getTitleBar() != null) { + return getTitleBar().getRightIcon(); + } + return null; + } + + /** + * 递归获取 ViewGroup 中的 TitleBar 对象 + */ + default TitleBar findTitleBar(ViewGroup group) { + for (int i = 0; i < group.getChildCount(); i++) { + View view = group.getChildAt(i); + if ((view instanceof TitleBar)) { + return (TitleBar) view; + } else if (view instanceof ViewGroup) { + TitleBar titleBar = findTitleBar((ViewGroup) view); + if (titleBar != null) { + return titleBar; + } + } + } + return null; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/action/ToastAction.java b/app/src/main/java/com/hjq/demo/action/ToastAction.java new file mode 100644 index 00000000..7f1bccc7 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/action/ToastAction.java @@ -0,0 +1,26 @@ +package com.hjq.demo.action; + +import androidx.annotation.StringRes; + +import com.hjq.toast.ToastUtils; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/08 + * desc : 吐司意图 + */ +public interface ToastAction { + + default void toast(CharSequence text) { + ToastUtils.show(text); + } + + default void toast(@StringRes int id) { + ToastUtils.show(id); + } + + default void toast(Object object) { + ToastUtils.show(object); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/mvp/MvpInject.java b/app/src/main/java/com/hjq/demo/aop/CheckNet.java similarity index 55% rename from app/src/main/java/com/hjq/demo/mvp/MvpInject.java rename to app/src/main/java/com/hjq/demo/aop/CheckNet.java index 9f932431..f8ba21bf 100644 --- a/app/src/main/java/com/hjq/demo/mvp/MvpInject.java +++ b/app/src/main/java/com/hjq/demo/aop/CheckNet.java @@ -1,4 +1,4 @@ -package com.hjq.demo.mvp; +package com.hjq.demo.aop; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -8,9 +8,9 @@ /** * author : Android 轮子哥 * github : https://github.com/getActivity/AndroidProject - * time : 2019/05/11 - * desc : Mvp 实例化注解 + * time : 2020/01/11 + * desc : 检测网络注解 */ -@Target(ElementType.FIELD) // 字段注解 -@Retention(RetentionPolicy.RUNTIME) // 运行时注解 -public @interface MvpInject {} \ No newline at end of file +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.METHOD) +public @interface CheckNet {} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/aop/CheckNetAspect.java b/app/src/main/java/com/hjq/demo/aop/CheckNetAspect.java new file mode 100644 index 00000000..b47c7c0d --- /dev/null +++ b/app/src/main/java/com/hjq/demo/aop/CheckNetAspect.java @@ -0,0 +1,53 @@ +package com.hjq.demo.aop; + +import android.app.Application; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; + +import androidx.core.content.ContextCompat; + +import com.hjq.demo.R; +import com.hjq.demo.helper.ActivityStackManager; +import com.hjq.toast.ToastUtils; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2020/01/11 + * desc : 网络检测 + */ +@Aspect +public class CheckNetAspect { + + /** + * 方法切入点 + */ + @Pointcut("execution(@com.hjq.demo.aop.CheckNet * *(..))") + public void method() {} + + /** + * 在连接点进行方法替换 + */ + @Around("method() && @annotation(checkNet)") + public void aroundJoinPoint(ProceedingJoinPoint joinPoint, CheckNet checkNet) throws Throwable { + Application application = ActivityStackManager.getInstance().getApplication(); + if (application != null) { + ConnectivityManager manager = ContextCompat.getSystemService(application, ConnectivityManager.class); + if (manager != null) { + NetworkInfo info = manager.getActiveNetworkInfo(); + // 判断网络是否连接 + if (info == null || !info.isConnected()) { + ToastUtils.show(R.string.common_network); + return; + } + } + } + //执行原方法 + joinPoint.proceed(); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/aop/DebugLog.java b/app/src/main/java/com/hjq/demo/aop/DebugLog.java new file mode 100644 index 00000000..e9863b11 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/aop/DebugLog.java @@ -0,0 +1,19 @@ +package com.hjq.demo.aop; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/06 + * desc : Debug 日志注解 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) +public @interface DebugLog { + + String value() default "DebugLog"; +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/aop/DebugLogAspect.java b/app/src/main/java/com/hjq/demo/aop/DebugLogAspect.java new file mode 100644 index 00000000..7c327488 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/aop/DebugLogAspect.java @@ -0,0 +1,156 @@ +package com.hjq.demo.aop; + +import android.os.Looper; +import android.os.Trace; +import android.util.Log; + +import androidx.annotation.NonNull; + +import com.hjq.demo.other.AppConfig; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.Signature; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.CodeSignature; +import org.aspectj.lang.reflect.MethodSignature; + +import java.util.concurrent.TimeUnit; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/06 + * desc : Debug 日志打印 + */ +@Aspect +public class DebugLogAspect { + + /** + * 构造方法切入点 + */ + @Pointcut("execution(@com.hjq.demo.aop.DebugLog *.new(..))") + public void constructor() {} + + /** + * 方法切入点 + */ + @Pointcut("execution(@com.hjq.demo.aop.DebugLog * *(..))") + public void method() {} + + /** + * 在连接点进行方法替换 + */ + @Around("(method() || constructor()) && @annotation(debugLog)") + public Object aroundJoinPoint(ProceedingJoinPoint joinPoint, DebugLog debugLog) throws Throwable { + enterMethod(joinPoint, debugLog); + + long startNanos = System.nanoTime(); + Object result = joinPoint.proceed(); + long stopNanos = System.nanoTime(); + + exitMethod(joinPoint, debugLog, result, TimeUnit.NANOSECONDS.toMillis(stopNanos - startNanos)); + + return result; + } + + + /** + * 方法执行前切入 + */ + private void enterMethod(ProceedingJoinPoint joinPoint, DebugLog debugLog) { + if (!AppConfig.isDebug()) { + return; + } + + CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature(); + + // 方法所在类 + String className = codeSignature.getDeclaringType().getName(); + // 方法名 + String methodName = codeSignature.getName(); + // 方法参数名集合 + String[] parameterNames = codeSignature.getParameterNames(); + // 方法参数集合 + Object[] parameterValues = joinPoint.getArgs(); + + //记录并打印方法的信息 + StringBuilder builder = getMethodLogInfo(className, methodName, parameterNames, parameterValues); + + log(debugLog.value(), builder.toString()); + + final String section = builder.toString().substring(2); + Trace.beginSection(section); + } + + /** + * 获取方法的日志信息 + * + * @param className 类名 + * @param methodName 方法名 + * @param parameterNames 方法参数名集合 + * @param parameterValues 方法参数值集合 + */ + @NonNull + private StringBuilder getMethodLogInfo(String className, String methodName, String[] parameterNames, Object[] parameterValues) { + StringBuilder builder = new StringBuilder("\u21E2 "); + builder.append(className) + .append(".") + .append(methodName) + .append('('); + for (int i = 0; i < parameterValues.length; i++) { + if (i > 0) { + builder.append(", "); + } + builder.append(parameterNames[i]).append('='); + builder.append(parameterValues[i].toString()); + } + builder.append(')'); + + if (Looper.myLooper() != Looper.getMainLooper()) { + builder.append(" [Thread:\"").append(Thread.currentThread().getName()).append("\"]"); + } + return builder; + } + + + /** + * 方法执行完毕,切出 + * + * @param result 方法执行后的结果 + * @param lengthMillis 执行方法所需要的时间 + */ + private void exitMethod(ProceedingJoinPoint joinPoint, DebugLog debugLog, Object result, long lengthMillis) { + if (!AppConfig.isDebug()) { + return; + } + + Trace.endSection(); + + Signature signature = joinPoint.getSignature(); + + String className = signature.getDeclaringType().getName(); + String methodName = signature.getName(); + + StringBuilder builder = new StringBuilder("\u21E0 ") + .append(className) + .append(".") + .append(methodName) + .append(" [") + .append(lengthMillis) + .append("ms]"); + + // 判断方法是否有返回值 + if (signature instanceof MethodSignature && ((MethodSignature) signature).getReturnType() != void.class) { + builder.append(" = "); + builder.append(result.toString()); + } + + log(debugLog.value(), builder.toString()); + } + + private void log(String tag, String msg) { + Log.d(tag, msg); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/aop/Permissions.java b/app/src/main/java/com/hjq/demo/aop/Permissions.java new file mode 100644 index 00000000..c7b6a5d1 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/aop/Permissions.java @@ -0,0 +1,22 @@ +package com.hjq.demo.aop; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/06 + * desc : 权限申请注解 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +public @interface Permissions { + + /** + * 需要申请权限的集合 + */ + String[] value(); +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/aop/PermissionsAspect.java b/app/src/main/java/com/hjq/demo/aop/PermissionsAspect.java new file mode 100644 index 00000000..35df5d73 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/aop/PermissionsAspect.java @@ -0,0 +1,63 @@ +package com.hjq.demo.aop; + +import com.hjq.demo.R; +import com.hjq.demo.helper.ActivityStackManager; +import com.hjq.permissions.OnPermission; +import com.hjq.permissions.XXPermissions; +import com.hjq.toast.ToastUtils; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; + +import java.util.List; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/06 + * desc : 权限申请处理 + */ +@Aspect +public class PermissionsAspect { + + /** + * 方法切入点 + */ + @Pointcut("execution(@com.hjq.demo.aop.Permissions * *(..))") + public void method() {} + + /** + * 在连接点进行方法替换 + */ + @Around("method() && @annotation(permissions)") + public void aroundJoinPoint(final ProceedingJoinPoint joinPoint, Permissions permissions) { + XXPermissions.with(ActivityStackManager.getInstance().getTopActivity()) + .permission(permissions.value()) + .request(new OnPermission() { + + @Override + public void hasPermission(List granted, boolean all) { + if (all) { + try { + // 获得权限,执行原方法 + joinPoint.proceed(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + @Override + public void noPermission(List denied, boolean quick) { + if (quick) { + ToastUtils.show(R.string.common_permission_fail); + XXPermissions.gotoPermissionSettings(ActivityStackManager.getInstance().getTopActivity(), false); + } else { + ToastUtils.show(R.string.common_permission_hint); + } + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/aop/SingleClick.java b/app/src/main/java/com/hjq/demo/aop/SingleClick.java new file mode 100644 index 00000000..e27372f6 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/aop/SingleClick.java @@ -0,0 +1,22 @@ +package com.hjq.demo.aop; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/06 + * desc : 防重复点击注解 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface SingleClick { + + /** + * 快速点击的间隔 + */ + long value() default 1000; +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/aop/SingleClickAspect.java b/app/src/main/java/com/hjq/demo/aop/SingleClickAspect.java new file mode 100644 index 00000000..43bf454c --- /dev/null +++ b/app/src/main/java/com/hjq/demo/aop/SingleClickAspect.java @@ -0,0 +1,61 @@ +package com.hjq.demo.aop; + +import android.util.Log; +import android.view.View; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; + +import java.util.Calendar; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/06 + * desc : 防重复点击处理 + */ +@Aspect +public class SingleClickAspect { + + /** + * 最近一次点击的时间 + */ + private long mLastTime; + /** + * 最近一次点击的控件ID + */ + private int mLastId; + + /** + * 方法切入点 + */ + @Pointcut("execution(@com.hjq.demo.aop.SingleClick * *(..))") + public void method() {} + + /** + * 在连接点进行方法替换 + */ + @Around("method() && @annotation(singleClick)") + public void aroundJoinPoint(ProceedingJoinPoint joinPoint, SingleClick singleClick) throws Throwable { + View view = null; + for (Object arg : joinPoint.getArgs()) { + if (arg instanceof View) { + view = (View) arg; + } + } + if (view != null) { + long currentTime = Calendar.getInstance().getTimeInMillis(); + if (currentTime - mLastTime < singleClick.value() && view.getId() + == mLastId) { + Log.i("SingleClick", "发生快速点击"); + return; + } + mLastTime = currentTime; + mLastId = view.getId(); + //执行原方法 + joinPoint.proceed(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/common/MyActivity.java b/app/src/main/java/com/hjq/demo/common/MyActivity.java index 7a07c316..e4996da9 100644 --- a/app/src/main/java/com/hjq/demo/common/MyActivity.java +++ b/app/src/main/java/com/hjq/demo/common/MyActivity.java @@ -1,30 +1,28 @@ package com.hjq.demo.common; import android.content.Intent; -import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.util.Log; import android.view.View; -import android.view.ViewGroup; -import androidx.annotation.DrawableRes; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import com.gyf.immersionbar.ImmersionBar; -import com.hjq.bar.OnTitleBarListener; import com.hjq.bar.TitleBar; import com.hjq.base.BaseActivity; -import com.hjq.base.BuildConfig; +import com.hjq.base.BaseDialog; import com.hjq.demo.R; -import com.hjq.demo.helper.ActivityStackManager; -import com.hjq.demo.other.EventBusManager; -import com.hjq.demo.other.StatusManager; -import com.hjq.toast.ToastUtils; +import com.hjq.demo.action.SwipeAction; +import com.hjq.demo.action.TitleBarAction; +import com.hjq.demo.action.ToastAction; +import com.hjq.demo.http.model.HttpData; +import com.hjq.demo.ui.dialog.WaitDialog; +import com.hjq.http.EasyHttp; +import com.hjq.http.listener.OnHttpListener; import com.hjq.umeng.UmengClient; import butterknife.ButterKnife; -import butterknife.Unbinder; +import okhttp3.Call; /** * author : Android 轮子哥 @@ -33,82 +31,77 @@ * desc : 项目中的 Activity 基类 */ public abstract class MyActivity extends BaseActivity - implements OnTitleBarListener { + implements ToastAction, TitleBarAction, + SwipeAction, OnHttpListener { /** 标题栏对象 */ private TitleBar mTitleBar; /** 状态栏沉浸 */ private ImmersionBar mImmersionBar; - /** ButterKnife 注解 */ - private Unbinder mButterKnife; + + /** 加载对话框 */ + private BaseDialog mDialog; + /** 对话框数量 */ + private int mDialogTotal; /** - * 获取标题栏 id + * 当前加载对话框是否在显示中 */ - protected int getTitleId() { - return 0; + public boolean isShowDialog() { + return mDialog != null && mDialog.isShowing(); } - @Override - protected void initActivity() { - super.initActivity(); - ActivityStackManager.getInstance().onCreated(this); + /** + * 显示加载对话框 + */ + public void showDialog() { + if (mDialog == null) { + mDialog = new WaitDialog.Builder(this) + .setCancelable(false) + .create(); + } + if (!mDialog.isShowing()) { + mDialog.show(); + } + mDialogTotal++; + } + + /** + * 隐藏加载对话框 + */ + public void hideDialog() { + if (mDialogTotal == 1) { + if (mDialog != null && mDialog.isShowing()) { + mDialog.dismiss(); + } + } + if (mDialogTotal > 0) { + mDialogTotal--; + } } @Override protected void initLayout() { super.initLayout(); - // 初始化标题栏的监听 - if (getTitleId() > 0) { - // 勤快模式 - View view = findViewById(getTitleId()); - if (view instanceof TitleBar) { - mTitleBar = (TitleBar) view; - } - } else if (getTitleId() == 0) { - // 懒人模式 - mTitleBar = findTitleBar(getContentView()); - } - if (mTitleBar != null) { - mTitleBar.setOnTitleBarListener(this); + if (getTitleBar() != null) { + getTitleBar().setOnTitleBarListener(this); } - mButterKnife = ButterKnife.bind(this); - EventBusManager.register(this); + ButterKnife.bind(this); initImmersion(); } - /** - * 递归获取 ViewGroup 中的 TitleBar 对象 - */ - static TitleBar findTitleBar(ViewGroup group) { - for (int i = 0; i < group.getChildCount(); i++) { - View view = group.getChildAt(i); - if ((view instanceof TitleBar)) { - return (TitleBar) view; - } else if (view instanceof ViewGroup) { - TitleBar titleBar = findTitleBar((ViewGroup) view); - if (titleBar != null) { - return titleBar; - } - } - } - return null; - } - /** * 初始化沉浸式 */ protected void initImmersion() { // 初始化沉浸式状态栏 if (isStatusBarEnabled()) { - statusBarConfig().init(); + createStatusBarConfig().init(); // 设置标题栏沉浸 - if (getTitleId() > 0) { - ImmersionBar.setTitleBar(this, findViewById(getTitleId())); - } else if (mTitleBar != null) { + if (mTitleBar != null) { ImmersionBar.setTitleBar(this, mTitleBar); } } @@ -117,33 +110,33 @@ protected void initImmersion() { /** * 是否使用沉浸式状态栏 */ - public boolean isStatusBarEnabled() { + protected boolean isStatusBarEnabled() { return true; } /** - * 获取状态栏沉浸的配置对象 + * 状态栏字体深色模式 */ - public ImmersionBar getStatusBarConfig() { - return mImmersionBar; - } - - /** - * 获取状态栏字体颜色 - */ - public boolean statusBarDarkFont() { - // 返回真表示黑色字体 + protected boolean isStatusBarDarkFont() { return true; } /** * 初始化沉浸式状态栏 */ - protected ImmersionBar statusBarConfig() { + protected ImmersionBar createStatusBarConfig() { // 在BaseActivity里初始化 mImmersionBar = ImmersionBar.with(this) // 默认状态栏字体颜色为黑色 - .statusBarDarkFont(statusBarDarkFont()); + .statusBarDarkFont(isStatusBarDarkFont()); + return mImmersionBar; + } + + /** + * 获取状态栏沉浸的配置对象 + */ + @Nullable + public ImmersionBar getStatusBarConfig() { return mImmersionBar; } @@ -166,125 +159,20 @@ public void setTitle(CharSequence title) { } } - /** - * 设置标题栏的左标题 - */ - public void setLeftTitle(int id) { - if (mTitleBar != null) { - mTitleBar.setLeftTitle(id); - } - } - - public void setLeftTitle(CharSequence text) { - if (mTitleBar != null) { - mTitleBar.setLeftTitle(text); - } - } - - public CharSequence getLeftTitle() { - if (mTitleBar != null) { - return mTitleBar.getLeftTitle(); - } - return ""; - } - - /** - * 设置标题栏的右标题 - */ - public void setRightTitle(int id) { - if (mTitleBar != null) { - mTitleBar.setRightTitle(id); - } - } - - public void setRightTitle(CharSequence text) { - if (mTitleBar != null) { - mTitleBar.setRightTitle(text); - } - } - - public CharSequence getRightTitle() { - if (mTitleBar != null) { - return mTitleBar.getRightTitle(); - } - return ""; - } - - /** - * 设置标题栏的左图标 - */ - public void setLeftIcon(int id) { - if (mTitleBar != null) { - mTitleBar.setLeftIcon(id); - } - } - - public void setLeftIcon(Drawable drawable) { - if (mTitleBar != null) { - mTitleBar.setLeftIcon(drawable); - } - } - - @Nullable - public Drawable getLeftIcon() { - if (mTitleBar != null) { - return mTitleBar.getLeftIcon(); - } - return null; - } - - /** - * 设置标题栏的右图标 - */ - public void setRightIcon(int id) { - if (mTitleBar != null) { - mTitleBar.setRightIcon(id); - } - } - - public void setRightIcon(Drawable drawable) { - if (mTitleBar != null) { - mTitleBar.setRightIcon(drawable); - } - } - - @Nullable - public Drawable getRightIcon() { - if (mTitleBar != null) { - return mTitleBar.getRightIcon(); - } - return null; - } - + @Override @Nullable public TitleBar getTitleBar() { + if (mTitleBar == null) { + mTitleBar = findTitleBar(getContentView()); + } return mTitleBar; } - /** - * {@link OnTitleBarListener} - */ - - /** - * TitleBar 左边的View被点击了 - */ @Override public void onLeftClick(View v) { onBackPressed(); } - /** - * TitleBar 中间的View被点击了 - */ - @Override - public void onTitleClick(View v) {} - - /** - * TitleBar 右边的View被点击了 - */ - @Override - public void onRightClick(View v) {} - @Override protected void onResume() { super.onResume(); @@ -297,16 +185,6 @@ protected void onPause() { super.onPause(); } - @Override - protected void onDestroy() { - super.onDestroy(); - if (mButterKnife != null) { - mButterKnife.unbind(); - } - EventBusManager.unregister(this); - ActivityStackManager.getInstance().onDestroyed(this); - } - @Override public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) { super.startActivityForResult(intent, requestCode, options); @@ -320,75 +198,38 @@ public void finish() { } /** - * 显示吐司 + * {@link OnHttpListener} */ - public void toast(CharSequence text) { - ToastUtils.show(text); - } - public void toast(@StringRes int id) { - ToastUtils.show(id); - } - - public void toast(Object object) { - ToastUtils.show(object); + @Override + public void onStart(Call call) { + showDialog(); } - /** - * 打印日志 - */ - public void log(Object object) { - if (BuildConfig.DEBUG) { - Log.v(getClass().getSimpleName(), object != null ? object.toString() : "null"); + @Override + public void onSucceed(Object result) { + if (result instanceof HttpData) { + toast(((HttpData) result).getMessage()); } } - private final StatusManager mStatusManager = new StatusManager(); - - /** - * 显示加载中 - */ - public void showLoading() { - mStatusManager.showLoading(this); - } - - public void showLoading(@StringRes int id) { - mStatusManager.showLoading(this, getString(id)); - } - - public void showLoading(CharSequence text) { - mStatusManager.showLoading(this, text); - } - - /** - * 显示加载完成 - */ - public void showComplete() { - mStatusManager.showComplete(); - } - - /** - * 显示空提示 - */ - public void showEmpty() { - mStatusManager.showEmpty(getContentView()); - } - - /** - * 显示错误提示 - */ - public void showError() { - mStatusManager.showError(getContentView()); + @Override + public void onFail(Exception e) { + toast(e.getMessage()); } - /** - * 显示自定义提示 - */ - public void showLayout(@DrawableRes int drawableId, @StringRes int stringId) { - mStatusManager.showLayout(getContentView(), drawableId, stringId); + @Override + public void onEnd(Call call) { + hideDialog(); } - public void showLayout(Drawable drawable, CharSequence hint) { - mStatusManager.showLayout(getContentView(), drawable, hint); + @Override + protected void onDestroy() { + EasyHttp.cancel(this); + if (isShowDialog()) { + mDialog.dismiss(); + } + mDialog = null; + super.onDestroy(); } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/common/MyRecyclerViewAdapter.java b/app/src/main/java/com/hjq/demo/common/MyAdapter.java similarity index 77% rename from app/src/main/java/com/hjq/demo/common/MyRecyclerViewAdapter.java rename to app/src/main/java/com/hjq/demo/common/MyAdapter.java index 8f1868b5..5bfdbb7f 100644 --- a/app/src/main/java/com/hjq/demo/common/MyRecyclerViewAdapter.java +++ b/app/src/main/java/com/hjq/demo/common/MyAdapter.java @@ -3,12 +3,12 @@ import android.content.Context; import android.view.View; +import androidx.annotation.IntRange; import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.StringRes; -import com.hjq.base.BaseRecyclerViewAdapter; -import com.hjq.toast.ToastUtils; +import com.hjq.base.BaseAdapter; import java.util.ArrayList; import java.util.List; @@ -21,7 +21,7 @@ * time : 2018/12/19 * desc : 项目中 RecyclerView 适配器基类 */ -public abstract class MyRecyclerViewAdapter extends BaseRecyclerViewAdapter { +public abstract class MyAdapter extends BaseAdapter { /** 列表数据 */ private List mDataSet; @@ -32,7 +32,7 @@ public abstract class MyRecyclerViewAdapter extends BaseRecyclerViewAdapter data) { + public void setData(@Nullable List data) { mDataSet = data; notifyDataSetChanged(); } @@ -88,14 +88,14 @@ public void clearData() { /** * 获取某个位置上的数据 */ - public T getItem(int position) { + public T getItem(@IntRange(from = 0) int position) { return mDataSet.get(position); } /** * 更新某个位置上的数据 */ - public void setItem(int position, T item) { + public void setItem(@IntRange(from = 0) int position, @NonNull T item) { if (mDataSet == null) { mDataSet = new ArrayList<>(); } @@ -106,7 +106,7 @@ public void setItem(int position, T item) { /** * 添加单条数据 */ - public void addItem(T item) { + public void addItem(@NonNull T item) { if (mDataSet == null) { mDataSet = new ArrayList<>(); } @@ -114,7 +114,7 @@ public void addItem(T item) { addItem(mDataSet.size(), item); } - public void addItem(int position, T item) { + public void addItem(@IntRange(from = 0) int position, @NonNull T item) { if (mDataSet == null) { mDataSet = new ArrayList<>(); } @@ -131,14 +131,14 @@ public void addItem(int position, T item) { /** * 删除单条数据 */ - public void removeItem(T item) { + public void removeItem(@NonNull T item) { int index = mDataSet.indexOf(item); if (index != -1) { removeItem(index); } } - public void removeItem(int position) { + public void removeItem(@IntRange(from = 0) int position) { //如果是在for循环删除后要记得i-- mDataSet.remove(position); //告诉适配器删除数据的位置,会有动画效果 @@ -155,7 +155,7 @@ public int getPageNumber() { /** * 设置当前的页码 */ - public void setPageNumber(int pageNumber) { + public void setPageNumber(@IntRange(from = 0)int pageNumber) { mPageNumber = pageNumber; } @@ -169,13 +169,14 @@ public boolean isLastPage() { /** * 设置是否为最后一页 */ - public void setLastPage(boolean lastPage) { - mLastPage = lastPage; + public void setLastPage(boolean flag) { + mLastPage = flag; } /** * 获取标记 */ + @Nullable public Object getTag() { return mTag; } @@ -183,26 +184,11 @@ public Object getTag() { /** * 设置标记 */ - public void setTag(Object tag) { + public void setTag(@NonNull Object tag) { mTag = tag; } - /** - * 显示吐司 - */ - public void toast(CharSequence text) { - ToastUtils.show(text); - } - - public void toast(@StringRes int id) { - ToastUtils.show(id); - } - - public void toast(Object object) { - ToastUtils.show(object); - } - - public abstract class ViewHolder extends BaseRecyclerViewAdapter.ViewHolder { + public abstract class ViewHolder extends BaseAdapter.ViewHolder { public ViewHolder(@LayoutRes int id) { super(id); @@ -215,7 +201,7 @@ public ViewHolder(View itemView) { } } - public class SimpleHolder extends ViewHolder { + public final class SimpleHolder extends ViewHolder { public SimpleHolder(@LayoutRes int id) { super(id); diff --git a/app/src/main/java/com/hjq/demo/common/MyApplication.java b/app/src/main/java/com/hjq/demo/common/MyApplication.java index b039eb56..2d969892 100644 --- a/app/src/main/java/com/hjq/demo/common/MyApplication.java +++ b/app/src/main/java/com/hjq/demo/common/MyApplication.java @@ -2,21 +2,35 @@ import android.app.Application; import android.content.Context; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; import android.util.Log; import android.widget.Toast; -import com.hjq.demo.BuildConfig; -import com.hjq.demo.other.EventBusManager; +import com.billy.android.swipe.SmartSwipeBack; +import com.hjq.bar.TitleBar; +import com.hjq.bar.style.TitleBarLightStyle; +import com.hjq.demo.R; +import com.hjq.demo.action.SwipeAction; +import com.hjq.demo.helper.ActivityStackManager; +import com.hjq.demo.http.model.RequestHandler; +import com.hjq.demo.http.server.ReleaseServer; +import com.hjq.demo.http.server.TestServer; +import com.hjq.demo.other.AppConfig; import com.hjq.demo.ui.activity.CrashActivity; import com.hjq.demo.ui.activity.HomeActivity; -import com.hjq.image.ImageLoader; +import com.hjq.http.EasyConfig; +import com.hjq.http.config.IRequestServer; import com.hjq.toast.ToastInterceptor; import com.hjq.toast.ToastUtils; import com.hjq.umeng.UmengClient; -import com.squareup.leakcanary.LeakCanary; +import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.scwang.smartrefresh.layout.footer.ClassicsFooter; +import com.scwang.smartrefresh.layout.header.ClassicsHeader; import com.tencent.bugly.crashreport.CrashReport; import cat.ereza.customactivityoncrash.config.CaocConfig; +import okhttp3.OkHttpClient; /** * author : Android 轮子哥 @@ -36,18 +50,12 @@ public void onCreate() { * 初始化一些第三方框架 */ public static void initSDK(Application application) { - // 这个过程专门用于堆分析的 leak 金丝雀 - // 你不应该在这个过程中初始化你的应用程序 - if (LeakCanary.isInAnalyzerProcess(application)) { - return; - } - - // 内存泄漏检测 - LeakCanary.install(application); - // 友盟统计、登录、分享 SDK UmengClient.init(application); + // 吐司工具类 + ToastUtils.init(application); + // 设置 Toast 拦截器 ToastUtils.setToastInterceptor(new ToastInterceptor() { @Override @@ -61,17 +69,23 @@ public boolean intercept(Toast toast, CharSequence text) { return intercept; } }); - // 吐司工具类 - ToastUtils.init(application); - // 图片加载器 - ImageLoader.init(application); + // 标题栏全局样式 + TitleBar.initStyle(new TitleBarLightStyle(application) { + + @Override + public Drawable getBackground() { + return new ColorDrawable(getColor(R.color.colorPrimary)); + } - // EventBus 事件总线 - EventBusManager.init(); + @Override + public Drawable getBackIcon() { + return getDrawable(R.drawable.ic_back_black); + } + }); // Bugly 异常捕捉 - CrashReport.initCrashReport(application, BuildConfig.BUGLY_ID, false); + CrashReport.initCrashReport(application, AppConfig.getBuglyId(), false); // Crash 捕捉界面 CaocConfig.Builder.create() @@ -86,6 +100,46 @@ public boolean intercept(Toast toast, CharSequence text) { // 设置监听器 //.eventListener(new YourCustomEventListener()) .apply(); + + // 设置全局的 Header 构建器 + SmartRefreshLayout.setDefaultRefreshHeaderCreator((context, layout) -> new ClassicsHeader(context).setEnableLastTime(false)); + // 设置全局的 Footer 构建器 + SmartRefreshLayout.setDefaultRefreshFooterCreator((context, layout) -> new ClassicsFooter(context).setDrawableSize(20)); + + // Activity 栈管理初始化 + ActivityStackManager.getInstance().init(application); + + // 网络请求框架初始化 + IRequestServer server; + if (AppConfig.isDebug()) { + server = new TestServer(); + } else { + server = new ReleaseServer(); + } + + EasyConfig.with(new OkHttpClient()) + // 是否打印日志 + .setLogEnabled(AppConfig.isDebug()) + // 设置服务器配置 + .setServer(server) + // 设置请求处理策略 + .setHandler(new RequestHandler()) + // 设置请求重试次数 + .setRetryCount(3) + // 添加全局请求参数 + //.addParam("token", "6666666") + // 添加全局请求头 + //.addHeader("time", "20191030") + // 启用配置 + .into(); + + // Activity 侧滑返回 + SmartSwipeBack.activitySlidingBack(application, activity -> { + if (activity instanceof SwipeAction) { + return ((SwipeAction) activity).isSwipeEnable(); + } + return true; + }); } @Override diff --git a/app/src/main/java/com/hjq/demo/common/MyDialogFragment.java b/app/src/main/java/com/hjq/demo/common/MyDialogFragment.java deleted file mode 100644 index f2298970..00000000 --- a/app/src/main/java/com/hjq/demo/common/MyDialogFragment.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.hjq.demo.common; - -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.StringRes; -import androidx.fragment.app.FragmentActivity; - -import com.hjq.base.BaseDialogFragment; -import com.hjq.toast.ToastUtils; - -import butterknife.ButterKnife; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2019/05/18 - * desc : 项目中的 Dialog 基类 - */ -public final class MyDialogFragment { - - public static class Builder - extends BaseDialogFragment.Builder { - - public Builder(FragmentActivity activity) { - super(activity); - } - - @Override - public B setContentView(@NonNull View view) { - // 使用 ButterKnife 注解 - ButterKnife.bind(this, view); - return super.setContentView(view); - } - - /** - * 显示吐司 - */ - public void toast(CharSequence text) { - ToastUtils.show(text); - } - - public void toast(@StringRes int id) { - ToastUtils.show(id); - } - - public void toast(Object object) { - ToastUtils.show(object); - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/common/MyFragment.java b/app/src/main/java/com/hjq/demo/common/MyFragment.java new file mode 100644 index 00000000..d4990056 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/common/MyFragment.java @@ -0,0 +1,143 @@ +package com.hjq.demo.common; + +import android.view.ViewGroup; + +import androidx.annotation.Nullable; + +import com.gyf.immersionbar.ImmersionBar; +import com.hjq.bar.TitleBar; +import com.hjq.base.BaseFragment; +import com.hjq.demo.action.TitleBarAction; +import com.hjq.demo.action.ToastAction; +import com.hjq.http.EasyHttp; +import com.hjq.umeng.UmengClient; + +import butterknife.ButterKnife; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2018/10/18 + * desc : 项目中 Fragment 懒加载基类 + */ +public abstract class MyFragment extends BaseFragment + implements ToastAction, TitleBarAction { + + /** 标题栏对象 */ + private TitleBar mTitleBar; + /** 状态栏沉浸 */ + private ImmersionBar mImmersionBar; + + @Override + protected void initFragment() { + ButterKnife.bind(this, getView()); + + if (getTitleBar() != null) { + getTitleBar().setOnTitleBarListener(this); + } + + initImmersion(); + super.initFragment(); + } + + /** + * 初始化沉浸式 + */ + protected void initImmersion() { + + // 初始化沉浸式状态栏 + if (isStatusBarEnabled()) { + statusBarConfig().init(); + + // 设置标题栏沉浸 + if (mTitleBar != null) { + ImmersionBar.setTitleBar(this, mTitleBar); + } + } + } + + /** + * 是否在Fragment使用沉浸式 + */ + public boolean isStatusBarEnabled() { + return false; + } + + /** + * 获取状态栏沉浸的配置对象 + */ + protected ImmersionBar getStatusBarConfig() { + return mImmersionBar; + } + + /** + * 初始化沉浸式 + */ + private ImmersionBar statusBarConfig() { + //在BaseActivity里初始化 + mImmersionBar = ImmersionBar.with(this) + // 默认状态栏字体颜色为黑色 + .statusBarDarkFont(statusBarDarkFont()) + // 解决软键盘与底部输入框冲突问题,默认为false,还有一个重载方法,可以指定软键盘mode + .keyboardEnable(true); + return mImmersionBar; + } + + /** + * 获取状态栏字体颜色 + */ + protected boolean statusBarDarkFont() { + // 返回真表示黑色字体 + return true; + } + + @Override + @Nullable + public TitleBar getTitleBar() { + if (mTitleBar == null) { + mTitleBar = findTitleBar((ViewGroup) getView()); + } + return mTitleBar; + } + + /** + * 当前加载对话框是否在显示中 + */ + public boolean isShowDialog() { + return getAttachActivity().isShowDialog(); + } + + /** + * 显示加载对话框 + */ + public void showDialog() { + getAttachActivity().showDialog(); + } + + /** + * 隐藏加载对话框 + */ + public void hideDialog() { + getAttachActivity().hideDialog(); + } + + @Override + public void onResume() { + super.onResume(); + // 重新初始化状态栏 + statusBarConfig().init(); + UmengClient.onResume(this); + } + + @Override + public void onPause() { + UmengClient.onPause(this); + super.onPause(); + } + + @Override + public void onDetach() { + EasyHttp.cancel(this); + super.onDetach(); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/common/MyLazyFragment.java b/app/src/main/java/com/hjq/demo/common/MyLazyFragment.java deleted file mode 100644 index ca2b3a16..00000000 --- a/app/src/main/java/com/hjq/demo/common/MyLazyFragment.java +++ /dev/null @@ -1,285 +0,0 @@ -package com.hjq.demo.common; - -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; - -import com.gyf.immersionbar.ImmersionBar; -import com.hjq.bar.OnTitleBarListener; -import com.hjq.bar.TitleBar; -import com.hjq.base.BaseLazyFragment; -import com.hjq.base.BuildConfig; -import com.hjq.demo.other.EventBusManager; -import com.hjq.demo.other.StatusManager; -import com.hjq.toast.ToastUtils; -import com.hjq.umeng.UmengClient; - -import butterknife.ButterKnife; -import butterknife.Unbinder; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/10/18 - * desc : 项目中 Fragment 懒加载基类 - */ -public abstract class MyLazyFragment - extends BaseLazyFragment implements OnTitleBarListener { - - /** 标题栏对象 */ - private TitleBar mTitleBar; - /** 状态栏沉浸 */ - private ImmersionBar mImmersionBar; - /** ButterKnife 注解 */ - private Unbinder mButterKnife; - - /** - * 获取标题栏 id - */ - protected int getTitleId() { - return 0; - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = super.onCreateView(inflater, container, savedInstanceState); - if (view != null) { - mButterKnife = ButterKnife.bind(this, view); - } - return view; - } - - @Override - protected void initFragment() { - if (getTitleId() > 0) { - // 勤快模式 - View view = findViewById(getTitleId()); - if (view instanceof TitleBar) { - mTitleBar = (TitleBar) view; - } - } else if (getTitleId() == 0 && getView() instanceof ViewGroup) { - // 懒人模式 - mTitleBar = MyActivity.findTitleBar((ViewGroup) getView()); - } - if (mTitleBar != null) { - mTitleBar.setOnTitleBarListener(this); - } - - initImmersion(); - super.initFragment(); - EventBusManager.register(this); - } - - /** - * 初始化沉浸式 - */ - protected void initImmersion() { - - // 初始化沉浸式状态栏 - if (isStatusBarEnabled()) { - statusBarConfig().init(); - - // 设置标题栏沉浸 - if (getTitleId() > 0) { - ImmersionBar.setTitleBar(this, findViewById(getTitleId())); - } else if (mTitleBar != null) { - ImmersionBar.setTitleBar(this, mTitleBar); - } - } - } - - /** - * 是否在Fragment使用沉浸式 - */ - public boolean isStatusBarEnabled() { - return false; - } - - /** - * 获取状态栏沉浸的配置对象 - */ - protected ImmersionBar getStatusBarConfig() { - return mImmersionBar; - } - - /** - * 初始化沉浸式 - */ - private ImmersionBar statusBarConfig() { - //在BaseActivity里初始化 - mImmersionBar = ImmersionBar.with(this) - // 默认状态栏字体颜色为黑色 - .statusBarDarkFont(statusBarDarkFont()) - // 解决软键盘与底部输入框冲突问题,默认为false,还有一个重载方法,可以指定软键盘mode - .keyboardEnable(true); - return mImmersionBar; - } - - /** - * 获取状态栏字体颜色 - */ - protected boolean statusBarDarkFont() { - // 返回真表示黑色字体 - return true; - } - - @Override - public void setUserVisibleHint(boolean isVisibleToUser) { - super.setUserVisibleHint(isVisibleToUser); - if (isVisibleToUser && isStatusBarEnabled() && isLazyLoad()) { - // 重新初始化状态栏 - statusBarConfig().init(); - } - } - - @Override - public void onDestroy() { - super.onDestroy(); - if (mButterKnife != null) { - mButterKnife.unbind(); - } - EventBusManager.unregister(this); - } - - /** - * 设置标题栏的标题 - */ - public void setTitle(@StringRes int id) { - setTitle(getString(id)); - } - - /** - * 设置标题栏的标题 - */ - public void setTitle(CharSequence title) { - if (mTitleBar != null) { - mTitleBar.setTitle(title); - } else { - // 如果没有标题栏对象就直接设置给绑定的 Activity - getAttachActivity().setTitle(title); - } - } - - @Nullable - public TitleBar getTitleBar() { - if (getTitleId() > 0 && findViewById(getTitleId()) instanceof TitleBar) { - return findViewById(getTitleId()); - } - return null; - } - - /** - * 显示吐司 - */ - public void toast(CharSequence text) { - ToastUtils.show(text); - } - - public void toast(@StringRes int id) { - ToastUtils.show(id); - } - - public void toast(Object object) { - ToastUtils.show(object); - } - - /** - * 打印日志 - */ - public void log(Object object) { - if (BuildConfig.DEBUG) { - Log.v(getClass().getSimpleName(), object != null ? object.toString() : "null"); - } - } - - @Override - public void onResume() { - super.onResume(); - UmengClient.onResume(this); - } - - @Override - public void onPause() { - UmengClient.onPause(this); - super.onPause(); - } - - /** - * {@link OnTitleBarListener} - */ - - /** - * TitleBar 左边的View被点击了 - */ - @Override - public void onLeftClick(View v) {} - - /** - * TitleBar 中间的View被点击了 - */ - @Override - public void onTitleClick(View v) {} - - /** - * TitleBar 右边的View被点击了 - */ - @Override - public void onRightClick(View v) {} - - private final StatusManager mStatusManager = new StatusManager(); - - /** - * 显示加载中 - */ - public void showLoading() { - mStatusManager.showLoading(getAttachActivity()); - } - - public void showLoading(@StringRes int id) { - mStatusManager.showLoading(getAttachActivity(), getString(id)); - } - - public void showLoading(CharSequence text) { - mStatusManager.showLoading(getAttachActivity(), text); - } - - /** - * 显示加载完成 - */ - public void showComplete() { - mStatusManager.showComplete(); - } - - /** - * 显示空提示 - */ - public void showEmpty() { - mStatusManager.showEmpty(getView()); - } - - /** - * 显示错误提示 - */ - public void showError() { - mStatusManager.showError(getView()); - } - - /** - * 显示自定义提示 - */ - public void showLayout(@DrawableRes int drawableId, @StringRes int stringId) { - mStatusManager.showLayout(getView(), drawableId, stringId); - } - - public void showLayout(Drawable drawable, CharSequence hint) { - mStatusManager.showLayout(getView(), drawable, hint); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/common/MyListViewAdapter.java b/app/src/main/java/com/hjq/demo/common/MyListViewAdapter.java deleted file mode 100644 index fc882b2c..00000000 --- a/app/src/main/java/com/hjq/demo/common/MyListViewAdapter.java +++ /dev/null @@ -1,232 +0,0 @@ -package com.hjq.demo.common; - -import android.content.Context; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.LayoutRes; -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; - -import com.hjq.base.BaseListViewAdapter; -import com.hjq.toast.ToastUtils; - -import java.util.ArrayList; -import java.util.List; - -import butterknife.ButterKnife; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/10/18 - * desc : 项目中 ListView 适配器基类 - */ -public abstract class MyListViewAdapter extends BaseListViewAdapter { - - /** 列表数据 */ - private List mDataSet; - /** 当前列表的页码,默认为第一页,用于分页加载功能 */ - private int mPageNumber = 1; - /** 是否是最后一页,默认为false,用于分页加载功能 */ - private boolean mLastPage; - /** 标记对象 */ - private Object mTag; - - public MyListViewAdapter(Context context) { - super(context); - } - - @Override - public int getItemCount() { - return mDataSet == null ? 0 : mDataSet.size(); - } - - /** - * 设置新的数据 - */ - public void setData(List data) { - mDataSet = data; - notifyDataSetChanged(); - } - - /** - * 获取当前数据 - */ - @Nullable - public List getData() { - return mDataSet; - } - - /** - * 追加一些数据 - */ - public void addData(List data) { - if (mDataSet != null) { - mDataSet.addAll(data); - } else { - mDataSet = data; - } - notifyDataSetChanged(); - } - - /** - * 清空当前数据 - */ - public void clearData() { - //当前的数据不能为空 - if (mDataSet == null || mDataSet.size() == 0) { - return; - } - - mDataSet.clear(); - notifyDataSetChanged(); - } - - /** - * 获取某个位置上的数据 - */ - @Override - public T getItem(int position) { - return mDataSet.get(position); - } - - /** - * 更新某个位置上的数据 - */ - public void setItem(int position, T item) { - if (mDataSet == null) { - mDataSet = new ArrayList<>(); - } - mDataSet.set(position, item); - notifyDataSetChanged(); - } - - /** - * 添加单条数据 - */ - public void addItem(T item) { - if (mDataSet == null) { - mDataSet = new ArrayList<>(); - } - - addItem(mDataSet.size(), item); - } - - /** - * 添加单条数据 - */ - public void addItem(int position, T item) { - if (mDataSet == null) { - mDataSet = new ArrayList<>(); - } - - //如果是在for循环添加后要记得position++ - if (position < mDataSet.size()) { - mDataSet.add(position, item); - } else { - mDataSet.add(item); - } - notifyDataSetChanged(); - } - - /** - * 删除单条数据 - */ - public void removeItem(T item) { - int index = mDataSet.indexOf(item); - if (index != -1) { - removeItem(index); - } - } - - public void removeItem(int position) { - //如果是在for循环删除后要记得i-- - mDataSet.remove(position); - notifyDataSetChanged(); - } - - /** - * 获取当前的页码 - */ - public int getPageNumber() { - return mPageNumber; - } - - /** - * 设置当前的页码 - */ - public void setPageNumber(int pageNumber) { - mPageNumber = pageNumber; - } - - /** - * 当前是否为最后一页 - */ - public boolean isLastPage() { - return mLastPage; - } - - /** - * 设置是否为最后一页 - */ - public void setLastPage(boolean lastPage) { - mLastPage = lastPage; - } - - /** - * 获取标记 - */ - public Object getTag() { - return mTag; - } - - /** - * 设置标记 - */ - public void setTag(Object tag) { - mTag = tag; - } - - /** - * 显示吐司 - */ - public void toast(CharSequence text) { - ToastUtils.show(text); - } - - public void toast(@StringRes int id) { - ToastUtils.show(id); - } - - public void toast(Object object) { - ToastUtils.show(object); - } - - public abstract class ViewHolder extends BaseListViewAdapter.ViewHolder { - - public ViewHolder(ViewGroup parent, @LayoutRes int id) { - super(parent, id); - ButterKnife.bind(getItemView()); - } - - public ViewHolder(View itemView) { - super(itemView); - ButterKnife.bind(itemView); - } - } - - public class SimpleHolder extends ViewHolder { - - public SimpleHolder(ViewGroup parent, @LayoutRes int id) { - super(parent, id); - } - - public SimpleHolder(View itemView) { - super(itemView); - } - - @Override - public void onBindView(int position) {} - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/helper/ActivityStackManager.java b/app/src/main/java/com/hjq/demo/helper/ActivityStackManager.java index 8380f185..622004bf 100644 --- a/app/src/main/java/com/hjq/demo/helper/ActivityStackManager.java +++ b/app/src/main/java/com/hjq/demo/helper/ActivityStackManager.java @@ -2,7 +2,10 @@ import android.app.Activity; import android.app.Application; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.collection.ArrayMap; /** @@ -11,12 +14,14 @@ * time : 2018/11/18 * desc : Activity 栈管理 */ -public final class ActivityStackManager { +public final class ActivityStackManager implements Application.ActivityLifecycleCallbacks { private static volatile ActivityStackManager sInstance; private final ArrayMap mActivitySet = new ArrayMap<>(); + /** 当前应用上下文对象 */ + private Application mApplication; /** 当前 Activity 对象标记 */ private String mCurrentTag; @@ -34,11 +39,16 @@ public static ActivityStackManager getInstance() { return sInstance; } + public void init(Application application) { + mApplication = application; + application.registerActivityLifecycleCallbacks(this); + } + /** * 获取 Application 对象 */ public Application getApplication() { - return getTopActivity().getApplication(); + return mApplication; } /** @@ -82,30 +92,44 @@ public final void finishAllActivities(Class... classArray) { } /** - * Activity 同名方法回调 + * 获取一个对象的独立无二的标记 */ - public void onCreated(Activity activity) { + private static String getObjectTag(Object object) { + // 对象所在的包名 + 对象的内存地址 + return object.getClass().getName() + Integer.toHexString(object.hashCode()); + } + + @Override + public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) { mCurrentTag = getObjectTag(activity); mActivitySet.put(getObjectTag(activity), activity); } - /** - * Activity 同名方法回调 - */ - public void onDestroyed(Activity activity) { + @Override + public void onActivityStarted(@NonNull Activity activity) { + mCurrentTag = getObjectTag(activity); + } + + @Override + public void onActivityResumed(@NonNull Activity activity) { + mCurrentTag = getObjectTag(activity); + } + + @Override + public void onActivityPaused(@NonNull Activity activity) {} + + @Override + public void onActivityStopped(@NonNull Activity activity) {} + + @Override + public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {} + + @Override + public void onActivityDestroyed(@NonNull Activity activity) { mActivitySet.remove(getObjectTag(activity)); - // 如果当前的 Activity 是最后一个的话 if (getObjectTag(activity).equals(mCurrentTag)) { // 清除当前标记 mCurrentTag = null; } } - - /** - * 获取一个对象的独立无二的标记 - */ - private static String getObjectTag(Object object) { - // 对象所在的包名 + 对象的内存地址 - return object.getClass().getName() + Integer.toHexString(object.hashCode()); - } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/helper/CacheDataManager.java b/app/src/main/java/com/hjq/demo/helper/CacheDataManager.java index 68fee3e9..19d5836b 100644 --- a/app/src/main/java/com/hjq/demo/helper/CacheDataManager.java +++ b/app/src/main/java/com/hjq/demo/helper/CacheDataManager.java @@ -43,10 +43,7 @@ private static boolean deleteDir(File dir) { if (dir.isDirectory()) { String[] children = dir.list(); for (String child : children) { - boolean success = deleteDir(new File(dir, child)); - if (!success) { - return false; - } + deleteDir(new File(dir, child)); } } else { return dir.delete(); diff --git a/app/src/main/java/com/hjq/demo/helper/InputTextHelper.java b/app/src/main/java/com/hjq/demo/helper/InputTextHelper.java index e12d2ce8..49cf53ad 100644 --- a/app/src/main/java/com/hjq/demo/helper/InputTextHelper.java +++ b/app/src/main/java/com/hjq/demo/helper/InputTextHelper.java @@ -41,7 +41,7 @@ public final class InputTextHelper implements TextWatcher { */ private InputTextHelper(View view, boolean alpha) { if (view == null) { - throw new IllegalArgumentException("The view is empty"); + throw new IllegalArgumentException("are you ok?"); } mView = view; isAlpha = alpha; @@ -214,8 +214,6 @@ public static final class Builder { private boolean isAlpha; /** TextView集合 */ private final List mViewSet = new ArrayList<>(); - /** 文本输入辅助类 */ - private InputTextHelper mTextHelper; /** 文本 */ private OnInputTextListener mListener; @@ -244,11 +242,12 @@ public Builder setListener(OnInputTextListener listener) { } public InputTextHelper build(){ - mTextHelper = new InputTextHelper(mView, isAlpha); - mTextHelper.addViews(mViewSet); - mTextHelper.setListener(mListener); - mActivity.getApplication().registerActivityLifecycleCallbacks(new TextInputLifecycle(mActivity, mTextHelper)); - return mTextHelper; + /** 文本输入辅助类 */ + InputTextHelper helper = new InputTextHelper(mView, isAlpha); + helper.addViews(mViewSet); + helper.setListener(mListener); + mActivity.getApplication().registerActivityLifecycleCallbacks(new TextInputLifecycle(mActivity, helper)); + return helper; } } diff --git a/app/src/main/java/com/hjq/demo/helper/ManifestHelper.java b/app/src/main/java/com/hjq/demo/helper/ManifestHelper.java deleted file mode 100644 index 59215aa5..00000000 --- a/app/src/main/java/com/hjq/demo/helper/ManifestHelper.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.hjq.demo.helper; - -import android.content.Context; -import android.content.pm.PackageManager; -import android.os.Bundle; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2019/04/02 - * desc : 获取清单文件中的值 - */ -public final class ManifestHelper { - - /** - * 获取 meta-data 的值 - */ - private static Bundle getMetaData(Context context) { - try { - return context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA).metaData; - } catch (PackageManager.NameNotFoundException ignored) { - return new Bundle(); - } - } - - /** - * 检查 key - * @param context 上下文 - * @return meta-data - */ - public static Bundle checkMetaData(Context context, String key) { - Bundle metaData = getMetaData(context); - if (!metaData.containsKey(key)) { - // 清单文件没有设置这个 key - throw new IllegalArgumentException("are you ok?"); - } - return metaData; - } - - /** - * 是否有设置这个值 - * - * @param context 上下文 - * @param key key - * @return value - */ - public static boolean contains(Context context, String key) { - return checkMetaData(context, key).containsKey(key); - } - - /** - * 获取 Object - * - * @param context 上下文 - * @param key key - * @return value - */ - public static Object get(Context context, String key) { - return checkMetaData(context, key).get(key); - } - - /** - * 获取 String - * - * @param context 上下文 - * @param key key - * @return value - */ - public static String getString(Context context, String key) { - return checkMetaData(context, key).getString(key); - } - - /** - * 获取 boolean - * - * @param context 上下文 - * @param key key - * @return value - */ - public static boolean getBoolean(Context context, String key) { - return checkMetaData(context, key).getBoolean(key); - } - - /** - * 获取 int - * - * @param context 上下文 - * @param key key - * @return value - */ - public static int getInt(Context context, String key) { - return checkMetaData(context, key).getInt(key); - } - - /** - * 获取 long - * - * @param context 上下文 - * @param key key - * @return value - */ - public static long getLong(Context context, String key) { - return checkMetaData(context, key).getLong(key); - } - - /** - * 获取 float - * - * @param context 上下文 - * @param key key - * @return value - */ - public static float getFloat(Context context, String key) { - return checkMetaData(context, key).getFloat(key); - } - - /** - * 获取 double - * - * @param context 上下文 - * @param key key - * @return value - */ - public static double getDouble(Context context, String key) { - return checkMetaData(context, key).getDouble(key); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/helper/PopupWindowHelper.java b/app/src/main/java/com/hjq/demo/helper/PopupWindowHelper.java deleted file mode 100644 index 25931a6b..00000000 --- a/app/src/main/java/com/hjq/demo/helper/PopupWindowHelper.java +++ /dev/null @@ -1,128 +0,0 @@ -package com.hjq.demo.helper; - -import android.content.Context; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.Animation; -import android.view.animation.ScaleAnimation; -import android.widget.PopupWindow; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/10/18 - * desc : PopupWindow辅助类 - */ -public final class PopupWindowHelper implements PopupWindow.OnDismissListener { - - /** PopupWindow 对象 */ - private PopupWindow mPopupWindow; - /** PopupWindow 显示的 View */ - private final View mPopupView; - /** 记录PopupWindow销毁时间 */ - private long mDismissTime; - - public PopupWindowHelper(View popupView) { - mPopupView = popupView; - } - - public PopupWindowHelper(Context context, int id) { - mPopupView = View.inflate(context, id, null); - } - - /** - * 初始化PopupWindow - */ - private void initPopupWindow() { - // 给PopupWindow的View设置缩放动画 - ScaleAnimation sa = new ScaleAnimation(0.5f, 1.0f, 0.5f, 1.0f, - Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); - sa.setDuration(200); - mPopupView.startAnimation(sa); - - mPopupWindow = new PopupWindow(mPopupView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - // 需要在popupWindow使用动画,必须先设置背景,否则动画不能显示出效果,为了不和当前的背景冲突,这里设置全透明背景的图片 - mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); - // 设置外部可触的,点击其他地方会自动消失 - mPopupWindow.setTouchable(true); - mPopupWindow.setFocusable(true); - mPopupWindow.setOutsideTouchable(true); - // 监听PopupWindow销毁监听 - mPopupWindow.setOnDismissListener(this); - } - - /** - * {@link PopupWindow.OnDismissListener} - */ - @Override - public void onDismiss() { - // 记录当前销毁的时间 - mDismissTime = System.currentTimeMillis(); - } - - /** - * 显示一个PopupWindow - * - * @param clickView PopupWindow显示在什么View的下方 - */ - public void show(View clickView) { - - // 如果PopupWindow还未初始化就先进行初始化 - if (mPopupWindow == null) { - initPopupWindow(); - } - - // 避免用户点击clickView导致的销毁后再次显示的Bug - if (System.currentTimeMillis() - mDismissTime < 500) { - return; - } - - /* - //获取某个view对象在窗口的位置,然后计算出PopupWindow的位置 - int[] location = new int[2]; - mClickView.getLocationInWindow(location); - - //将PopupWindow显示出来 - mPopupWindow.showAtLocation(mParentView, Gravity.LEFT + Gravity.TOP, 0, location[1] + mClickView.getHeight()); - */ - - mPopupWindow.showAsDropDown(clickView); - } - - /** - * 销毁当前的PopupWindow - */ - public void dismiss() { - if (isShowing()) { - mPopupWindow.dismiss(); - } - } - - /** - * 当前PopupWindow是否已经显示 - */ - public boolean isShowing() { - return mPopupWindow != null && mPopupWindow.isShowing(); - } - - /** - * 获取当前的PopupWindow对象 - */ - @Nullable - public PopupWindow getPopupWindow() { - return mPopupWindow; - } - - /** - * 获取当前的PopupWindow的View对象 - */ - @NonNull - public View getPopupView() { - return mPopupView; - } -} diff --git a/app/src/main/java/com/hjq/demo/helper/RadioButtonGroupHelper.java b/app/src/main/java/com/hjq/demo/helper/RadioButtonGroupHelper.java index f561647a..0decf929 100644 --- a/app/src/main/java/com/hjq/demo/helper/RadioButtonGroupHelper.java +++ b/app/src/main/java/com/hjq/demo/helper/RadioButtonGroupHelper.java @@ -29,7 +29,7 @@ public RadioButtonGroupHelper(RadioButton... groups) { for (RadioButton view : groups) { // 如果这个RadioButton没有设置id的话 if (view.getId() == View.NO_ID) { - throw new IllegalArgumentException("The resource id must be set for the RadioButton"); + throw new IllegalArgumentException("are you ok?"); } view.setOnCheckedChangeListener(this); mViewSet.add(view); diff --git a/app/src/main/java/com/hjq/demo/helper/WebViewLifecycleUtils.java b/app/src/main/java/com/hjq/demo/helper/WebViewLifecycleUtils.java deleted file mode 100644 index 30e7cbb9..00000000 --- a/app/src/main/java/com/hjq/demo/helper/WebViewLifecycleUtils.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.hjq.demo.helper; - -import android.view.ViewGroup; -import android.webkit.WebView; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/10/18 - * desc : WebView 生命周期优化工具 - */ -public final class WebViewLifecycleUtils { - - public static void onResume(WebView webView) { - webView.onResume(); - webView.resumeTimers(); - } - - public static void onPause(WebView webView) { - webView.onPause(); - webView.pauseTimers(); - } - - public static void onDestroy(WebView webView) { - ((ViewGroup) webView.getParent()).removeView(webView); - //清除历史记录 - webView.clearHistory(); - //停止加载 - webView.stopLoading(); - //加载一个空白页 - webView.loadUrl("about:blank"); - webView.setWebChromeClient(null); - webView.setWebViewClient(null); - //移除WebView所有的View对象 - webView.removeAllViews(); - //销毁此的WebView的内部状态 - webView.destroy(); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/http/glide/GlideConfig.java b/app/src/main/java/com/hjq/demo/http/glide/GlideConfig.java new file mode 100644 index 00000000..990bae9b --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/glide/GlideConfig.java @@ -0,0 +1,77 @@ +package com.hjq.demo.http.glide; + +import android.content.Context; + +import androidx.annotation.NonNull; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.GlideBuilder; +import com.bumptech.glide.Registry; +import com.bumptech.glide.annotation.GlideModule; +import com.bumptech.glide.load.engine.bitmap_recycle.LruBitmapPool; +import com.bumptech.glide.load.engine.cache.DiskLruCacheWrapper; +import com.bumptech.glide.load.engine.cache.LruResourceCache; +import com.bumptech.glide.load.engine.cache.MemorySizeCalculator; +import com.bumptech.glide.load.model.GlideUrl; +import com.bumptech.glide.module.AppGlideModule; +import com.bumptech.glide.request.RequestOptions; +import com.hjq.demo.R; +import com.hjq.http.EasyConfig; + +import java.io.File; +import java.io.InputStream; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/15 + * desc : Glide 全局配置 + */ +@GlideModule +public class GlideConfig extends AppGlideModule { + + /** 本地图片缓存文件最大值 */ + private static final int IMAGE_DISK_CACHE_MAX_SIZE = 300 * 1024 * 1024; + + @SuppressWarnings("ResultOfMethodCallIgnored") + @Override + public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) { + // 读写外部缓存目录不需要申请存储权限 + File diskCacheFile = new File(context.getCacheDir(), "glide"); + // 如果这个路径是一个文件 + if (diskCacheFile.exists() && diskCacheFile.isFile()) { + // 执行删除操作 + diskCacheFile.delete(); + } + // 如果这个路径不存在 + if (!diskCacheFile.exists()) { + // 创建多级目录 + diskCacheFile.mkdirs(); + } + builder.setDiskCache(() -> DiskLruCacheWrapper.create(diskCacheFile, IMAGE_DISK_CACHE_MAX_SIZE)); + + MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context).build(); + int defaultMemoryCacheSize = calculator.getMemoryCacheSize(); + int defaultBitmapPoolSize = calculator.getBitmapPoolSize(); + + int customMemoryCacheSize = (int) (1.2 * defaultMemoryCacheSize); + int customBitmapPoolSize = (int) (1.2 * defaultBitmapPoolSize); + + builder.setMemoryCache(new LruResourceCache(customMemoryCacheSize)); + builder.setBitmapPool(new LruBitmapPool(customBitmapPoolSize)); + + // 设置默认的加载占位图和加载出错图 + builder.setDefaultRequestOptions(new RequestOptions().placeholder(R.drawable.image_loading).error(R.drawable.image_load_err)); + } + + @Override + public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) { + // Glide 默认采用的是 HttpURLConnection 来做网络请求,这里切换成更高效的 OkHttp + registry.replace(GlideUrl.class, InputStream.class, new OkHttpLoader.Factory(EasyConfig.getInstance().getClient())); + } + + @Override + public boolean isManifestParsingEnabled() { + return false; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/http/glide/OkHttpFetcher.java b/app/src/main/java/com/hjq/demo/http/glide/OkHttpFetcher.java new file mode 100644 index 00000000..4708f057 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/glide/OkHttpFetcher.java @@ -0,0 +1,107 @@ +package com.hjq.demo.http.glide; + +import androidx.annotation.NonNull; + +import com.bumptech.glide.Priority; +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.HttpException; +import com.bumptech.glide.load.data.DataFetcher; +import com.bumptech.glide.load.model.GlideUrl; +import com.bumptech.glide.util.ContentLengthInputStream; +import com.bumptech.glide.util.Preconditions; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/15 + * desc : OkHttp 加载器 + */ +public class OkHttpFetcher implements DataFetcher, Callback { + + private final Call.Factory mCallFactory; + private final GlideUrl mGlideUrl; + private InputStream mInputStream; + private ResponseBody mResponseBody; + private DataCallback mDataCallback; + private volatile Call mCall; + + OkHttpFetcher(Call.Factory factory, GlideUrl url) { + mCallFactory = factory; + mGlideUrl = url; + } + + @Override + public void loadData(@NonNull Priority priority, + @NonNull final DataFetcher.DataCallback callback) { + Request.Builder requestBuilder = new Request.Builder().url(mGlideUrl.toStringUrl()); + for (Map.Entry headerEntry : mGlideUrl.getHeaders().entrySet()) { + String key = headerEntry.getKey(); + requestBuilder.addHeader(key, headerEntry.getValue()); + } + Request request = requestBuilder.build(); + mDataCallback = callback; + + mCall = mCallFactory.newCall(request); + mCall.enqueue(this); + } + + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + mDataCallback.onLoadFailed(e); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + mResponseBody = response.body(); + if (response.isSuccessful()) { + long contentLength = Preconditions.checkNotNull(mResponseBody).contentLength(); + mInputStream = ContentLengthInputStream.obtain(mResponseBody.byteStream(), contentLength); + mDataCallback.onDataReady(mInputStream); + } else { + mDataCallback.onLoadFailed(new HttpException(response.message(), response.code())); + } + } + + @Override + public void cleanup() { + try { + if (mInputStream != null) { + mInputStream.close(); + } + } catch (IOException ignored) {} + + if (mResponseBody != null) { + mResponseBody.close(); + } + mDataCallback = null; + } + + @Override + public void cancel() { + if (mCall != null) { + mCall.cancel(); + } + } + + @NonNull + @Override + public Class getDataClass() { + return InputStream.class; + } + + @NonNull + @Override + public DataSource getDataSource() { + return DataSource.REMOTE; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/http/glide/OkHttpLoader.java b/app/src/main/java/com/hjq/demo/http/glide/OkHttpLoader.java new file mode 100644 index 00000000..300ab61f --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/glide/OkHttpLoader.java @@ -0,0 +1,57 @@ +package com.hjq.demo.http.glide; + +import androidx.annotation.NonNull; + +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.model.GlideUrl; +import com.bumptech.glide.load.model.ModelLoader; +import com.bumptech.glide.load.model.ModelLoaderFactory; +import com.bumptech.glide.load.model.MultiModelLoaderFactory; + +import java.io.InputStream; + +import okhttp3.Call; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/15 + * desc : OkHttp 加载模型 + */ +public class OkHttpLoader implements ModelLoader { + + private final Call.Factory mFactory; + + private OkHttpLoader(@NonNull Call.Factory factory) { + mFactory = factory; + } + + @Override + public boolean handles(@NonNull GlideUrl url) { + return true; + } + + @Override + public LoadData buildLoadData(@NonNull GlideUrl model, int width, int height, + @NonNull Options options) { + return new LoadData<>(model, new OkHttpFetcher(mFactory, model)); + } + + public static class Factory implements ModelLoaderFactory { + + private final Call.Factory mFactory; + + Factory(@NonNull Call.Factory factory) { + mFactory = factory; + } + + @NonNull + @Override + public ModelLoader build(@NonNull MultiModelLoaderFactory multiFactory) { + return new OkHttpLoader(mFactory); + } + + @Override + public void teardown() {} + } +} diff --git a/app/src/main/java/com/hjq/demo/http/model/HttpData.java b/app/src/main/java/com/hjq/demo/http/model/HttpData.java new file mode 100644 index 00000000..08199f4f --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/model/HttpData.java @@ -0,0 +1,29 @@ +package com.hjq.demo.http.model; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/07 + * desc : 统一接口数据结构 + */ +public class HttpData { + + /** 返回码 */ + private int code; + /** 提示语 */ + private String msg; + /** 数据 */ + private T data; + + public int getCode() { + return code; + } + + public String getMessage() { + return msg; + } + + public T getData() { + return data; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/http/model/RequestHandler.java b/app/src/main/java/com/hjq/demo/http/model/RequestHandler.java new file mode 100644 index 00000000..8b005940 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/model/RequestHandler.java @@ -0,0 +1,164 @@ +package com.hjq.demo.http.model; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import com.hjq.demo.R; +import com.hjq.demo.helper.ActivityStackManager; +import com.hjq.demo.ui.activity.LoginActivity; +import com.hjq.http.EasyLog; +import com.hjq.http.config.IRequestHandler; +import com.hjq.http.exception.CancelException; +import com.hjq.http.exception.DataException; +import com.hjq.http.exception.HttpException; +import com.hjq.http.exception.NetworkException; +import com.hjq.http.exception.ResponseException; +import com.hjq.http.exception.ResultException; +import com.hjq.http.exception.ServerException; +import com.hjq.http.exception.TimeoutException; +import com.hjq.http.exception.TokenException; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; + +import okhttp3.Response; +import okhttp3.ResponseBody; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/07 + * desc : 请求处理类 + */ +public final class RequestHandler implements IRequestHandler { + + private static final Gson GSON = new Gson(); + + @Override + public Object requestSucceed(Context context, Response response, Type type) throws Exception { + if (!response.isSuccessful()) { + // 返回响应异常 + throw new ResponseException(context.getString(R.string.http_server_error), response); + } + + ResponseBody body = response.body(); + if (body == null) { + return null; + } + + if (Response.class.equals(type)) { + return response; + } + + if (Bitmap.class.equals(type)) { + // 如果这是一个 Bitmap 对象 + return BitmapFactory.decodeStream(body.byteStream()); + } + + String text; + try { + text = body.string(); + } catch (IOException e) { + // 返回结果读取异常 + throw new DataException(context.getString(R.string.http_data_explain_error), e); + } + + // 打印这个 Json + EasyLog.json(text); + + final Object result; + if (String.class.equals(type)) { + // 如果这是一个 String 对象 + result = text; + } else if (JSONObject.class.equals(type)) { + try { + // 如果这是一个 JSONObject 对象 + result = new JSONObject(text); + } catch (JSONException e) { + throw new DataException(context.getString(R.string.http_data_explain_error), e); + } + } else if (JSONArray.class.equals(type)) { + try { + // 如果这是一个 JSONArray 对象 + result = new JSONArray(text); + }catch (JSONException e) { + throw new DataException(context.getString(R.string.http_data_explain_error), e); + } + } else { + + try { + result = GSON.fromJson(text, type); + } catch (JsonSyntaxException e) { + // 返回结果读取异常 + throw new DataException(context.getString(R.string.http_data_explain_error), e); + } + + if (result instanceof HttpData) { + HttpData model = (HttpData) result; + if (model.getCode() == 0) { + // 代表执行成功 + return result; + } else if (model.getCode() == 1001) { + // 代表登录失效,需要重新登录 + throw new TokenException(context.getString(R.string.http_account_error)); + } else { + // 代表执行失败 + throw new ResultException(model.getMessage(), model); + } + } + } + return result; + } + + @Override + public Exception requestFail(Context context, Exception e) { + // 判断这个异常是不是自己抛的 + if (e instanceof HttpException) { + if (e instanceof TokenException) { + // 登录信息失效,跳转到登录页 + if (context instanceof Activity) { + Intent intent = new Intent(context, LoginActivity.class); + context.startActivity(intent); + } + // 进行内存优化,销毁除登录页之外的所有界面 + ActivityStackManager.getInstance().finishAllActivities(LoginActivity.class); + } + } else { + if (e instanceof SocketTimeoutException) { + e = new TimeoutException(context.getString(R.string.http_server_out_time), e); + } else if (e instanceof UnknownHostException) { + NetworkInfo info = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo(); + // 判断网络是否连接 + if (info != null && info.isConnected()) { + // 有连接就是服务器的问题 + e = new ServerException(context.getString(R.string.http_server_error), e); + } else { + // 没有连接就是网络异常 + e = new NetworkException(context.getString(R.string.http_network_error), e); + } + } else if (e instanceof IOException) { + //e = new CancelException(context.getString(R.string.http_request_cancel), e); + e = new CancelException("", e); + }else { + e = new HttpException(e.getMessage(), e); + } + } + + // 打印错误信息 + EasyLog.print(e); + return e; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/http/request/CopyApi.java b/app/src/main/java/com/hjq/demo/http/request/CopyApi.java new file mode 100644 index 00000000..6024314a --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/request/CopyApi.java @@ -0,0 +1,17 @@ +package com.hjq.demo.http.request; + +import com.hjq.http.config.IRequestApi; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/07 + * desc : 可进行拷贝的副本 + */ +public class CopyApi implements IRequestApi { + + @Override + public String getApi() { + return ""; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/http/request/GetCodeApi.java b/app/src/main/java/com/hjq/demo/http/request/GetCodeApi.java new file mode 100644 index 00000000..7ace0ac5 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/request/GetCodeApi.java @@ -0,0 +1,25 @@ +package com.hjq.demo.http.request; + +import com.hjq.http.config.IRequestApi; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/07 + * desc : 获取验证码 + */ +public class GetCodeApi implements IRequestApi { + + @Override + public String getApi() { + return "code/get"; + } + + /** 手机号 */ + private String phone; + + public GetCodeApi setPhone(String phone) { + this.phone = phone; + return this; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/http/request/LoginApi.java b/app/src/main/java/com/hjq/demo/http/request/LoginApi.java new file mode 100644 index 00000000..9e78b5c5 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/request/LoginApi.java @@ -0,0 +1,32 @@ +package com.hjq.demo.http.request; + +import com.hjq.http.config.IRequestApi; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/07 + * desc : 用户登录 + */ +public class LoginApi implements IRequestApi { + + @Override + public String getApi() { + return "user/login"; + } + + /** 手机号 */ + private String phone; + /** 登录密码 */ + private String password; + + public LoginApi setPhone(String phone) { + this.phone = phone; + return this; + } + + public LoginApi setPassword(String password) { + this.password = password; + return this; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/http/request/LogoutApi.java b/app/src/main/java/com/hjq/demo/http/request/LogoutApi.java new file mode 100644 index 00000000..19375a86 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/request/LogoutApi.java @@ -0,0 +1,17 @@ +package com.hjq.demo.http.request; + +import com.hjq.http.config.IRequestApi; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/07 + * desc : 退出登录 + */ +public class LogoutApi implements IRequestApi { + + @Override + public String getApi() { + return "user/logout"; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/http/request/PasswordApi.java b/app/src/main/java/com/hjq/demo/http/request/PasswordApi.java new file mode 100644 index 00000000..4f15bbe4 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/request/PasswordApi.java @@ -0,0 +1,39 @@ +package com.hjq.demo.http.request; + +import com.hjq.http.config.IRequestApi; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/07 + * desc : 修改密码 + */ +public class PasswordApi implements IRequestApi { + + @Override + public String getApi() { + return "user/password"; + } + + /** 手机号(已登录可不传) */ + private String phone; + /** 验证码 */ + private String code; + /** 密码 */ + private String password; + + public PasswordApi setPhone(String phone) { + this.phone = phone; + return this; + } + + public PasswordApi setCode(String code) { + this.code = code; + return this; + } + + public PasswordApi setPassword(String password) { + this.password = password; + return this; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/http/request/PhoneApi.java b/app/src/main/java/com/hjq/demo/http/request/PhoneApi.java new file mode 100644 index 00000000..3e0663fe --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/request/PhoneApi.java @@ -0,0 +1,40 @@ +package com.hjq.demo.http.request; + +import com.hjq.http.config.IRequestApi; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/07 + * desc : 修改手机 + */ +public class PhoneApi implements IRequestApi { + + @Override + public String getApi() { + return "user/phone"; + } + + /** 旧手机号验证码(没有绑定情况下可不传) */ + private String preCode; + + /** 新手机号 */ + private String phone; + /** 新手机号验证码 */ + private String code; + + public PhoneApi setPreCode(String preCode) { + this.preCode = preCode; + return this; + } + + public PhoneApi setPhone(String phone) { + this.phone = phone; + return this; + } + + public PhoneApi setCode(String code) { + this.code = code; + return this; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/http/request/RegisterApi.java b/app/src/main/java/com/hjq/demo/http/request/RegisterApi.java new file mode 100644 index 00000000..17930a72 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/request/RegisterApi.java @@ -0,0 +1,39 @@ +package com.hjq.demo.http.request; + +import com.hjq.http.config.IRequestApi; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/07 + * desc : 用户注册 + */ +public class RegisterApi implements IRequestApi { + + @Override + public String getApi() { + return "user/register"; + } + + /** 手机号 */ + private String phone; + /** 验证码 */ + private String code; + /** 密码 */ + private String password; + + public RegisterApi setPhone(String phone) { + this.phone = phone; + return this; + } + + public RegisterApi setCode(String code) { + this.code = code; + return this; + } + + public RegisterApi setPassword(String password) { + this.password = password; + return this; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/http/request/UpdateImageApi.java b/app/src/main/java/com/hjq/demo/http/request/UpdateImageApi.java new file mode 100644 index 00000000..7d6e22dc --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/request/UpdateImageApi.java @@ -0,0 +1,27 @@ +package com.hjq.demo.http.request; + +import com.hjq.http.config.IRequestApi; + +import java.io.File; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/07 + * desc : 上传图片 + */ +public class UpdateImageApi implements IRequestApi { + + @Override + public String getApi() { + return "update/image"; + } + + /** 图片文件 */ + private File image; + + public UpdateImageApi setImage(File image) { + this.image = image; + return this; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/http/request/UserInfoApi.java b/app/src/main/java/com/hjq/demo/http/request/UserInfoApi.java new file mode 100644 index 00000000..23813411 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/request/UserInfoApi.java @@ -0,0 +1,17 @@ +package com.hjq.demo.http.request; + +import com.hjq.http.config.IRequestApi; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/07 + * desc : 获取用户信息 + */ +public class UserInfoApi implements IRequestApi { + + @Override + public String getApi() { + return "user/info"; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/http/request/VerifyCodeApi.java b/app/src/main/java/com/hjq/demo/http/request/VerifyCodeApi.java new file mode 100644 index 00000000..00ffde1d --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/request/VerifyCodeApi.java @@ -0,0 +1,32 @@ +package com.hjq.demo.http.request; + +import com.hjq.http.config.IRequestApi; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/07 + * desc : 验证码校验 + */ +public class VerifyCodeApi implements IRequestApi { + + @Override + public String getApi() { + return "code/checkout"; + } + + /** 手机号 */ + private String phone; + /** 验证码 */ + private String code; + + public VerifyCodeApi setPhone(String phone) { + this.phone = phone; + return this; + } + + public VerifyCodeApi setCode(String code) { + this.code = code; + return this; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/http/response/CopyBean.java b/app/src/main/java/com/hjq/demo/http/response/CopyBean.java new file mode 100644 index 00000000..e439f4c9 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/response/CopyBean.java @@ -0,0 +1,11 @@ +package com.hjq.demo.http.response; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/07 + * desc : 可进行拷贝的副本 + */ +public class CopyBean { + +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/http/response/LoginBean.java b/app/src/main/java/com/hjq/demo/http/response/LoginBean.java new file mode 100644 index 00000000..96adc612 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/response/LoginBean.java @@ -0,0 +1,16 @@ +package com.hjq.demo.http.response; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/07 + * desc : 登录返回 + */ +public class LoginBean { + + private String token; + + public String getToken() { + return token; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/http/response/RegisterBean.java b/app/src/main/java/com/hjq/demo/http/response/RegisterBean.java new file mode 100644 index 00000000..ca9cb66f --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/response/RegisterBean.java @@ -0,0 +1,11 @@ +package com.hjq.demo.http.response; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/07 + * desc : 注册返回 + */ +public class RegisterBean { + +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/http/response/UserInfoBean.java b/app/src/main/java/com/hjq/demo/http/response/UserInfoBean.java new file mode 100644 index 00000000..fab07011 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/response/UserInfoBean.java @@ -0,0 +1,11 @@ +package com.hjq.demo.http.response; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/07 + * desc : 用户信息 + */ +public class UserInfoBean { + +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/http/server/ReleaseServer.java b/app/src/main/java/com/hjq/demo/http/server/ReleaseServer.java new file mode 100644 index 00000000..e93ebca8 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/server/ReleaseServer.java @@ -0,0 +1,22 @@ +package com.hjq.demo.http.server; + +import com.hjq.http.config.IRequestServer; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/07 + * desc : 正式环境 + */ +public class ReleaseServer implements IRequestServer { + + @Override + public String getHost() { + return "https://www.baidu.com/"; + } + + @Override + public String getPath() { + return "api/"; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/http/server/TestServer.java b/app/src/main/java/com/hjq/demo/http/server/TestServer.java new file mode 100644 index 00000000..6a67a3c8 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/http/server/TestServer.java @@ -0,0 +1,15 @@ +package com.hjq.demo.http.server; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/07 + * desc : 测试环境 + */ +public class TestServer extends ReleaseServer { + + @Override + public String getHost() { + return "https://www.baidu.com/"; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/mvp/IMvpView.java b/app/src/main/java/com/hjq/demo/mvp/IMvpView.java deleted file mode 100644 index bc45af0f..00000000 --- a/app/src/main/java/com/hjq/demo/mvp/IMvpView.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.hjq.demo.mvp; - -import android.content.Context; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/11/17 - * desc : MVP 通用性接口 - */ -public interface IMvpView { - - /** - * 获取上下文对象 - */ - Context getContext(); - - /** - * 加载中 - */ - void onLoading(); - - /** - * 加载完成 - */ - void onComplete(); - - /** - * 用于请求的数据为空的状态 - */ - void onEmpty(); - - /** - * 用于请求数据出错 - */ - void onError(); -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/mvp/MvpActivity.java b/app/src/main/java/com/hjq/demo/mvp/MvpActivity.java deleted file mode 100644 index f4c95f73..00000000 --- a/app/src/main/java/com/hjq/demo/mvp/MvpActivity.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.hjq.demo.mvp; - -import android.content.Context; - -import com.hjq.demo.common.MyActivity; -import com.hjq.demo.mvp.proxy.IMvpPresenterProxy; -import com.hjq.demo.mvp.proxy.MvpPresenterProxyImpl; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/11/17 - * desc : MVP Activity 基类 - */ -public abstract class MvpActivity extends MyActivity implements IMvpView { - - private IMvpPresenterProxy mMvpProxy; - - @Override - public void initActivity() { - mMvpProxy = createPresenterProxy(); - mMvpProxy.bindPresenter(); - super.initActivity(); - } - - protected IMvpPresenterProxy createPresenterProxy() { - return new MvpPresenterProxyImpl(this); - } - - @Override - protected void onDestroy() { - mMvpProxy.unbindPresenter(); - super.onDestroy(); - } - - @Override - public Context getContext() { - return this; - } - - @Override - public void onLoading() { - showLoading(); - } - - @Override - public void onComplete() { - showComplete(); - } - - @Override - public void onEmpty() { - showEmpty(); - } - - @Override - public void onError() { - showError(); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/mvp/MvpLazyFragment.java b/app/src/main/java/com/hjq/demo/mvp/MvpLazyFragment.java deleted file mode 100644 index 981be9ac..00000000 --- a/app/src/main/java/com/hjq/demo/mvp/MvpLazyFragment.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.hjq.demo.mvp; - -import com.hjq.demo.common.MyLazyFragment; -import com.hjq.demo.mvp.proxy.IMvpPresenterProxy; -import com.hjq.demo.mvp.proxy.MvpPresenterProxyImpl; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/11/17 - * desc : MVP 懒加载 Fragment 基类 - */ -public abstract class MvpLazyFragment extends MyLazyFragment implements IMvpView { - - private IMvpPresenterProxy mMvpProxy; - - @Override - protected void initFragment() { - mMvpProxy = createPresenterProxy(); - mMvpProxy.bindPresenter(); - super.initFragment(); - } - - protected IMvpPresenterProxy createPresenterProxy() { - return new MvpPresenterProxyImpl(this); - } - - @Override - public void onDestroy() { - if (mMvpProxy != null) { - mMvpProxy.unbindPresenter(); - } - super.onDestroy(); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/mvp/MvpModel.java b/app/src/main/java/com/hjq/demo/mvp/MvpModel.java deleted file mode 100644 index 029eab4e..00000000 --- a/app/src/main/java/com/hjq/demo/mvp/MvpModel.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.hjq.demo.mvp; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/11/17 - * desc : MVP 模型基类 - */ -public abstract class MvpModel { - - private L mListener; - - public void setListener(L listener) { - mListener = listener; - } - - public L getListener() { - return mListener; - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/mvp/MvpPresenter.java b/app/src/main/java/com/hjq/demo/mvp/MvpPresenter.java deleted file mode 100644 index 6060e42e..00000000 --- a/app/src/main/java/com/hjq/demo/mvp/MvpPresenter.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.hjq.demo.mvp; - -import android.content.Context; - -import androidx.annotation.StringRes; - -import com.hjq.demo.mvp.proxy.IMvpModelProxy; -import com.hjq.demo.mvp.proxy.MvpModelProxyImpl; -import com.hjq.toast.ToastUtils; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/11/17 - * desc : MVP 业务基类 - */ -public abstract class MvpPresenter implements InvocationHandler { - - /** View 层 */ - private V mView; - - /** 代理对象 */ - private V mProxyView; - - private IMvpModelProxy mMvpProxy; - - protected IMvpModelProxy createModelProxyImpl() { - return new MvpModelProxyImpl(this); - } - - @SuppressWarnings("unchecked") - public void attachView(V view) { - mView = view; - // 使用动态代理,解决 getView 方法可能为空的问题 - mProxyView = (V) Proxy.newProxyInstance(view.getClass().getClassLoader(), view.getClass().getInterfaces(), this); - // V 层解绑了 P 层,那么 getView 就为空,调用 V 层就会发生空指针异常 - // 如果在 P 层的每个子类中都进行 getView() != null 防空判断会导致开发成本非常高,并且容易出现遗漏 - mMvpProxy = createModelProxyImpl(); - mMvpProxy.bindModel(); - } - - /** - * 动态代理接口,每次调用了代理对象的方法最终也会回到到这里 - * - * {@link InvocationHandler} - */ - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - // 如果当前还是绑定状态就执行 View 的方法,否则就不执行 - return isAttached() ? method.invoke(mView, args) : null; - } - - public void detachView() { - mView = null; - // 这里注意不能把代理对象置空 - // mProxyView = null; - mMvpProxy.unbindModel(); - } - - public boolean isAttached() { - return mProxyView != null && mView != null; - } - - public V getView() { - return mProxyView; - } - - /** - * 获取上下文 - */ - public Context getContext() { - return getView().getContext(); - } - - /** - * 显示吐司 - */ - public void toast(CharSequence text) { - ToastUtils.show(text); - } - - public void toast(@StringRes int id) { - ToastUtils.show(id); - } - - public void toast(Object object) { - ToastUtils.show(object); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/mvp/copy/CopyContract.java b/app/src/main/java/com/hjq/demo/mvp/copy/CopyContract.java deleted file mode 100644 index 885760b1..00000000 --- a/app/src/main/java/com/hjq/demo/mvp/copy/CopyContract.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.hjq.demo.mvp.copy; - -import com.hjq.demo.mvp.IMvpView; - -import java.util.List; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/11/17 - * desc : 可进行拷贝的契约类 - */ -public final class CopyContract { - - public interface View extends IMvpView { - - void loginSuccess(List data); - - void loginError(String msg); - } - - public interface Presenter { - - void login(String account, String password); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/mvp/copy/CopyModel.java b/app/src/main/java/com/hjq/demo/mvp/copy/CopyModel.java deleted file mode 100644 index 91579a35..00000000 --- a/app/src/main/java/com/hjq/demo/mvp/copy/CopyModel.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.hjq.demo.mvp.copy; - -import com.hjq.demo.mvp.MvpModel; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/11/17 - * desc : 可进行拷贝的接口实现类 - */ -public final class CopyModel extends MvpModel { - - private String mAccount; - private String mPassword; - - public CopyModel() { - // 在这里做一些初始化操作 - } - - public void setAccount(String account) { - this.mAccount = account; - } - - public void setPassword(String password) { - this.mPassword = password; - } - - public void login() { - // 为了省事,这里直接回调成功 - if ("账户".equals(mAccount) && "密码".equals(mPassword)) { - getListener().onSucceed(null); - } else { - getListener().onFail("账户或密码不对哦"); - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/mvp/copy/CopyMvpActivity.java b/app/src/main/java/com/hjq/demo/mvp/copy/CopyMvpActivity.java deleted file mode 100644 index 3e501af2..00000000 --- a/app/src/main/java/com/hjq/demo/mvp/copy/CopyMvpActivity.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.hjq.demo.mvp.copy; - -import android.view.View; - -import com.hjq.demo.R; -import com.hjq.demo.mvp.MvpActivity; -import com.hjq.demo.mvp.MvpInject; - -import java.util.List; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/11/17 - * desc : 可进行拷贝的MVP Activity 类 - */ -public final class CopyMvpActivity extends MvpActivity implements CopyContract.View { - - @MvpInject - CopyPresenter mPresenter; - - @Override - protected int getLayoutId() { - return R.layout.activity_copy; - } - - @Override - protected void initView() { - - } - - @Override - protected void initData() { - - } - - public void onLogin(View view) { - // 登录操作 - mPresenter.login("账户", "密码"); - } - - /** - * {@link CopyContract.View} - */ - - @Override - public void loginError(String msg) { - toast(msg); - } - - @Override - public void loginSuccess(List data) { - toast("登录成功了"); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/mvp/copy/CopyOnListener.java b/app/src/main/java/com/hjq/demo/mvp/copy/CopyOnListener.java deleted file mode 100644 index 4cf44c51..00000000 --- a/app/src/main/java/com/hjq/demo/mvp/copy/CopyOnListener.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.hjq.demo.mvp.copy; - -import java.util.List; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/11/17 - * desc : 可进行拷贝的监听器 - */ -public interface CopyOnListener { - - void onSucceed(List data); - - void onFail(String msg); -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/mvp/copy/CopyPresenter.java b/app/src/main/java/com/hjq/demo/mvp/copy/CopyPresenter.java deleted file mode 100644 index ef01ebd2..00000000 --- a/app/src/main/java/com/hjq/demo/mvp/copy/CopyPresenter.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.hjq.demo.mvp.copy; - -import com.hjq.demo.mvp.MvpInject; -import com.hjq.demo.mvp.MvpPresenter; - -import java.util.List; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/11/17 - * desc : 可进行拷贝的业务处理类 - */ -public final class CopyPresenter extends MvpPresenter - implements CopyContract.Presenter, CopyOnListener { - - @MvpInject - CopyModel mModel; - - /** - * {@link CopyContract.Presenter} - */ - - @Override - public void login(String account, String password) { - mModel.setAccount(account); - mModel.setPassword(password); - mModel.setListener(this); - mModel.login(); - } - - /** - * {@link CopyOnListener} - */ - - @Override - public void onSucceed(List data) { - getView().loginSuccess(data); - } - - @Override - public void onFail(String msg) { - getView().loginError(msg); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/mvp/proxy/IMvpModelProxy.java b/app/src/main/java/com/hjq/demo/mvp/proxy/IMvpModelProxy.java deleted file mode 100644 index 4b0d7c8c..00000000 --- a/app/src/main/java/com/hjq/demo/mvp/proxy/IMvpModelProxy.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.hjq.demo.mvp.proxy; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2019/05/11 - * desc : 模型层代理接口 - */ -public interface IMvpModelProxy { - /** - * 绑定 Model - */ - void bindModel(); - - /** - * 解绑 Model - */ - void unbindModel(); -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/mvp/proxy/IMvpPresenterProxy.java b/app/src/main/java/com/hjq/demo/mvp/proxy/IMvpPresenterProxy.java deleted file mode 100644 index 5b893c80..00000000 --- a/app/src/main/java/com/hjq/demo/mvp/proxy/IMvpPresenterProxy.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.hjq.demo.mvp.proxy; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2019/05/11 - * desc : 逻辑层代理接口 - */ -public interface IMvpPresenterProxy { - /** - * 绑定 Presenter - */ - void bindPresenter(); - - /** - * 解绑 Presenter - */ - void unbindPresenter(); -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/mvp/proxy/MvpModelProxyImpl.java b/app/src/main/java/com/hjq/demo/mvp/proxy/MvpModelProxyImpl.java deleted file mode 100644 index 5f530910..00000000 --- a/app/src/main/java/com/hjq/demo/mvp/proxy/MvpModelProxyImpl.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.hjq.demo.mvp.proxy; - -import com.hjq.demo.mvp.MvpInject; -import com.hjq.demo.mvp.MvpModel; -import com.hjq.demo.mvp.MvpPresenter; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2019/05/11 - * desc : 模型层代理实现 - */ -public class MvpModelProxyImpl implements IMvpModelProxy { - - private MvpPresenter mPresenter; - private List mModels; - - public MvpModelProxyImpl(MvpPresenter presenter) { - mPresenter = presenter; - } - - @SuppressWarnings("all") - @Override - public void bindModel() { - mModels = new ArrayList<>(); - - Field[] fields = mPresenter.getClass().getDeclaredFields(); - for (Field field : fields) { - MvpInject inject = field.getAnnotation(MvpInject.class); - if(inject != null){ - try { - Class clazz = (Class) field.getType(); - MvpModel model = clazz.newInstance(); - field.setAccessible(true); - field.set(mPresenter, model); - mModels.add(model); - } catch (IllegalAccessException | InstantiationException | ClassCastException e) { - e.printStackTrace(); - /** - * IllegalAccessException - * field.set:没有权限访问,请检查注解对象的修饰符 - */ - /** - * InstantiationException - * clazz.newInstance:检查一下注解的对象有没有空的构造函数 - */ - /** - * ClassCastException - * clazz.newInstance:检查一下自己注解的对象类型是否正确 - * field.set:检查一下自己的 M 层类型是否正确 - */ - throw new IllegalStateException("are you ok?"); - } - } - } - } - - @Override - public void unbindModel() { - mModels.clear(); - mModels = null; - mPresenter = null; - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/mvp/proxy/MvpPresenterProxyImpl.java b/app/src/main/java/com/hjq/demo/mvp/proxy/MvpPresenterProxyImpl.java deleted file mode 100644 index 32b8e23e..00000000 --- a/app/src/main/java/com/hjq/demo/mvp/proxy/MvpPresenterProxyImpl.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.hjq.demo.mvp.proxy; - -import com.hjq.demo.mvp.IMvpView; -import com.hjq.demo.mvp.MvpInject; -import com.hjq.demo.mvp.MvpPresenter; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2019/05/11 - * desc : 模型层代理实现 - */ -public class MvpPresenterProxyImpl implements IMvpPresenterProxy { - - private IMvpView mView; - private List mPresenters; - - public MvpPresenterProxyImpl(IMvpView view){ - mView = view; - } - - @SuppressWarnings("all") - @Override - public void bindPresenter() { - mPresenters = new ArrayList<>(); - - Field[] fields = mView.getClass().getDeclaredFields(); - for (Field field : fields) { - MvpInject inject = field.getAnnotation(MvpInject.class); - if(inject != null){ - try { - Class clazz = (Class) field.getType(); - MvpPresenter presenter = clazz.newInstance(); - field.setAccessible(true); - field.set(mView, presenter); - presenter.attachView(mView); - mPresenters.add(presenter); - } catch (IllegalAccessException | InstantiationException | ClassCastException e) { - e.printStackTrace(); - /** - * IllegalAccessException - * field.set:没有权限访问,请检查注解对象的修饰符 - */ - /** - * InstantiationException - * clazz.newInstance:检查一下注解的对象有没有空的构造函数 - */ - /** - * ClassCastException - * clazz.newInstance:检查一下自己注解的对象类型是否正确 - * field.set:检查一下自己的 V 层(Activity 或 Fragment)有没有实现 P 层对应的接口 - */ - throw new IllegalStateException("are you ok?"); - } - } - } - } - - @Override - public void unbindPresenter() { - // 一定要解绑 - for (MvpPresenter presenter : mPresenters) { - presenter.detachView(); - } - mPresenters.clear(); - mPresenters = null; - mView = null; - } -} diff --git a/app/src/main/java/com/hjq/demo/other/AppConfig.java b/app/src/main/java/com/hjq/demo/other/AppConfig.java index 2dd2ffc4..25114fd5 100644 --- a/app/src/main/java/com/hjq/demo/other/AppConfig.java +++ b/app/src/main/java/com/hjq/demo/other/AppConfig.java @@ -44,4 +44,11 @@ public static int getVersionCode() { public static String getProductFlavors() { return BuildConfig.FLAVOR; } + + /** + * 获取 BuglyId + */ + public static String getBuglyId() { + return BuildConfig.BUGLY_ID; + } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/other/EventBusManager.java b/app/src/main/java/com/hjq/demo/other/EventBusManager.java deleted file mode 100644 index f8f796cb..00000000 --- a/app/src/main/java/com/hjq/demo/other/EventBusManager.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.hjq.demo.other; - -import androidx.collection.ArrayMap; - -import com.hjq.demo.MyEventBusIndex; - -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; -import org.greenrobot.eventbus.meta.SubscriberInfoIndex; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2019/04/02 - * desc : EventBus 管理类 - */ -public final class EventBusManager { - - /** EventBus 索引类 */ - private static final SubscriberInfoIndex SUBSCRIBE_INDEX = new MyEventBusIndex(); - - /** 这个类是否需要注册 EventBus */ - private static final ArrayMap SUBSCRIBE_EVENT = new ArrayMap<>(); - - /** 不允许被外部实例化 */ - private EventBusManager() {} - - /** - * 初始化 EventBus - */ - public static void init() { - EventBus.builder() - // 使用 Apt 插件 - .ignoreGeneratedIndex(false) - // 添加索引类 - .addIndex(SUBSCRIBE_INDEX) - // 作为默认配置 - .installDefaultEventBus(); - } - - /** - * 注册 EventBus - */ - public static void register(Object subscriber) { - if (canSubscribeEvent(subscriber)) { - EventBus.getDefault().register(subscriber); - } - } - - /** - * 反注册 EventBus - */ - public static void unregister(Object subscriber) { - if (canSubscribeEvent(subscriber) && EventBus.getDefault().isRegistered(subscriber)) { - EventBus.getDefault().unregister(subscriber); - } - } - - /** - * 判断是否使用了 EventBus 注解 - * - * @param subscriber 被订阅的类 - */ - private static boolean canSubscribeEvent(Object subscriber) { - Class clazz = subscriber.getClass(); - // 这个 Class 类型有没有遍历过 - Boolean result = SUBSCRIBE_EVENT.get(clazz.getName()); - if (result != null) { - // 有的话直接返回结果 - return result; - } - - // 没有的话进行遍历 - while (clazz != null) { - // 如果索引集合中有这个 Class 类型的订阅信息,则这个类型的对象都需要注册 EventBus - if (SUBSCRIBE_INDEX.getSubscriberInfo(clazz) != null) { - // 这个类需要注册 EventBus - result = true; - clazz = null; - } else { - String clazzName = clazz.getName(); - // 跳过系统类(忽略 java. javax. android. androidx. 等开头包名的类) - if (clazzName.startsWith("java") || clazzName.startsWith("android")) { - clazz = null; - } else { - // 往上查找 - clazz = clazz.getSuperclass(); - } - } - } - // 这个类不需要注册 EventBus - if (result == null) { - result = false; - } - SUBSCRIBE_EVENT.put(subscriber.getClass().getName(), result); - return result; - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onEventBus(EventBusManager helper) { - // 占位,只为了能生成 MyEventBusIndex 索引类 - // 如果项目中已经有用到 @Subscribe 去注解方法,这个方法可以直接删除 - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/other/PhotoSpaceDecoration.java b/app/src/main/java/com/hjq/demo/other/GridSpaceDecoration.java similarity index 92% rename from app/src/main/java/com/hjq/demo/other/PhotoSpaceDecoration.java rename to app/src/main/java/com/hjq/demo/other/GridSpaceDecoration.java index 51959d78..9c173dd3 100644 --- a/app/src/main/java/com/hjq/demo/other/PhotoSpaceDecoration.java +++ b/app/src/main/java/com/hjq/demo/other/GridSpaceDecoration.java @@ -14,11 +14,11 @@ * time : 2019/07/25 * desc : 图片选择列表分割线 */ -public final class PhotoSpaceDecoration extends RecyclerView.ItemDecoration { +public final class GridSpaceDecoration extends RecyclerView.ItemDecoration { private final int mSpace; - public PhotoSpaceDecoration(int space) { + public GridSpaceDecoration(int space) { mSpace = space; } diff --git a/app/src/main/java/com/hjq/demo/other/IntentKey.java b/app/src/main/java/com/hjq/demo/other/IntentKey.java index 53a1ef2e..a8e1039f 100644 --- a/app/src/main/java/com/hjq/demo/other/IntentKey.java +++ b/app/src/main/java/com/hjq/demo/other/IntentKey.java @@ -75,6 +75,8 @@ public final class IntentKey { // 文件类型相关 + /** 文本 */ + public static final String FILE = "file"; /** 文本 */ public static final String TXT = "txt"; /** 图片 */ diff --git a/app/src/main/java/com/hjq/demo/other/KeyboardWatcher.java b/app/src/main/java/com/hjq/demo/other/KeyboardWatcher.java index 0ad0dfdd..0f169e4b 100644 --- a/app/src/main/java/com/hjq/demo/other/KeyboardWatcher.java +++ b/app/src/main/java/com/hjq/demo/other/KeyboardWatcher.java @@ -3,13 +3,14 @@ import android.app.Activity; import android.app.Application; import android.graphics.Rect; -import android.os.Build; import android.os.Bundle; import android.view.View; import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowManager; +import androidx.annotation.NonNull; + /** * author : Android 轮子哥 * github : https://github.com/getActivity/AndroidProject @@ -88,42 +89,28 @@ public void setListener(SoftKeyboardStateListener listener) { */ @Override - public void onActivityCreated(Activity activity, Bundle savedInstanceState) { - - } + public void onActivityCreated(@NonNull Activity activity, Bundle savedInstanceState) {} @Override - public void onActivityStarted(Activity activity) { - - } + public void onActivityStarted(@NonNull Activity activity) {} @Override - public void onActivityResumed(Activity activity) { - - } + public void onActivityResumed(@NonNull Activity activity) {} @Override - public void onActivityPaused(Activity activity) { - - } + public void onActivityPaused(@NonNull Activity activity) {} @Override - public void onActivityStopped(Activity activity) { - - } + public void onActivityStopped(@NonNull Activity activity) {} @Override - public void onActivitySaveInstanceState(Activity activity, Bundle outState) { - - } + public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {} @Override - public void onActivityDestroyed(Activity activity) { + public void onActivityDestroyed(@NonNull Activity activity) { if (mActivity == activity) { mActivity.getApplication().unregisterActivityLifecycleCallbacks(this); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - mContentView.getViewTreeObserver().removeOnGlobalLayoutListener(this); - } + mContentView.getViewTreeObserver().removeOnGlobalLayoutListener(this); mActivity = null; mContentView = null; diff --git a/app/src/main/java/com/hjq/demo/other/PickerLayoutManager.java b/app/src/main/java/com/hjq/demo/other/PickerLayoutManager.java new file mode 100644 index 00000000..ef454900 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/other/PickerLayoutManager.java @@ -0,0 +1,270 @@ +package com.hjq.demo.other; + +import android.content.Context; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.core.view.ViewCompat; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.LinearSnapHelper; +import androidx.recyclerview.widget.RecyclerView; + +/** + * author : 钉某人 + * github : https://github.com/DingMouRen/LayoutManagerGroup + * time : 2019/09/11 + * desc : 选择器布局管理器 + */ +public class PickerLayoutManager extends LinearLayoutManager { + + private final LinearSnapHelper mLinearSnapHelper; + private final int mOrientation; + private final int mMaxItem; + private final float mScale; + private final boolean mAlpha; + + private RecyclerView mRecyclerView; + private OnPickerListener mListener; + + private PickerLayoutManager(Context context, int orientation, boolean reverseLayout, int maxItem, float scale, boolean alpha) { + super(context, orientation, reverseLayout); + mLinearSnapHelper = new LinearSnapHelper(); + mMaxItem = maxItem; + mOrientation = orientation; + mAlpha = alpha; + mScale = scale; + } + + @Override + public void onAttachedToWindow(RecyclerView recyclerView) { + super.onAttachedToWindow(recyclerView); + mRecyclerView = recyclerView; + // 设置子控件的边界可以超过父布局的范围 + mRecyclerView.setClipToPadding(false); + // 添加 LinearSnapHelper + mLinearSnapHelper.attachToRecyclerView(mRecyclerView); + } + + @Override + public void onDetachedFromWindow(RecyclerView recyclerView, RecyclerView.Recycler recycler) { + super.onDetachedFromWindow(recyclerView, recycler); + mRecyclerView = null; + } + + @Override + public boolean isAutoMeasureEnabled() { + return mMaxItem == 0; + } + + @Override + public void onMeasure(@NonNull RecyclerView.Recycler recycler, @NonNull RecyclerView.State state, int widthSpec, int heightSpec) { + int width = RecyclerView.LayoutManager.chooseSize(widthSpec, + getPaddingLeft() + getPaddingRight(), + ViewCompat.getMinimumWidth(mRecyclerView)); + int height = RecyclerView.LayoutManager.chooseSize(heightSpec, + getPaddingTop() + getPaddingBottom(), + ViewCompat.getMinimumHeight(mRecyclerView)); + + if (state.getItemCount() != 0 && mMaxItem != 0) { + + View itemView = recycler.getViewForPosition(0); + measureChildWithMargins(itemView, widthSpec, heightSpec); + + if (mOrientation == HORIZONTAL) { + int measuredWidth = itemView.getMeasuredWidth(); + int paddingHorizontal = (mMaxItem - 1) / 2 * measuredWidth; + mRecyclerView.setPadding(paddingHorizontal,0, paddingHorizontal,0); + width = measuredWidth * mMaxItem; + } else if (mOrientation == VERTICAL) { + int measuredHeight = itemView.getMeasuredHeight(); + int paddingVertical = (mMaxItem - 1) / 2 * measuredHeight; + mRecyclerView.setPadding(0, paddingVertical,0, paddingVertical); + height = measuredHeight * mMaxItem; + } + } + setMeasuredDimension(width, height); + } + + @Override + public void onScrollStateChanged(int state) { + super.onScrollStateChanged(state); + // 当 RecyclerView 停止滚动时 + if (state == RecyclerView.SCROLL_STATE_IDLE) { + if (mListener != null) { + mListener.onPicked(mRecyclerView, getPickedPosition()); + } + } + } + + @Override + public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { + super.onLayoutChildren(recycler, state); + if (getItemCount() < 0 || state.isPreLayout()) { + return; + } + + if (mOrientation == HORIZONTAL){ + scaleHorizontalChildView(); + }else if (mOrientation == VERTICAL){ + scaleVerticalChildView(); + } + } + + @Override + public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { + scaleHorizontalChildView(); + return super.scrollHorizontallyBy(dx, recycler, state); + } + + @Override + public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { + scaleVerticalChildView(); + return super.scrollVerticallyBy(dy, recycler, state); + } + + /** + * 横向情况下的缩放 + */ + private void scaleHorizontalChildView() { + float mid = getWidth() / 2.0f; + for (int i = 0; i < getChildCount(); i++) { + View childView = getChildAt(i); + if (childView != null) { + float childMid = (getDecoratedLeft(childView) + getDecoratedRight(childView)) / 2.0f; + float scale = 1.0f + (-1 * (1 - mScale)) * (Math.min(mid, Math.abs(mid - childMid))) / mid; + childView.setScaleX(scale); + childView.setScaleY(scale); + if (mAlpha) { + childView.setAlpha(scale); + } + } + } + } + + /** + * 竖向方向上的缩放 + */ + private void scaleVerticalChildView(){ + float mid = getHeight() / 2.0f; + for (int i = 0; i < getChildCount(); i++) { + View childView = getChildAt(i); + if (childView != null) { + float childMid = (getDecoratedTop(childView) + getDecoratedBottom(childView)) / 2.0f; + float scale = 1.0f + (-1 * (1 - mScale)) * (Math.min(mid, Math.abs(mid - childMid))) / mid; + childView.setScaleX(scale); + childView.setScaleY(scale); + if (mAlpha) { + childView.setAlpha(scale); + } + } + } + } + + /** + * 获取选中的位置 + */ + public int getPickedPosition() { + View itemView = mLinearSnapHelper.findSnapView(this); + if(itemView != null) { + return getPosition(itemView); + } + return 0; + } + + /** + * 设置监听器 + */ + public void setOnPickerListener(OnPickerListener listener) { + mListener = listener; + } + + public interface OnPickerListener { + + /** + * 滚动停止时触发的监听 + * + * @param recyclerView RecyclerView 对象 + * @param position 当前滚动的位置 + */ + void onPicked(RecyclerView recyclerView, int position); + } + + public static final class Builder { + + private final Context mContext; + private int mOrientation = VERTICAL; + private boolean mReverseLayout; + private OnPickerListener mListener; + + private int mMaxItem = 3; + private float mScale = 0.6f; + private boolean mAlpha = true; + + public Builder(Context context) { + mContext = context; + } + + /** + * 设置布局摆放器方向 + */ + public Builder setOrientation(@RecyclerView.Orientation int orientation) { + mOrientation = orientation; + return this; + } + + /** + * 设置是否反向显示 + */ + public Builder setReverseLayout(boolean reverseLayout) { + mReverseLayout = reverseLayout; + return this; + } + + /** + * 设置最大显示条目数 + */ + public Builder setMaxItem(int maxItem) { + mMaxItem = maxItem; + return this; + } + + /** + * 设置缩放比例 + */ + public Builder setScale(float scale) { + mScale = scale; + return this; + } + + /** + * 设置透明开关 + */ + public Builder setAlpha(boolean alpha) { + mAlpha = alpha; + return this; + } + + public Builder setOnPickerListener(OnPickerListener listener) { + mListener = listener; + return this; + } + + /** + * 构建布局管理器 + */ + public PickerLayoutManager build() { + PickerLayoutManager layoutManager = new PickerLayoutManager(mContext, mOrientation, mReverseLayout, mMaxItem, mScale, mAlpha); + if (mListener != null) { + layoutManager.setOnPickerListener(mListener); + } + return layoutManager; + } + + /** + * 应用到 RecyclerView + */ + public void into(RecyclerView recyclerView) { + recyclerView.setLayoutManager(build()); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/other/StatusManager.java b/app/src/main/java/com/hjq/demo/other/StatusManager.java deleted file mode 100644 index cb5b4d37..00000000 --- a/app/src/main/java/com/hjq/demo/other/StatusManager.java +++ /dev/null @@ -1,149 +0,0 @@ -package com.hjq.demo.other; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.DrawableRes; -import androidx.annotation.RequiresPermission; -import androidx.annotation.StringRes; -import androidx.core.content.ContextCompat; -import androidx.fragment.app.FragmentActivity; - -import com.hjq.base.BaseDialog; -import com.hjq.demo.R; -import com.hjq.demo.ui.dialog.WaitDialog; -import com.hjq.widget.layout.HintLayout; - -import static android.Manifest.permission.ACCESS_NETWORK_STATE; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2019/04/18 - * desc : 界面状态管理类 - */ -public final class StatusManager { - - /** 加载对话框 */ - private BaseDialog mDialog; - - /** 提示布局 */ - private HintLayout mHintLayout; - - /** - * 显示加载中 - */ - public void showLoading(FragmentActivity activity) { - showLoading(activity, activity.getString(R.string.common_loading)); - } - - public void showLoading(FragmentActivity activity, CharSequence text) { - if (activity == null || activity.isFinishing()) { - return; - } - - if (mDialog == null) { - mDialog = new WaitDialog.Builder(activity) - .setMessage(text) - .create(); - } - - if (!mDialog.isShowing()) { - mDialog.show(); - } - } - - /** - * 显示加载完成 - */ - public void showComplete() { - - if (mDialog != null && mDialog.isShowing()) { - mDialog.dismiss(); - } - - if (mHintLayout != null && mHintLayout.isShow()) { - mHintLayout.hide(); - } - } - - /** - * 显示空提示 - */ - public void showEmpty(View view) { - showLayout(view, R.drawable.icon_hint_empty, R.string.hint_layout_no_data); - } - - /** - * 显示错误提示 - */ - public void showError(View view) { - // 判断当前网络是否可用 - if (isNetworkAvailable(view.getContext())) { - showLayout(view, R.drawable.icon_hint_request, R.string.hint_layout_error_request); - } else { - showLayout(view, R.drawable.icon_hint_nerwork, R.string.hint_layout_error_network); - } - } - - /** - * 显示自定义提示 - */ - public void showLayout(View view, @DrawableRes int drawableId, @StringRes int stringId) { - showLayout(view, ContextCompat.getDrawable(view.getContext(), drawableId), view.getResources().getString(stringId)); - } - - public void showLayout(View view, Drawable drawable, CharSequence hint) { - if (mDialog != null && mDialog.isShowing()) { - mDialog.dismiss(); - } - - if (mHintLayout == null) { - - if (view instanceof HintLayout) { - mHintLayout = (HintLayout) view; - } else if (view instanceof ViewGroup) { - mHintLayout = findHintLayout((ViewGroup) view); - } - - if (mHintLayout == null) { - // 必须在布局中定义一个 HintLayout - throw new IllegalStateException("You didn't add this HintLayout to your layout"); - } - } - mHintLayout.show(); - mHintLayout.setIcon(drawable); - mHintLayout.setHint(hint); - } - - /** - * 智能获取布局中的 HintLayout 对象 - */ - private static HintLayout findHintLayout(ViewGroup group) { - for (int i = 0; i < group.getChildCount(); i++) { - View view = group.getChildAt(i); - if ((view instanceof HintLayout)) { - return (HintLayout) view; - } else if (view instanceof ViewGroup) { - HintLayout layout = findHintLayout((ViewGroup) view); - if (layout != null) { - return layout; - } - } - } - return null; - } - - /** - * 判断网络功能是否可用 - */ - @RequiresPermission(ACCESS_NETWORK_STATE) - private static boolean isNetworkAvailable(Context context){ - NetworkInfo info = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo(); - return (info != null && info.isConnected()); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/activity/BrowserActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/BrowserActivity.java new file mode 100644 index 00000000..a44c2e89 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/ui/activity/BrowserActivity.java @@ -0,0 +1,189 @@ +package com.hjq.demo.ui.activity; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.view.KeyEvent; +import android.view.View; +import android.webkit.WebView; +import android.widget.ProgressBar; + +import androidx.annotation.NonNull; + +import com.hjq.demo.R; +import com.hjq.demo.action.StatusAction; +import com.hjq.demo.aop.CheckNet; +import com.hjq.demo.aop.DebugLog; +import com.hjq.demo.aop.SingleClick; +import com.hjq.demo.common.MyActivity; +import com.hjq.demo.other.IntentKey; +import com.hjq.demo.widget.BrowserView; +import com.hjq.demo.widget.HintLayout; +import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.scwang.smartrefresh.layout.api.RefreshLayout; +import com.scwang.smartrefresh.layout.listener.OnRefreshListener; + +import butterknife.BindView; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2018/10/18 + * desc : 浏览器界面 + */ +public final class BrowserActivity extends MyActivity + implements StatusAction, OnRefreshListener { + + @CheckNet + @DebugLog + public static void start(Context context, String url) { + if (url == null || "".equals(url)) { + return; + } + Intent intent = new Intent(context, BrowserActivity.class); + intent.putExtra(IntentKey.URL, url); + context.startActivity(intent); + } + + @BindView(R.id.hl_browser_hint) + HintLayout mHintLayout; + @BindView(R.id.pb_browser_progress) + ProgressBar mProgressBar; + @BindView(R.id.sl_browser_refresh) + SmartRefreshLayout mRefreshLayout; + @BindView(R.id.wv_browser_view) + BrowserView mBrowserView; + + @Override + protected int getLayoutId() { + return R.layout.activity_browser; + } + + @Override + protected void initView() { + // 设置网页刷新监听 + mRefreshLayout.setOnRefreshListener(this); + } + + @Override + protected void initData() { + showLoading(); + + mBrowserView.setBrowserViewClient(new MyBrowserViewClient()); + mBrowserView.setBrowserChromeClient(new MyBrowserChromeClient(mBrowserView)); + + String url = getString(IntentKey.URL); + mBrowserView.loadUrl(url); + } + + @Override + public HintLayout getHintLayout() { + return mHintLayout; + } + + @Override + public void onLeftClick(View v) { + finish(); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK && mBrowserView.canGoBack()) { + // 后退网页并且拦截该事件 + mBrowserView.goBack(); + return true; + } + return super.onKeyDown(keyCode, event); + } + + @Override + protected void onResume() { + mBrowserView.onResume(); + super.onResume(); + } + + @Override + protected void onPause() { + mBrowserView.onPause(); + super.onPause(); + } + + @Override + protected void onDestroy() { + mBrowserView.onDestroy(); + super.onDestroy(); + } + + /** + * 重新加载当前页 + */ + @SingleClick + @CheckNet + private void reload() { + mBrowserView.reload(); + } + + /** + * {@link OnRefreshListener} + */ + + @Override + public void onRefresh(@NonNull RefreshLayout refreshLayout) { + mBrowserView.reload(); + } + + private class MyBrowserViewClient extends BrowserView.BrowserViewClient { + + /** + * 网页加载错误时回调,这个方法会在 onPageFinished 之前调用 + */ + @Override + public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { + // 这里为什么要用延迟呢?因为加载出错之后会先调用 onReceivedError 再调用 onPageFinished + post(() -> showError(v -> reload())); + } + + /** + * 开始加载网页 + */ + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + mProgressBar.setVisibility(View.VISIBLE); + } + + /** + * 完成加载网页 + */ + @Override + public void onPageFinished(WebView view, String url) { + mProgressBar.setVisibility(View.GONE); + mRefreshLayout.finishRefresh(); + showComplete(); + } + } + + private class MyBrowserChromeClient extends BrowserView.BrowserChromeClient { + + private MyBrowserChromeClient(BrowserView view) { + super(view); + } + + /** + * 收到网页标题 + */ + @Override + public void onReceivedTitle(WebView view, String title) { + if (title != null) { + setTitle(title); + } + } + + /** + * 收到加载进度变化 + */ + @Override + public void onProgressChanged(WebView view, int newProgress) { + mProgressBar.setProgress(newProgress); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/activity/CameraActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/CameraActivity.java new file mode 100644 index 00000000..90e031e8 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/ui/activity/CameraActivity.java @@ -0,0 +1,163 @@ +package com.hjq.demo.ui.activity; + +import android.content.Intent; +import android.media.MediaScannerConnection; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.provider.MediaStore; + +import androidx.annotation.Nullable; +import androidx.core.content.FileProvider; + +import com.hjq.base.BaseActivity; +import com.hjq.demo.R; +import com.hjq.demo.aop.DebugLog; +import com.hjq.demo.aop.Permissions; +import com.hjq.demo.common.MyActivity; +import com.hjq.demo.other.AppConfig; +import com.hjq.demo.other.IntentKey; +import com.hjq.permissions.Permission; +import com.hjq.permissions.XXPermissions; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/12/18 + * desc : 拍照选择 + */ +public final class CameraActivity extends MyActivity { + + private static final int CAMERA_REQUEST_CODE = 1024; + + @DebugLog + @Permissions({Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE, Permission.CAMERA}) + public static void start(BaseActivity activity, OnCameraListener listener) { + File file = createCameraFile(); + Intent intent = new Intent(activity, CameraActivity.class); + intent.putExtra(IntentKey.FILE, file); + activity.startActivityForResult(intent, (resultCode, data) -> { + + if (listener == null) { + return; + } + + if (resultCode == RESULT_OK) { + listener.onSelected(file); + } else { + listener.onCancel(); + } + }); + } + + private File mFile; + + @Override + protected int getLayoutId() { + return 0; + } + + @Override + protected void initView() { + + } + + @Override + protected void initData() { + // 启动系统相机 + Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + if (XXPermissions.isHasPermission(this, Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE, Permission.CAMERA) + && intent.resolveActivity(getPackageManager()) != null) { + mFile = getSerializable(IntentKey.FILE); + if (mFile != null && mFile.exists()) { + + Uri imageUri; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + // 通过 FileProvider 创建一个 Content 类型的 Uri 文件 + imageUri = FileProvider.getUriForFile(this, AppConfig.getPackageName() + ".provider", mFile); + } else { + imageUri = Uri.fromFile(mFile); + } + // 对目标应用临时授权该 Uri 所代表的文件 + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + // 将拍取的照片保存到指定 Uri + intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); + startActivityForResult(intent, CAMERA_REQUEST_CODE); + } else { + toast(R.string.photo_picture_error); + finish(); + } + } else { + toast(R.string.photo_launch_fail); + finish(); + } + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + @Override + protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == CAMERA_REQUEST_CODE) { + switch (resultCode) { + case RESULT_OK: + // 重新扫描多媒体(否则可能扫描不到) + MediaScannerConnection.scanFile(getApplicationContext(), new String[]{mFile.getPath()}, null,null); + break; + case RESULT_CANCELED: + // 删除这个文件 + mFile.delete(); + break; + default: + break; + } + setResult(resultCode); + finish(); + } + } + + /** + * 创建一个拍照图片文件对象 + */ + @SuppressWarnings("ResultOfMethodCallIgnored") + private static File createCameraFile() { + File folder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Camera"); + if (!folder.exists() || !folder.isDirectory()) { + if (!folder.mkdirs()) { + folder = Environment.getExternalStorageDirectory(); + } + } + + try { + File file = new File(folder, "IMG_" + new SimpleDateFormat("yyyyMMdd_kkmmss", Locale.getDefault()).format(new Date()) + ".jpg"); + file.createNewFile(); + return file; + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + /** + * 拍照选择监听 + */ + public interface OnCameraListener { + + /** + * 选择回调 + * + * @param file 照片 + */ + void onSelected(File file); + + /** + * 取消回调 + */ + default void onCancel() {} + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/activity/CrashActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/CrashActivity.java index f6cda734..983f5421 100644 --- a/app/src/main/java/com/hjq/demo/ui/activity/CrashActivity.java +++ b/app/src/main/java/com/hjq/demo/ui/activity/CrashActivity.java @@ -2,18 +2,17 @@ import android.content.ClipData; import android.content.ClipboardManager; -import android.content.DialogInterface; +import android.content.Context; import android.util.TypedValue; import android.view.View; import android.widget.TextView; import androidx.appcompat.app.AlertDialog; -import androidx.core.content.ContextCompat; import com.hjq.demo.R; +import com.hjq.demo.aop.SingleClick; import com.hjq.demo.common.MyActivity; -import butterknife.OnClick; import cat.ereza.customactivityoncrash.CustomActivityOnCrash; import cat.ereza.customactivityoncrash.config.CaocConfig; @@ -27,6 +26,8 @@ public final class CrashActivity extends MyActivity { private CaocConfig mConfig; + private AlertDialog mDialog; + @Override protected int getLayoutId() { return R.layout.activity_crash; @@ -34,37 +35,36 @@ protected int getLayoutId() { @Override protected void initView() { - + setOnClickListener(R.id.btn_crash_restart, R.id.btn_crash_log); } @Override protected void initData() { mConfig = CustomActivityOnCrash.getConfigFromIntent(getIntent()); if (mConfig == null) { - // 这种情况永远不会发生,只要完成该活动就可以避免递归崩溃。 + // 这种情况永远不会发生,只要完成该活动就可以避免递归崩溃 finish(); } } - @OnClick({R.id.btn_crash_restart, R.id.btn_crash_log}) + @SingleClick + @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_crash_restart: CustomActivityOnCrash.restartApplication(CrashActivity.this, mConfig); break; case R.id.btn_crash_log: - AlertDialog dialog = new AlertDialog.Builder(CrashActivity.this) - .setTitle(R.string.crash_error_details) - .setMessage(CustomActivityOnCrash.getAllErrorDetailsFromIntent(CrashActivity.this, getIntent())) - .setPositiveButton(R.string.crash_close, null) - .setNeutralButton(R.string.crash_copy_log, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - copyErrorToClipboard(); - } - }) - .show(); - TextView textView = dialog.findViewById(android.R.id.message); + if (mDialog == null) { + mDialog = new AlertDialog.Builder(CrashActivity.this) + .setTitle(R.string.crash_error_details) + .setMessage(CustomActivityOnCrash.getAllErrorDetailsFromIntent(CrashActivity.this, getIntent())) + .setPositiveButton(R.string.crash_close, null) + .setNeutralButton(R.string.crash_copy_log, (dialog, which) -> copyErrorToClipboard()) + .create(); + } + mDialog.show(); + TextView textView = mDialog.findViewById(android.R.id.message); if (textView != null) { textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12); } @@ -77,9 +77,13 @@ public void onClick(DialogInterface dialog, int which) { /** * 复制报错信息到剪贴板 */ - @SuppressWarnings("all") private void copyErrorToClipboard() { String errorInformation = CustomActivityOnCrash.getAllErrorDetailsFromIntent(CrashActivity.this, getIntent()); - ContextCompat.getSystemService(this, ClipboardManager.class).setPrimaryClip(ClipData.newPlainText(getString(R.string.crash_error_info), errorInformation)); + ((ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE)).setPrimaryClip(ClipData.newPlainText(getString(R.string.crash_error_info), errorInformation)); + } + + @Override + public boolean isSwipeEnable() { + return false; } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/activity/DialogActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/DialogActivity.java index 91861cd5..db16a10c 100644 --- a/app/src/main/java/com/hjq/demo/ui/activity/DialogActivity.java +++ b/app/src/main/java/com/hjq/demo/ui/activity/DialogActivity.java @@ -2,38 +2,38 @@ import android.content.Intent; import android.view.Gravity; -import android.view.KeyEvent; import android.view.View; import android.widget.ImageView; import androidx.annotation.Nullable; import com.hjq.base.BaseDialog; -import com.hjq.base.BaseDialogFragment; import com.hjq.demo.R; +import com.hjq.demo.aop.SingleClick; import com.hjq.demo.common.MyActivity; -import com.hjq.demo.ui.dialog.PayPasswordDialog; -import com.hjq.demo.ui.dialog.ShareDialog; -import com.hjq.demo.ui.dialog.UpdateDialog; -import com.hjq.demo.wxapi.WXEntryActivity; import com.hjq.demo.ui.dialog.AddressDialog; import com.hjq.demo.ui.dialog.DateDialog; import com.hjq.demo.ui.dialog.InputDialog; import com.hjq.demo.ui.dialog.MenuDialog; import com.hjq.demo.ui.dialog.MessageDialog; +import com.hjq.demo.ui.dialog.PayPasswordDialog; +import com.hjq.demo.ui.dialog.SelectDialog; +import com.hjq.demo.ui.dialog.ShareDialog; import com.hjq.demo.ui.dialog.TimeDialog; import com.hjq.demo.ui.dialog.ToastDialog; +import com.hjq.demo.ui.dialog.UpdateDialog; import com.hjq.demo.ui.dialog.WaitDialog; +import com.hjq.demo.ui.popup.MenuPopup; +import com.hjq.demo.wxapi.WXEntryActivity; import com.hjq.umeng.Platform; import com.hjq.umeng.UmengClient; import com.hjq.umeng.UmengShare; import java.util.ArrayList; import java.util.Calendar; +import java.util.HashMap; import java.util.List; -import butterknife.OnClick; - /** * author : Android 轮子哥 * github : https://github.com/getActivity/AndroidProject @@ -49,7 +49,15 @@ protected int getLayoutId() { @Override protected void initView() { - + setOnClickListener(R.id.btn_dialog_message, R.id.btn_dialog_input, + R.id.btn_dialog_bottom_menu, R.id.btn_dialog_center_menu, + R.id.btn_dialog_single_select, R.id.btn_dialog_more_select, + R.id.btn_dialog_succeed_toast, R.id.btn_dialog_fail_toast, + R.id.btn_dialog_warn_toast, R.id.btn_dialog_wait, + R.id.btn_dialog_pay, R.id.btn_dialog_address, + R.id.btn_dialog_date, R.id.btn_dialog_time, + R.id.btn_dialog_update, R.id.btn_dialog_share, + R.id.btn_dialog_custom); } @Override @@ -57,14 +65,12 @@ protected void initData() { } - @OnClick({R.id.btn_dialog_message, R.id.btn_dialog_input, R.id.btn_dialog_bottom_menu, R.id.btn_dialog_center_menu, - R.id.btn_dialog_succeed_toast, R.id.btn_dialog_fail_toast, R.id.btn_dialog_warn_toast, R.id.btn_dialog_wait, - R.id.btn_dialog_pay, R.id.btn_dialog_address, R.id.btn_dialog_date, R.id.btn_dialog_time, - R.id.btn_dialog_update, R.id.btn_dialog_share, R.id.btn_dialog_custom}) + @SingleClick + @Override public void onClick(View v) { switch (v.getId()) { - // 消息对话框 case R.id.btn_dialog_message: + // 消息对话框 new MessageDialog.Builder(this) // 标题可以不用填写 .setTitle("我是标题") @@ -103,7 +109,8 @@ public void onCancel(BaseDialog dialog) { .setConfirm(getString(R.string.common_confirm)) // 设置 null 表示不显示取消按钮 .setCancel(getString(R.string.common_cancel)) - //.setAutoDismiss(false) // 设置点击按钮后不关闭对话框 + // 设置点击按钮后不关闭对话框 + //.setAutoDismiss(false) .setListener(new InputDialog.OnListener() { @Override @@ -171,6 +178,52 @@ public void onCancel(BaseDialog dialog) { }) .show(); break; + case R.id.btn_dialog_single_select: + // 单选对话框 + new SelectDialog.Builder(this) + .setTitle("请选择你的性别") + .setList("男", "女") + // 设置单选模式 + .setSingleSelect() + // 设置默认选中 + .setSelect(0) + .setListener(new SelectDialog.OnListener() { + + @Override + public void onSelected(BaseDialog dialog, HashMap data) { + toast("确定了" + data.toString()); + } + + @Override + public void onCancel(BaseDialog dialog) { + toast("取消了"); + } + }) + .show(); + break; + case R.id.btn_dialog_more_select: + // 多选对话框 + new SelectDialog.Builder(this) + .setTitle("请选择工作日") + .setList("星期一", "星期二", "星期三", "星期四", "星期五") + // 设置最大选择数 + .setMaxSelect(3) + // 设置默认选中 + .setSelect(2, 3, 4) + .setListener(new SelectDialog.OnListener() { + + @Override + public void onSelected(BaseDialog dialog, HashMap data) { + toast("确定了" + data.toString()); + } + + @Override + public void onCancel(BaseDialog dialog) { + toast("取消了"); + } + }) + .show(); + break; case R.id.btn_dialog_succeed_toast: // 成功对话框 new ToastDialog.Builder(this) @@ -194,16 +247,11 @@ public void onCancel(BaseDialog dialog) { break; case R.id.btn_dialog_wait: // 等待对话框 - final BaseDialog dialog = new WaitDialog.Builder(this) + final BaseDialog waitDialog = new WaitDialog.Builder(this) // 消息文本可以不用填写 .setMessage(getString(R.string.common_loading)) .show(); - postDelayed(new Runnable() { - @Override - public void run() { - dialog.dismiss(); - } - }, 2000); + postDelayed(waitDialog::dismiss, 2000); break; case R.id.btn_dialog_pay: // 支付密码输入对话框 @@ -366,55 +414,32 @@ public void onCancel(Platform platform) { .show(); break; case R.id.btn_dialog_update: + // 升级对话框 new UpdateDialog.Builder(this) // 版本名 - .setVersionName("v 2.0") - // 文件大小 - .setFileSize("10 M") + .setVersionName("2.0") // 是否强制更新 .setForceUpdate(false) // 更新日志 .setUpdateLog("到底更新了啥\n到底更新了啥\n到底更新了啥\n到底更新了啥\n到底更新了啥") - // 下载 url + // 下载 URL .setDownloadUrl("https://raw.githubusercontent.com/getActivity/AndroidProject/master/AndroidProject.apk") + // 文件 MD5 + //.setFileMD5("56A5A5712D1856BDBD4C2AECA9B1FFE7") .show(); break; case R.id.btn_dialog_custom: // 自定义对话框 - new BaseDialogFragment.Builder(this) + new BaseDialog.Builder(this) .setContentView(R.layout.dialog_custom) //.setText(id, "我是预设置的文本") - .setOnClickListener(R.id.btn_dialog_custom_ok, new BaseDialog.OnClickListener() { - - @Override - public void onClick(BaseDialog dialog, ImageView view) { - dialog.dismiss(); - } - }) - .addOnShowListener(new BaseDialog.OnShowListener() { - @Override - public void onShow(BaseDialog dialog) { - toast("Dialog 显示了"); - } - }) - .addOnCancelListener(new BaseDialog.OnCancelListener() { - @Override - public void onCancel(BaseDialog dialog) { - toast("Dialog 取消了"); - } - }) - .addOnDismissListener(new BaseDialog.OnDismissListener() { - @Override - public void onDismiss(BaseDialog dialog) { - toast("Dialog 销毁了"); - } - }) - .setOnKeyListener(new BaseDialog.OnKeyListener() { - @Override - public boolean onKey(BaseDialog dialog, KeyEvent event) { - toast("按键代码:" + event.getKeyCode()); - return false; - } + .setOnClickListener(R.id.btn_dialog_custom_ok, (BaseDialog.OnClickListener) (dialog, view) -> dialog.dismiss()) + .addOnShowListener(dialog -> toast("Dialog 显示了")) + .addOnCancelListener(dialog -> toast("Dialog 取消了")) + .addOnDismissListener(dialog -> toast("Dialog 销毁了")) + .setOnKeyListener((dialog, event) -> { + toast("按键代码:" + event.getKeyCode()); + return false; }) .show(); break; @@ -429,4 +454,13 @@ protected void onActivityResult(int requestCode, int resultCode, @Nullable Inten // 友盟分享回调 UmengClient.onActivityResult(this, requestCode, resultCode, data); } + + @Override + public void onRightClick(View v) { + // 菜单弹窗 + new MenuPopup.Builder(this) + .setList("选择拍照", "选取相册") + .setListener((MenuPopup.OnListener) (popupWindow, position, s) -> toast(s)) + .showAsDropDown(v); + } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/activity/GuideActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/GuideActivity.java new file mode 100644 index 00000000..18125f25 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/ui/activity/GuideActivity.java @@ -0,0 +1,86 @@ +package com.hjq.demo.ui.activity; + +import android.view.View; +import android.widget.ImageView; + +import androidx.viewpager.widget.ViewPager; + +import com.hjq.demo.R; +import com.hjq.demo.aop.SingleClick; +import com.hjq.demo.common.MyActivity; +import com.hjq.demo.ui.pager.GuidePagerAdapter; +import com.rd.PageIndicatorView; + +import butterknife.BindView; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/09/21 + * desc : APP 引导页 + */ +public final class GuideActivity extends MyActivity + implements ViewPager.OnPageChangeListener { + + @BindView(R.id.vp_guide_pager) + ViewPager mViewPager; + @BindView(R.id.pv_guide_indicator) + PageIndicatorView mIndicatorView; + @BindView(R.id.iv_guide_complete) + ImageView mCompleteView; + + private GuidePagerAdapter mPagerAdapter; + + @Override + protected int getLayoutId() { + return R.layout.activity_guide; + } + + @Override + protected void initView() { + mIndicatorView.setViewPager(mViewPager); + setOnClickListener(R.id.iv_guide_complete); + } + + @Override + protected void initData() { + mPagerAdapter = new GuidePagerAdapter(); + mViewPager.setAdapter(mPagerAdapter); + mViewPager.addOnPageChangeListener(this); + } + + @SingleClick + @Override + public void onClick(View v) { + if (v.getId() == R.id.iv_guide_complete) { + startActivity(HomeActivity.class); + finish(); + } + } + + /** + * {@link ViewPager.OnPageChangeListener} + */ + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + if (mViewPager.getCurrentItem() == mPagerAdapter.getCount() - 1 && positionOffsetPixels > 0) { + mCompleteView.setVisibility(View.INVISIBLE); + } + } + + @Override + public void onPageSelected(int position) {} + + @Override + public void onPageScrollStateChanged(int state) { + if (state == ViewPager.SCROLL_STATE_IDLE) { + mCompleteView.setVisibility(mViewPager.getCurrentItem() == mPagerAdapter.getCount() - 1 ? View.VISIBLE : View.INVISIBLE); + } + } + + @Override + public boolean isSwipeEnable() { + return false; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/activity/HomeActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/HomeActivity.java index 63216740..1ea8b819 100644 --- a/app/src/main/java/com/hjq/demo/ui/activity/HomeActivity.java +++ b/app/src/main/java/com/hjq/demo/ui/activity/HomeActivity.java @@ -11,7 +11,7 @@ import com.hjq.base.BaseFragmentAdapter; import com.hjq.demo.R; import com.hjq.demo.common.MyActivity; -import com.hjq.demo.common.MyLazyFragment; +import com.hjq.demo.common.MyFragment; import com.hjq.demo.helper.ActivityStackManager; import com.hjq.demo.helper.DoubleClickHelper; import com.hjq.demo.other.KeyboardWatcher; @@ -29,17 +29,15 @@ * desc : 主页界面 */ public final class HomeActivity extends MyActivity - implements ViewPager.OnPageChangeListener, - BottomNavigationView.OnNavigationItemSelectedListener, - KeyboardWatcher.SoftKeyboardStateListener { + implements KeyboardWatcher.SoftKeyboardStateListener, + BottomNavigationView.OnNavigationItemSelectedListener{ @BindView(R.id.vp_home_pager) ViewPager mViewPager; @BindView(R.id.bv_home_navigation) BottomNavigationView mBottomNavigationView; - /** ViewPager 适配器 */ - private BaseFragmentAdapter mPagerAdapter; + private BaseFragmentAdapter mPagerAdapter; @Override protected int getLayoutId() { @@ -48,8 +46,6 @@ protected int getLayoutId() { @Override protected void initView() { - mViewPager.addOnPageChangeListener(this); - // 不使用图标默认变色 mBottomNavigationView.setItemIconTintList(null); mBottomNavigationView.setOnNavigationItemSelectedListener(this); @@ -72,36 +68,6 @@ protected void initData() { mViewPager.setOffscreenPageLimit(mPagerAdapter.getCount()); } - /** - * {@link ViewPager.OnPageChangeListener} - */ - - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {} - - @Override - public void onPageSelected(int position) { - switch (position) { - case 0: - mBottomNavigationView.setSelectedItemId(R.id.menu_home); - break; - case 1: - mBottomNavigationView.setSelectedItemId(R.id.home_found); - break; - case 2: - mBottomNavigationView.setSelectedItemId(R.id.home_message); - break; - case 3: - mBottomNavigationView.setSelectedItemId(R.id.home_me); - break; - default: - break; - } - } - - @Override - public void onPageScrollStateChanged(int state) {} - /** * {@link BottomNavigationView.OnNavigationItemSelectedListener} */ @@ -110,16 +76,16 @@ public void onPageScrollStateChanged(int state) {} public boolean onNavigationItemSelected(@NonNull MenuItem item) { switch (item.getItemId()) { case R.id.menu_home: - mViewPager.setCurrentItem(0); + mPagerAdapter.setCurrentItem(TestFragmentA.class); return true; case R.id.home_found: - mViewPager.setCurrentItem(1); + mPagerAdapter.setCurrentItem(TestFragmentB.class); return true; case R.id.home_message: - mViewPager.setCurrentItem(2); + mPagerAdapter.setCurrentItem(TestFragmentC.class); return true; case R.id.home_me: - mViewPager.setCurrentItem(3); + mPagerAdapter.setCurrentItem(TestFragmentD.class); return true; default: break; @@ -152,17 +118,15 @@ public boolean onKeyDown(int keyCode, KeyEvent event) { @Override public void onBackPressed() { if (DoubleClickHelper.isOnDoubleClick()) { - //移动到上一个任务栈,避免侧滑引起的不良反应 + // 移动到上一个任务栈,避免侧滑引起的不良反应 moveTaskToBack(false); - postDelayed(new Runnable() { - - @Override - public void run() { - // 进行内存优化,销毁掉所有的界面 - ActivityStackManager.getInstance().finishAllActivities(); - // 销毁进程(请注意:调用此 API 可能导致当前 Activity onDestroy 方法无法正常回调) - // System.exit(0); - } + postDelayed(() -> { + + // 进行内存优化,销毁掉所有的界面 + ActivityStackManager.getInstance().finishAllActivities(); + // 销毁进程(注意:调用此 API 可能导致当前 Activity onDestroy 方法无法正常回调) + // System.exit(0); + }, 300); } else { toast(R.string.home_exit_hint); @@ -171,9 +135,13 @@ public void run() { @Override protected void onDestroy() { - mViewPager.removeOnPageChangeListener(this); mViewPager.setAdapter(null); mBottomNavigationView.setOnNavigationItemSelectedListener(null); super.onDestroy(); } + + @Override + public boolean isSwipeEnable() { + return false; + } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/activity/ImageActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/ImageActivity.java index a9697126..fd33754b 100644 --- a/app/src/main/java/com/hjq/demo/ui/activity/ImageActivity.java +++ b/app/src/main/java/com/hjq/demo/ui/activity/ImageActivity.java @@ -6,10 +6,13 @@ import androidx.viewpager.widget.ViewPager; import com.gyf.immersionbar.BarHide; +import com.gyf.immersionbar.ImmersionBar; import com.hjq.demo.R; +import com.hjq.demo.aop.CheckNet; +import com.hjq.demo.aop.DebugLog; import com.hjq.demo.common.MyActivity; import com.hjq.demo.other.IntentKey; -import com.hjq.demo.ui.adapter.ImagePagerAdapter; +import com.hjq.demo.ui.pager.ImagePagerAdapter; import com.rd.PageIndicatorView; import java.util.ArrayList; @@ -39,6 +42,8 @@ public static void start(Context context, ArrayList urls) { start(context, urls, 0); } + @CheckNet + @DebugLog public static void start(Context context, ArrayList urls, int index) { Intent intent = new Intent(context, ImageActivity.class); intent.putExtra(IntentKey.PICTURE, urls); @@ -53,23 +58,24 @@ protected int getLayoutId() { @Override protected void initView() { - // 设置状态栏和导航栏参数 - getStatusBarConfig() + mIndicatorView.setViewPager(mViewPager); + } + + @Override + protected ImmersionBar createStatusBarConfig() { + return super.createStatusBarConfig() // 有导航栏的情况下,activity全屏显示,也就是activity最下面被导航栏覆盖,不写默认非全屏 .fullScreen(true) // 隐藏状态栏 .hideBar(BarHide.FLAG_HIDE_STATUS_BAR) // 透明导航栏,不写默认黑色(设置此方法,fullScreen()方法自动为true) - .transparentNavigationBar() - .init(); - - mIndicatorView.setViewPager(mViewPager); + .transparentNavigationBar(); } @Override protected void initData() { - ArrayList images = getIntent().getStringArrayListExtra(IntentKey.PICTURE); - int index = getIntent().getIntExtra(IntentKey.INDEX, 0); + ArrayList images = getStringArrayList(IntentKey.PICTURE); + int index = getInt(IntentKey.INDEX); if (images != null && images.size() > 0) { mViewPager.setAdapter(new ImagePagerAdapter(this, images)); if (index != 0 && index <= images.size()) { @@ -81,7 +87,12 @@ protected void initData() { } @Override - public boolean statusBarDarkFont() { + public boolean isStatusBarDarkFont() { + return false; + } + + @Override + public boolean isSwipeEnable() { return false; } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/activity/LoginActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/LoginActivity.java index 5c2367ff..900f9ab6 100644 --- a/app/src/main/java/com/hjq/demo/ui/activity/LoginActivity.java +++ b/app/src/main/java/com/hjq/demo/ui/activity/LoginActivity.java @@ -2,6 +2,7 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.content.Context; import android.content.Intent; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; @@ -13,18 +14,25 @@ import androidx.annotation.Nullable; import com.hjq.demo.R; +import com.hjq.demo.aop.DebugLog; +import com.hjq.demo.aop.SingleClick; import com.hjq.demo.common.MyActivity; import com.hjq.demo.helper.InputTextHelper; +import com.hjq.demo.http.glide.GlideApp; +import com.hjq.demo.http.model.HttpData; +import com.hjq.demo.http.request.LoginApi; +import com.hjq.demo.http.response.LoginBean; import com.hjq.demo.other.IntentKey; import com.hjq.demo.other.KeyboardWatcher; import com.hjq.demo.wxapi.WXEntryActivity; -import com.hjq.image.ImageLoader; +import com.hjq.http.EasyConfig; +import com.hjq.http.EasyHttp; +import com.hjq.http.listener.HttpCallback; import com.hjq.umeng.Platform; import com.hjq.umeng.UmengClient; import com.hjq.umeng.UmengLogin; import butterknife.BindView; -import butterknife.OnClick; /** * author : Android 轮子哥 @@ -36,6 +44,14 @@ public final class LoginActivity extends MyActivity implements UmengLogin.OnLoginListener, KeyboardWatcher.SoftKeyboardStateListener { + @DebugLog + public static void start(Context context, String phone, String password) { + Intent intent = new Intent(context, LoginActivity.class); + intent.putExtra(IntentKey.PHONE, phone); + intent.putExtra(IntentKey.PASSWORD, password); + context.startActivity(intent); + } + @BindView(R.id.iv_login_logo) ImageView mLogoView; @@ -75,32 +91,25 @@ protected void initView() { .addView(mPhoneView) .addView(mPasswordView) .setMain(mCommitView) - .setListener(new InputTextHelper.OnInputTextListener() { - - @Override - public boolean onInputChange(InputTextHelper helper) { - return mPhoneView.getText().toString().length() == 11 && - mPasswordView.getText().toString().length() >= 6; - } - }) + .setListener(helper -> mPhoneView.getText().toString().length() == 11 && + mPasswordView.getText().toString().length() >= 6) .build(); - post(new Runnable() { - - @Override - public void run() { - // 因为在小屏幕手机上面,因为计算规则的因素会导致动画效果特别夸张,所以不在小屏幕手机上面展示这个动画效果 - if (mBlankView.getHeight() > mBodyLayout.getHeight()) { - // 只有空白区域的高度大于登录框区域的高度才展示动画 - KeyboardWatcher.with(LoginActivity.this) - .setListener(LoginActivity.this); - } - } - }); + setOnClickListener(R.id.tv_login_forget, R.id.btn_login_commit, R.id.iv_login_qq, R.id.iv_login_wx); } @Override protected void initData() { + + postDelayed(() -> { + // 因为在小屏幕手机上面,因为计算规则的因素会导致动画效果特别夸张,所以不在小屏幕手机上面展示这个动画效果 + if (mBlankView.getHeight() > mBodyLayout.getHeight()) { + // 只有空白区域的高度大于登录框区域的高度才展示动画 + KeyboardWatcher.with(LoginActivity.this) + .setListener(LoginActivity.this); + } + }, 500); + // 判断用户当前有没有安装 QQ if (!UmengClient.isAppInstalled(this, Platform.QQ)) { mQQView.setVisibility(View.GONE); @@ -115,26 +124,27 @@ protected void initData() { if (mQQView.getVisibility() == View.GONE && mWeChatView.getVisibility() == View.GONE) { mOtherView.setVisibility(View.GONE); } + + // 填充传入的手机号和密码 + mPhoneView.setText(getString(IntentKey.PHONE)); + mPasswordView.setText(getString(IntentKey.PASSWORD)); } @Override public void onRightClick(View v) { // 跳转到注册界面 - startActivityForResult(RegisterActivity.class, new ActivityCallback() { - - @Override - public void onActivityResult(int resultCode, @Nullable Intent data) { - // 如果已经注册成功,就执行登录操作 - if (resultCode == RESULT_OK && data != null) { - mPhoneView.setText(data.getStringExtra(IntentKey.PHONE)); - mPasswordView.setText(data.getStringExtra(IntentKey.PASSWORD)); - onClick(mCommitView); - } + startActivityForResult(RegisterActivity.class, (resultCode, data) -> { + // 如果已经注册成功,就执行登录操作 + if (resultCode == RESULT_OK && data != null) { + mPhoneView.setText(data.getStringExtra(IntentKey.PHONE)); + mPasswordView.setText(data.getStringExtra(IntentKey.PASSWORD)); + onClick(mCommitView); } }); } - @OnClick({R.id.tv_login_forget, R.id.btn_login_commit, R.id.iv_login_qq, R.id.iv_login_wx}) + @SingleClick + @Override public void onClick(View v) { switch (v.getId()) { case R.id.tv_login_forget: @@ -143,17 +153,31 @@ public void onClick(View v) { case R.id.btn_login_commit: if (mPhoneView.getText().toString().length() != 11) { toast(R.string.common_phone_input_error); - } else { - showLoading(); - postDelayed(new Runnable() { - @Override - public void run() { - showComplete(); - // 处理登录 - startActivityFinish(HomeActivity.class); - } - }, 2000); + return; + } + + if (true) { + startActivity(HomeActivity.class); + finish(); + return; } + + EasyHttp.post(this) + .api(new LoginApi() + .setPhone(mPhoneView.getText().toString()) + .setPassword(mPasswordView.getText().toString())) + .request(new HttpCallback>(this) { + + @Override + public void onSucceed(HttpData data) { + // 更新 Token + EasyConfig.getInstance() + .addParam("token", data.getData().getToken()); + // 跳转到主页 + startActivity(HomeActivity.class); + finish(); + } + }); break; case R.id.iv_login_qq: case R.id.iv_login_wx: @@ -185,7 +209,6 @@ protected void onActivityResult(int requestCode, int resultCode, @Nullable Inten } /** - * 友盟第三方登录授权回调接口 * {@link UmengLogin.OnLoginListener} */ @@ -207,9 +230,9 @@ public void onSucceed(Platform platform, UmengLogin.LoginData data) { break; } - ImageLoader.with(this) - .load(data.getIcon()) - .circle() + GlideApp.with(this) + .load(data.getAvatar()) + .circleCrop() .into(mLogoView); toast("昵称:" + data.getName() + "\n" + "性别:" + data.getSex()); @@ -293,4 +316,9 @@ public void onSoftKeyboardClosed() { animatorSet.setDuration(mAnimTime); animatorSet.start(); } + + @Override + public boolean isSwipeEnable() { + return false; + } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/activity/PasswordForgetActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/PasswordForgetActivity.java index fe2444c0..cecc16b6 100644 --- a/app/src/main/java/com/hjq/demo/ui/activity/PasswordForgetActivity.java +++ b/app/src/main/java/com/hjq/demo/ui/activity/PasswordForgetActivity.java @@ -5,12 +5,17 @@ import android.widget.EditText; import com.hjq.demo.R; +import com.hjq.demo.aop.SingleClick; import com.hjq.demo.common.MyActivity; import com.hjq.demo.helper.InputTextHelper; +import com.hjq.demo.http.model.HttpData; +import com.hjq.demo.http.request.GetCodeApi; +import com.hjq.demo.http.request.VerifyCodeApi; +import com.hjq.http.EasyHttp; +import com.hjq.http.listener.HttpCallback; import com.hjq.widget.view.CountdownView; import butterknife.BindView; -import butterknife.OnClick; /** * author : Android 轮子哥 @@ -40,14 +45,10 @@ protected void initView() { .addView(mPhoneView) .addView(mCodeView) .setMain(mCommitView) - .setListener(new InputTextHelper.OnInputTextListener() { - - @Override - public boolean onInputChange(InputTextHelper helper) { - return mPhoneView.getText().toString().length() == 11 && mCodeView.getText().toString().length() == 4; - } - }) + .setListener(helper -> mPhoneView.getText().toString().length() == 11 && mCodeView.getText().toString().length() == 4) .build(); + + setOnClickListener(R.id.cv_password_forget_countdown, R.id.btn_password_forget_commit); } @Override @@ -55,22 +56,55 @@ protected void initData() { } - @OnClick({R.id.cv_password_forget_countdown, R.id.btn_password_forget_commit}) + @SingleClick + @Override public void onClick(View v) { switch (v.getId()) { case R.id.cv_password_forget_countdown: if (mPhoneView.getText().toString().length() != 11) { - // 重置验证码倒计时控件 - mCountdownView.resetState(); toast(R.string.common_phone_input_error); - } else { - // 获取验证码 + return; + } + + if (true) { toast(R.string.common_code_send_hint); + mCountdownView.start(); + return; } + + // 获取验证码 + EasyHttp.post(this) + .api(new GetCodeApi() + .setPhone(mPhoneView.getText().toString())) + .request(new HttpCallback>(this) { + + @Override + public void onSucceed(HttpData data) { + toast(R.string.common_code_send_hint); + mCountdownView.start(); + } + }); break; case R.id.btn_password_forget_commit: - // 重置密码 - startActivityFinish(PasswordResetActivity.class); + if (true) { + PasswordResetActivity.start(getActivity(), mPhoneView.getText().toString(), mCodeView.getText().toString()); + finish(); + return; + } + + // 验证码校验 + EasyHttp.post(this) + .api(new VerifyCodeApi() + .setPhone(mPhoneView.getText().toString()) + .setCode(mCodeView.getText().toString())) + .request(new HttpCallback>(this) { + + @Override + public void onSucceed(HttpData data) { + PasswordResetActivity.start(getActivity(), mPhoneView.getText().toString(), mCodeView.getText().toString()); + finish(); + } + }); break; default: break; diff --git a/app/src/main/java/com/hjq/demo/ui/activity/PasswordResetActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/PasswordResetActivity.java index 2dd2f87d..f7eaa997 100644 --- a/app/src/main/java/com/hjq/demo/ui/activity/PasswordResetActivity.java +++ b/app/src/main/java/com/hjq/demo/ui/activity/PasswordResetActivity.java @@ -1,15 +1,23 @@ package com.hjq.demo.ui.activity; +import android.content.Context; +import android.content.Intent; import android.view.View; import android.widget.Button; import android.widget.EditText; import com.hjq.demo.R; +import com.hjq.demo.aop.DebugLog; +import com.hjq.demo.aop.SingleClick; import com.hjq.demo.common.MyActivity; import com.hjq.demo.helper.InputTextHelper; +import com.hjq.demo.http.model.HttpData; +import com.hjq.demo.http.request.PasswordApi; +import com.hjq.demo.other.IntentKey; +import com.hjq.http.EasyHttp; +import com.hjq.http.listener.HttpCallback; import butterknife.BindView; -import butterknife.OnClick; /** * author : Android 轮子哥 @@ -19,6 +27,14 @@ */ public final class PasswordResetActivity extends MyActivity { + @DebugLog + public static void start(Context context, String phone, String code) { + Intent intent = new Intent(context, PasswordResetActivity.class); + intent.putExtra(IntentKey.PHONE, phone); + intent.putExtra(IntentKey.CODE, code); + context.startActivity(intent); + } + @BindView(R.id.et_password_reset_password1) EditText mPasswordView1; @BindView(R.id.et_password_reset_password2) @@ -26,6 +42,11 @@ public final class PasswordResetActivity extends MyActivity { @BindView(R.id.btn_password_reset_commit) Button mCommitView; + /** 手机号 */ + private String mPhone; + /** 验证码 */ + private String mCode; + @Override protected int getLayoutId() { return R.layout.activity_password_reset; @@ -37,31 +58,42 @@ protected void initView() { .addView(mPasswordView1) .addView(mPasswordView2) .setMain(mCommitView) - .setListener(new InputTextHelper.OnInputTextListener() { - - @Override - public boolean onInputChange(InputTextHelper helper) { - return mPasswordView1.getText().toString().length() >= 6 && - mPasswordView1.getText().toString().equals(mPasswordView2.getText().toString()); - } - }) + .setListener(helper -> mPasswordView1.getText().toString().length() >= 6 && + mPasswordView1.getText().toString().equals(mPasswordView2.getText().toString())) .build(); + setOnClickListener(R.id.btn_password_reset_commit); } @Override protected void initData() { - + mPhone = getString(IntentKey.PHONE); + mCode = getString(IntentKey.CODE); } - @OnClick({R.id.btn_password_reset_commit}) + @SingleClick + @Override public void onClick(View v) { - switch (v.getId()) { - case R.id.btn_password_reset_commit: + if (v.getId() == R.id.btn_password_reset_commit) { + if (true) { toast(R.string.password_reset_success); finish(); - break; - default: - break; + return; + } + + // 重置密码 + EasyHttp.post(this) + .api(new PasswordApi() + .setPhone(mPhone) + .setCode(mCode) + .setPassword(mPasswordView1.getText().toString())) + .request(new HttpCallback>(this) { + + @Override + public void onSucceed(HttpData data) { + toast(R.string.password_reset_success); + finish(); + } + }); } } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/activity/PersonalDataActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/PersonalDataActivity.java index 5f25dd11..781e2db3 100644 --- a/app/src/main/java/com/hjq/demo/ui/activity/PersonalDataActivity.java +++ b/app/src/main/java/com/hjq/demo/ui/activity/PersonalDataActivity.java @@ -3,18 +3,22 @@ import android.view.View; import android.widget.ImageView; -import com.hjq.base.BaseDialog; import com.hjq.demo.R; +import com.hjq.demo.aop.SingleClick; import com.hjq.demo.common.MyActivity; +import com.hjq.demo.http.glide.GlideApp; +import com.hjq.demo.http.model.HttpData; +import com.hjq.demo.http.request.UpdateImageApi; import com.hjq.demo.ui.dialog.AddressDialog; import com.hjq.demo.ui.dialog.InputDialog; -import com.hjq.image.ImageLoader; +import com.hjq.http.EasyHttp; +import com.hjq.http.listener.HttpCallback; import com.hjq.widget.layout.SettingBar; +import java.io.File; import java.util.List; import butterknife.BindView; -import butterknife.OnClick; /** * author : Android 轮子哥 @@ -35,6 +39,14 @@ public final class PersonalDataActivity extends MyActivity { @BindView(R.id.sb_person_data_phone) SettingBar mPhoneView; + /** 省 */ + private String mProvince = "广东省"; + /** 市 */ + private String mCity = "广州市"; + /** 区 */ + private String mArea = "天河区"; + + /** 头像地址 */ private String mAvatarUrl; @Override @@ -44,15 +56,25 @@ protected int getLayoutId() { @Override protected void initView() { - + setOnClickListener(R.id.iv_person_data_avatar, R.id.fl_person_data_head, + R.id.sb_person_data_name, R.id.sb_person_data_address, R.id.sb_person_data_phone); } @Override protected void initData() { - + GlideApp.with(getActivity()) + .load(R.drawable.ic_head_placeholder) + .placeholder(R.drawable.ic_head_placeholder) + .error(R.drawable.ic_head_placeholder) + .circleCrop() + .into(mAvatarView); + + String address = mProvince + mCity + mArea; + mAddressView.setRightText(address); } - @OnClick({R.id.iv_person_data_avatar, R.id.fl_person_data_head, R.id.sb_person_data_name, R.id.sb_person_data_address, R.id.sb_person_data_phone}) + @SingleClick + @Override public void onClick(View v) { switch (v.getId()) { case R.id.iv_person_data_avatar: @@ -68,11 +90,28 @@ public void onClick(View v) { PhotoActivity.start(getActivity(), new PhotoActivity.OnPhotoSelectListener() { @Override - public void onSelect(List data) { - mAvatarUrl = data.get(0); - ImageLoader.with(getActivity()) - .load(mAvatarUrl) - .into(mAvatarView); + public void onSelected(List data) { + if (true) { + mAvatarUrl = data.get(0); + GlideApp.with(getActivity()) + .load(mAvatarUrl) + .into(mAvatarView); + return; + } + // 上传头像 + EasyHttp.post(getActivity()) + .api(new UpdateImageApi() + .setImage(new File(data.get(0)))) + .request(new HttpCallback>(PersonalDataActivity.this) { + + @Override + public void onSucceed(HttpData data) { + mAvatarUrl = data.getData(); + GlideApp.with(getActivity()) + .load(mAvatarUrl) + .into(mAvatarView); + } + }); } @Override @@ -90,17 +129,10 @@ public void onCancel() {} //.setCancel("取消") // 设置点击按钮后不关闭对话框 //.setAutoDismiss(false) - .setListener(new InputDialog.OnListener() { - - @Override - public void onConfirm(BaseDialog dialog, String content) { - if (!mNameView.getRightText().equals(content)) { - mNameView.setRightText(content); - } + .setListener((dialog, content) -> { + if (!mNameView.getRightText().equals(content)) { + mNameView.setRightText(content); } - - @Override - public void onCancel(BaseDialog dialog) {} }) .show(); break; @@ -108,23 +140,19 @@ public void onCancel(BaseDialog dialog) {} new AddressDialog.Builder(this) //.setTitle("选择地区") // 设置默认省份 - .setProvince("广东省") + .setProvince(mProvince) // 设置默认城市(必须要先设置默认省份) - .setCity("广州市") + .setCity(mCity) // 不选择县级区域 //.setIgnoreArea() - .setListener(new AddressDialog.OnListener() { - - @Override - public void onSelected(BaseDialog dialog, String province, String city, String area) { - String address = province + city + area; - if (!mAddressView.getRightText().equals(address)) { - mAddressView.setRightText(address); - } + .setListener((dialog, province, city, area) -> { + String address = province + city + area; + if (!mAddressView.getRightText().equals(address)) { + mProvince = province; + mCity = city; + mArea = area; + mAddressView.setRightText(address); } - - @Override - public void onCancel(BaseDialog dialog) {} }) .show(); break; diff --git a/app/src/main/java/com/hjq/demo/ui/activity/PhoneResetActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/PhoneResetActivity.java index 226eed81..b79b4651 100644 --- a/app/src/main/java/com/hjq/demo/ui/activity/PhoneResetActivity.java +++ b/app/src/main/java/com/hjq/demo/ui/activity/PhoneResetActivity.java @@ -1,16 +1,25 @@ package com.hjq.demo.ui.activity; +import android.content.Context; +import android.content.Intent; import android.view.View; import android.widget.Button; import android.widget.EditText; import com.hjq.demo.R; +import com.hjq.demo.aop.DebugLog; +import com.hjq.demo.aop.SingleClick; import com.hjq.demo.common.MyActivity; import com.hjq.demo.helper.InputTextHelper; +import com.hjq.demo.http.model.HttpData; +import com.hjq.demo.http.request.GetCodeApi; +import com.hjq.demo.http.request.PhoneApi; +import com.hjq.demo.other.IntentKey; +import com.hjq.http.EasyHttp; +import com.hjq.http.listener.HttpCallback; import com.hjq.widget.view.CountdownView; import butterknife.BindView; -import butterknife.OnClick; /** * author : Android 轮子哥 @@ -20,6 +29,13 @@ */ public final class PhoneResetActivity extends MyActivity { + @DebugLog + public static void start(Context context, String code) { + Intent intent = new Intent(context, PasswordResetActivity.class); + intent.putExtra(IntentKey.CODE, code); + context.startActivity(intent); + } + @BindView(R.id.et_phone_reset_phone) EditText mPhoneView; @BindView(R.id.et_phone_reset_code) @@ -31,6 +47,9 @@ public final class PhoneResetActivity extends MyActivity { @BindView(R.id.btn_phone_reset_commit) Button mCommitView; + /** 验证码 */ + private String mCode; + @Override protected int getLayoutId() { return R.layout.activity_phone_reset; @@ -42,38 +61,68 @@ protected void initView() { .addView(mPhoneView) .addView(mCodeView) .setMain(mCommitView) - .setListener(new InputTextHelper.OnInputTextListener() { - - @Override - public boolean onInputChange(InputTextHelper helper) { - return mPhoneView.getText().toString().length() == 11 && mCodeView.getText().toString().length() == 4; - } - }) + .setListener(helper -> mPhoneView.getText().toString().length() == 11 && mCodeView.getText().toString().length() == 4) .build(); + + setOnClickListener(R.id.cv_phone_reset_countdown, R.id.btn_phone_reset_commit); } @Override protected void initData() { - + mCode = getString(IntentKey.CODE); } - @OnClick({R.id.cv_phone_reset_countdown, R.id.btn_phone_reset_commit}) + @SingleClick + @Override public void onClick(View v) { switch (v.getId()) { case R.id.cv_phone_reset_countdown: // 获取验证码 if (mPhoneView.getText().toString().length() != 11) { - // 重置验证码倒计时控件 - mCountdownView.resetState(); toast(R.string.common_phone_input_error); - } else { + return; + } + + if (true) { toast(R.string.common_code_send_hint); + mCountdownView.start(); + return; } + + // 获取验证码 + EasyHttp.post(this) + .api(new GetCodeApi() + .setPhone(mPhoneView.getText().toString())) + .request(new HttpCallback>(this) { + + @Override + public void onSucceed(HttpData data) { + toast(R.string.common_code_send_hint); + mCountdownView.start(); + } + }); break; case R.id.btn_phone_reset_commit: + if (true) { + toast(R.string.phone_reset_commit_succeed); + finish(); + return; + } + // 更换手机号 - toast(R.string.phone_reset_commit_succeed); - finish(); + EasyHttp.post(this) + .api(new PhoneApi() + .setPreCode(mCode) + .setPhone(mPhoneView.getText().toString()) + .setCode(mCodeView.getText().toString())) + .request(new HttpCallback>(this) { + + @Override + public void onSucceed(HttpData data) { + toast(R.string.phone_reset_commit_succeed); + finish(); + } + }); break; default: break; diff --git a/app/src/main/java/com/hjq/demo/ui/activity/PhoneVerifyActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/PhoneVerifyActivity.java index 0ae93306..81b93636 100644 --- a/app/src/main/java/com/hjq/demo/ui/activity/PhoneVerifyActivity.java +++ b/app/src/main/java/com/hjq/demo/ui/activity/PhoneVerifyActivity.java @@ -6,12 +6,17 @@ import android.widget.TextView; import com.hjq.demo.R; +import com.hjq.demo.aop.SingleClick; import com.hjq.demo.common.MyActivity; import com.hjq.demo.helper.InputTextHelper; +import com.hjq.demo.http.model.HttpData; +import com.hjq.demo.http.request.GetCodeApi; +import com.hjq.demo.http.request.VerifyCodeApi; +import com.hjq.http.EasyHttp; +import com.hjq.http.listener.HttpCallback; import com.hjq.widget.view.CountdownView; import butterknife.BindView; -import butterknife.OnClick; /** * author : Android 轮子哥 @@ -23,7 +28,6 @@ public final class PhoneVerifyActivity extends MyActivity { @BindView(R.id.tv_phone_verify_phone) TextView mPhoneView; - @BindView(R.id.et_phone_verify_code) EditText mCodeView; @BindView(R.id.cv_phone_verify_countdown) @@ -41,14 +45,10 @@ protected void initView() { InputTextHelper.with(this) .addView(mCodeView) .setMain(mCommitView) - .setListener(new InputTextHelper.OnInputTextListener() { - - @Override - public boolean onInputChange(InputTextHelper helper) { - return mCodeView.getText().toString().length() == 4; - } - }) + .setListener(helper -> mCodeView.getText().toString().length() == 4) .build(); + + setOnClickListener(R.id.cv_phone_verify_countdown, R.id.btn_phone_verify_commit); } @Override @@ -56,22 +56,50 @@ protected void initData() { mPhoneView.setText(String.format(getString(R.string.phone_verify_current_phone), "18888888888")); } - @OnClick({R.id.cv_phone_verify_countdown, R.id.btn_phone_verify_commit}) + @SingleClick + @Override public void onClick(View v) { switch (v.getId()) { case R.id.cv_phone_verify_countdown: - // 获取验证码 - if (mPhoneView.getText().toString().length() != 11) { - // 重置验证码倒计时控件 - mCountdownView.resetState(); - toast(R.string.common_phone_input_error); - } else { + if (true) { toast(R.string.common_code_send_hint); + return; } + + // 获取验证码 + EasyHttp.post(this) + .api(new GetCodeApi() + .setPhone(mPhoneView.getText().toString())) + .request(new HttpCallback>(this) { + + @Override + public void onSucceed(HttpData data) { + toast(R.string.common_code_send_hint); + } + }); break; case R.id.btn_phone_verify_commit: - // 修改手机号 - startActivityFinish(PhoneResetActivity.class); + if (true) { + // 跳转到绑定手机号页面 + PhoneResetActivity.start(getActivity(), mCodeView.getText().toString()); + finish(); + return; + } + + // 验证码校验 + EasyHttp.post(this) + .api(new VerifyCodeApi() + .setPhone(mPhoneView.getText().toString()) + .setCode(mCodeView.getText().toString())) + .request(new HttpCallback>(this) { + + @Override + public void onSucceed(HttpData data) { + // 跳转到绑定手机号页面 + PhoneResetActivity.start(getActivity(), mCodeView.getText().toString()); + finish(); + } + }); break; default: break; diff --git a/app/src/main/java/com/hjq/demo/ui/activity/PhotoActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/PhotoActivity.java index 8db2afd7..852ff175 100644 --- a/app/src/main/java/com/hjq/demo/ui/activity/PhotoActivity.java +++ b/app/src/main/java/com/hjq/demo/ui/activity/PhotoActivity.java @@ -3,44 +3,38 @@ import android.content.ContentResolver; import android.content.Intent; import android.database.Cursor; -import android.media.MediaScannerConnection; import android.net.Uri; -import android.os.Build; -import android.os.Environment; import android.provider.MediaStore; import android.text.TextUtils; import android.util.TypedValue; import android.view.View; import android.view.animation.AnimationUtils; -import androidx.annotation.Nullable; -import androidx.core.content.FileProvider; import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.hjq.base.BaseActivity; -import com.hjq.base.BaseDialog; -import com.hjq.base.BaseRecyclerViewAdapter; +import com.hjq.base.BaseAdapter; import com.hjq.demo.R; +import com.hjq.demo.action.StatusAction; +import com.hjq.demo.aop.DebugLog; +import com.hjq.demo.aop.Permissions; +import com.hjq.demo.aop.SingleClick; import com.hjq.demo.common.MyActivity; -import com.hjq.demo.other.AppConfig; +import com.hjq.demo.other.GridSpaceDecoration; import com.hjq.demo.other.IntentKey; -import com.hjq.demo.other.PhotoSpaceDecoration; import com.hjq.demo.ui.adapter.PhotoAdapter; import com.hjq.demo.ui.dialog.AlbumDialog; -import com.hjq.permissions.OnPermission; +import com.hjq.demo.widget.HintLayout; import com.hjq.permissions.Permission; -import com.hjq.permissions.XXPermissions; import java.io.File; -import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Set; import butterknife.BindView; -import butterknife.OnClick; /** * author : Android 轮子哥 @@ -49,14 +43,17 @@ * desc : 图片选择 */ public final class PhotoActivity extends MyActivity - implements BaseRecyclerViewAdapter.OnItemClickListener, - BaseRecyclerViewAdapter.OnItemLongClickListener, - BaseRecyclerViewAdapter.OnChildClickListener, Runnable { + implements StatusAction, + BaseAdapter.OnItemClickListener, + BaseAdapter.OnItemLongClickListener, + BaseAdapter.OnChildClickListener, Runnable { public static void start(BaseActivity activity, OnPhotoSelectListener listener) { start(activity, 1, listener); } + @DebugLog + @Permissions({Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE}) public static void start(BaseActivity activity, int maxSelect, OnPhotoSelectListener listener) { if (maxSelect < 1) { // 最少要选择一个图片 @@ -64,23 +61,22 @@ public static void start(BaseActivity activity, int maxSelect, OnPhotoSelectList } Intent intent = new Intent(activity, PhotoActivity.class); intent.putExtra(IntentKey.AMOUNT, maxSelect); - activity.startActivityForResult(intent, new ActivityCallback() { + activity.startActivityForResult(intent, (resultCode, data) -> { - @Override - public void onActivityResult(int resultCode, @Nullable Intent data) { - if (listener == null || data == null) { - return; - } + if (listener == null || data == null) { + return; + } - if (resultCode == RESULT_OK) { - listener.onSelect(data.getStringArrayListExtra(IntentKey.PICTURE)); - } else { - listener.onCancel(); - } + if (resultCode == RESULT_OK) { + listener.onSelected(data.getStringArrayListExtra(IntentKey.PICTURE)); + } else { + listener.onCancel(); } }); } + @BindView(R.id.hl_photo_hint) + HintLayout mHintLayout; @BindView(R.id.rv_photo_list) RecyclerView mRecyclerView; @BindView(R.id.fab_photo_floating) @@ -113,37 +109,25 @@ protected void initView() { // 禁用动画效果 mRecyclerView.setItemAnimator(null); // 添加分割线 - mRecyclerView.addItemDecoration(new PhotoSpaceDecoration((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics()))); + mRecyclerView.addItemDecoration(new GridSpaceDecoration((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics()))); + setOnClickListener(R.id.fab_photo_floating); } + @SuppressWarnings("all") @Override protected void initData() { // 获取最大的选择数 - mMaxSelect = getIntent().getIntExtra(IntentKey.AMOUNT, mMaxSelect); - - // 申请存储权限 - XXPermissions.with(this) - .permission(Permission.Group.STORAGE) - .constantRequest() - .request(new OnPermission() { - @Override - public void hasPermission(List granted, boolean isAll) { - // 显示加载进度条 - showLoading(); - // 加载图片列表 - new Thread(PhotoActivity.this).start(); - } + mMaxSelect = getInt(IntentKey.AMOUNT, mMaxSelect); - @Override - public void noPermission(List denied, boolean quick) { - if (quick) { - toast(R.string.common_permission_fail); - XXPermissions.gotoPermissionSettings(PhotoActivity.this, false); - } else { - toast(R.string.common_permission_hint); - } - } - }); + // 显示加载进度条 + showLoading(); + // 加载图片列表 + new Thread(PhotoActivity.this).start(); + } + + @Override + public HintLayout getHintLayout() { + return mHintLayout; } @Override @@ -164,21 +148,19 @@ public void onRightClick(View v) { new AlbumDialog.Builder(this) .setData(data) - .setListener(new AlbumDialog.OnListener() { - @Override - public void onSelected(BaseDialog dialog, int position, AlbumDialog.AlbumBean bean) { - setRightTitle(bean.getName()); - // 滚动回第一个位置 - mRecyclerView.scrollToPosition(0); - if (position == 0) { - mAdapter.setData(mAllPhoto); - } else { - mAdapter.setData(mAllAlbum.get(bean.getName())); - } - // 执行列表动画 - mRecyclerView.setLayoutAnimation(AnimationUtils.loadLayoutAnimation(getActivity(), R.anim.layout_animation_from_right)); - mRecyclerView.scheduleLayoutAnimation(); + .setListener((dialog, position, bean) -> { + + setRightTitle(bean.getName()); + // 滚动回第一个位置 + mRecyclerView.scrollToPosition(0); + if (position == 0) { + mAdapter.setData(mAllPhoto); + } else { + mAdapter.setData(mAllAlbum.get(bean.getName())); } + // 执行列表动画 + mRecyclerView.setLayoutAnimation(AnimationUtils.loadLayoutAnimation(getActivity(), R.anim.layout_from_right)); + mRecyclerView.scheduleLayoutAnimation(); }) .show(); } @@ -193,44 +175,44 @@ protected void onRestart() { mSelectPhoto.remove(path); mAllPhoto.remove(path); - List data = mAllAlbum.get(file.getParentFile().getName()); - if (data != null) { - data.remove(path); - } - mAdapter.notifyDataSetChanged(); - if (mSelectPhoto.isEmpty()) { - mFloatingView.setImageResource(R.drawable.ic_photo_camera); - } else { - mFloatingView.setImageResource(R.drawable.ic_photo_succeed); + File parentFile = file.getParentFile(); + if (parentFile != null) { + List data = mAllAlbum.get(parentFile.getName()); + if (data != null) { + data.remove(path); + } + mAdapter.notifyDataSetChanged(); + + if (mSelectPhoto.isEmpty()) { + mFloatingView.setImageResource(R.drawable.ic_photo_camera); + } else { + mFloatingView.setImageResource(R.drawable.ic_photo_succeed); + } } } } } - @OnClick(R.id.fab_photo_floating) + @SingleClick + @Override public void onClick(View v) { if (v.getId() == R.id.fab_photo_floating) { if (mSelectPhoto.isEmpty()) { - XXPermissions.with(this) - .permission(Permission.CAMERA) - .request(new OnPermission() { - @Override - public void hasPermission(List granted, boolean isAll) { - // 点击拍照 - launchCamera(); - } - - @Override - public void noPermission(List denied, boolean quick) { - if (quick) { - toast(R.string.common_permission_fail); - XXPermissions.gotoPermissionSettings(PhotoActivity.this, true); - } else { - toast(R.string.common_permission_hint); - } - } - }); + // 点击拍照 + CameraActivity.start(this, file -> { + + // 当前选中图片的数量必须小于最大选中数 + if (mSelectPhoto.size() < mMaxSelect) { + mSelectPhoto.add(file.getPath()); + } + + // 这里需要延迟刷新,否则可能会找不到拍照的图片 + postDelayed(() -> { + // 重新加载图片列表 + new Thread(PhotoActivity.this).start(); + }, 1000); + }); } else { // 完成选择 setResult(RESULT_OK, new Intent().putStringArrayListExtra(IntentKey.PICTURE, mSelectPhoto)); @@ -240,7 +222,7 @@ public void noPermission(List denied, boolean quick) { } /** - * {@link BaseRecyclerViewAdapter.OnItemClickListener} + * {@link BaseAdapter.OnItemClickListener} * @param recyclerView RecyclerView对象 * @param itemView 被点击的条目对象 * @param position 被点击的条目位置 @@ -255,7 +237,7 @@ public void onItemClick(RecyclerView recyclerView, View itemView, int position) } /** - * {@link BaseRecyclerViewAdapter.OnItemLongClickListener} + * {@link BaseAdapter.OnItemLongClickListener} * @param recyclerView RecyclerView对象 * @param itemView 被点击的条目对象 * @param position 被点击的条目位置 @@ -271,52 +253,55 @@ public boolean onItemLongClick(RecyclerView recyclerView, View itemView, int pos } /** - * {@link BaseRecyclerViewAdapter.OnChildClickListener} + * {@link BaseAdapter.OnChildClickListener} * @param recyclerView RecyclerView对象 * @param childView 被点击的条目子 View Id * @param position 被点击的条目位置 */ @Override public void onChildClick(RecyclerView recyclerView, View childView, int position) { - switch (childView.getId()) { - case R.id.fl_photo_check: - if (mSelectPhoto.contains(mAdapter.getItem(position))) { - mSelectPhoto.remove(mAdapter.getItem(position)); + if (childView.getId() == R.id.fl_photo_check) { + if (mSelectPhoto.contains(mAdapter.getItem(position))) { + mSelectPhoto.remove(mAdapter.getItem(position)); - if (mSelectPhoto.isEmpty()) { - mFloatingView.hide(); - postDelayed(new Runnable() { - @Override - public void run() { - mFloatingView.setImageResource(R.drawable.ic_photo_camera); - mFloatingView.show(); - } - }, 200); - } + if (mSelectPhoto.isEmpty()) { + mFloatingView.hide(); + postDelayed(() -> { + mFloatingView.setImageResource(R.drawable.ic_photo_camera); + mFloatingView.show(); + }, 200); + } - } else { + } else { - if (mSelectPhoto.size() < mMaxSelect) { - mSelectPhoto.add(mAdapter.getItem(position)); - - if (mSelectPhoto.size() == 1) { - mFloatingView.hide(); - postDelayed(new Runnable() { - @Override - public void run() { - mFloatingView.setImageResource(R.drawable.ic_photo_succeed); - mFloatingView.show(); - } - }, 200); + if (mMaxSelect == 1 && mSelectPhoto.size() == 1) { + + List data = mAdapter.getData(); + if (data != null) { + int index = data.indexOf(mSelectPhoto.get(0)); + if (index != -1) { + mSelectPhoto.remove(0); + mAdapter.notifyItemChanged(index); } - } else { - toast(String.format(getString(R.string.photo_max_hint), mMaxSelect)); } + mSelectPhoto.add(mAdapter.getItem(position)); + + } else if (mSelectPhoto.size() < mMaxSelect) { + + mSelectPhoto.add(mAdapter.getItem(position)); + + if (mSelectPhoto.size() == 1) { + mFloatingView.hide(); + postDelayed(() -> { + mFloatingView.setImageResource(R.drawable.ic_photo_succeed); + mFloatingView.show(); + }, 200); + } + } else { + toast(String.format(getString(R.string.photo_max_hint), mMaxSelect)); } - mAdapter.notifyItemChanged(position); - break; - default: - break; + } + mAdapter.notifyItemChanged(position); } } @@ -338,16 +323,10 @@ public void run() { ContentResolver contentResolver = getContentResolver(); String[] projections; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - projections = new String[]{MediaStore.Files.FileColumns._ID, MediaStore.MediaColumns.DATA, - MediaStore.MediaColumns.DISPLAY_NAME, MediaStore.MediaColumns.DATE_MODIFIED, - MediaStore.MediaColumns.MIME_TYPE, MediaStore.MediaColumns.WIDTH, MediaStore - .MediaColumns.HEIGHT, MediaStore.MediaColumns.SIZE, MediaStore.Video.Media.DURATION}; - } else { - projections = new String[]{MediaStore.MediaColumns._ID, MediaStore.MediaColumns.DATA, - MediaStore.MediaColumns.DISPLAY_NAME, MediaStore.MediaColumns.DATE_MODIFIED, - MediaStore.MediaColumns.MIME_TYPE, MediaStore.MediaColumns.SIZE, MediaStore.Video.Media.DURATION}; - } + projections = new String[]{MediaStore.Files.FileColumns._ID, MediaStore.MediaColumns.DATA, + MediaStore.MediaColumns.DISPLAY_NAME, MediaStore.MediaColumns.DATE_MODIFIED, + MediaStore.MediaColumns.MIME_TYPE, MediaStore.MediaColumns.WIDTH, MediaStore + .MediaColumns.HEIGHT, MediaStore.MediaColumns.SIZE}; Cursor cursor = contentResolver.query(contentUri, projections, selection, selectionAllArgs, sortOrder); if (cursor != null && cursor.moveToFirst()) { @@ -355,12 +334,8 @@ public void run() { int pathIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DATA); int mimeTypeIndex = cursor.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE); int sizeIndex = cursor.getColumnIndex(MediaStore.MediaColumns.SIZE); - int widthIndex = 0; - int heightIndex = 0; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - widthIndex = cursor.getColumnIndex(MediaStore.MediaColumns.WIDTH); - heightIndex = cursor.getColumnIndex(MediaStore.MediaColumns.HEIGHT); - } + int widthIndex = cursor.getColumnIndex(MediaStore.MediaColumns.WIDTH); + int heightIndex = cursor.getColumnIndex(MediaStore.MediaColumns.HEIGHT); do { long size = cursor.getLong(sizeIndex); @@ -374,12 +349,10 @@ public void run() { continue; } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - int width = cursor.getInt(widthIndex); - int height = cursor.getInt(heightIndex); - if (width < 1 || height < 1) { - continue; - } + int width = cursor.getInt(widthIndex); + int height = cursor.getInt(heightIndex); + if (width < 1 || height < 1) { + continue; } File file = new File(path); @@ -387,138 +360,64 @@ public void run() { continue; } - // 专辑名称 - String albumName = file.getParentFile().getName(); - List files = mAllAlbum.get(albumName); - if (files == null) { - files = new ArrayList<>(); - mAllAlbum.put(albumName, files); + File parentFile = file.getParentFile(); + if (parentFile != null) { + // 获取目录名作为专辑名称 + String albumName = parentFile.getName(); + List files = mAllAlbum.get(albumName); + if (files == null) { + files = new ArrayList<>(); + mAllAlbum.put(albumName, files); + } + files.add(path); + mAllPhoto.add(path); } - files.add(path); - mAllPhoto.add(path); } while (cursor.moveToNext()); cursor.close(); } - postDelayed(new Runnable() { + postDelayed(() -> { + // 滚动回第一个位置 + mRecyclerView.scrollToPosition(0); + // 设置新的列表数据 + mAdapter.setData(mAllPhoto); - @Override - public void run() { - // 滚动回第一个位置 - mRecyclerView.scrollToPosition(0); - // 设置新的列表数据 - mAdapter.setData(mAllPhoto); + if (mSelectPhoto.isEmpty()) { + mFloatingView.setImageResource(R.drawable.ic_photo_camera); + } else { + mFloatingView.setImageResource(R.drawable.ic_photo_succeed); + } - if (mSelectPhoto.isEmpty()) { - mFloatingView.setImageResource(R.drawable.ic_photo_camera); - } else { - mFloatingView.setImageResource(R.drawable.ic_photo_succeed); - } + // 执行列表动画 + mRecyclerView.setLayoutAnimation(AnimationUtils.loadLayoutAnimation(getActivity(), R.anim.layout_fall_down)); + mRecyclerView.scheduleLayoutAnimation(); - // 执行列表动画 - mRecyclerView.setLayoutAnimation(AnimationUtils.loadLayoutAnimation(getActivity(), R.anim.layout_animation_fall_down)); - mRecyclerView.scheduleLayoutAnimation(); + // 设置右标提 + setRightTitle(R.string.photo_all); + if (mAllPhoto.isEmpty()) { + // 显示空布局 + showEmpty(); + } else { // 显示加载完成 showComplete(); - // 设置右标提 - setRightTitle(R.string.photo_all); - } - }, 1000); - } - - /** - * 启动系统相机 - */ - private void launchCamera() { - Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - if (intent.resolveActivity(getPackageManager()) != null) { - File file = createCameraFile(); - if (file != null && file.exists()) { - - Uri imageUri; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - // 通过 FileProvider 创建一个 Content 类型的 Uri 文件 - imageUri = FileProvider.getUriForFile(this, AppConfig.getPackageName() + ".provider", file); - } else { - imageUri = Uri.fromFile(file); - } - // 对目标应用临时授权该 Uri 所代表的文件 - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - // 将拍取的照片保存到指定 Uri - intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); - startActivityForResult(intent, new ActivityCallback() { - - @SuppressWarnings("ResultOfMethodCallIgnored") - @Override - public void onActivityResult(int resultCode, @Nullable Intent data) { - switch (resultCode) { - case RESULT_OK: - if (file.exists() && file.isFile()) { - // 重新扫描多媒体(否则可能扫描不到) - MediaScannerConnection.scanFile(getApplicationContext(), new String[]{file.getPath()}, null,null); - - // 当前选中图片的数量必须小于最大选中数 - if (mSelectPhoto.size() < mMaxSelect) { - mSelectPhoto.add(file.getPath()); - } - - // 这里需要延迟刷新,否则可能会找不到拍照的图片 - postDelayed(new Runnable() { - @Override - public void run() { - // 重新加载图片列表 - new Thread(PhotoActivity.this).start(); - } - }, 1000); - } - break; - case RESULT_CANCELED: - file.delete(); - break; - default: - break; - } - } - }); - } else { - toast(R.string.photo_picture_error); } - } else { - toast(R.string.photo_launch_fail); - } - } - - /** - * 创建一个拍照图片文件对象 - */ - private File createCameraFile() { - File folder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Camera"); - if (!folder.exists() || !folder.isDirectory()) { - if (!folder.mkdirs()) { - folder = Environment.getExternalStorageDirectory(); - } - } - - try { - return File.createTempFile("IMG", ".jpg", folder); - } catch (IOException e) { - e.printStackTrace(); - return null; - } + }, 500); } /** * 图片选择监听 */ public interface OnPhotoSelectListener { + /** - * 选择结果回调 + * 选择回调 + * * @param data 图片列表 */ - void onSelect(List data); + void onSelected(List data); /** * 取消回调 diff --git a/app/src/main/java/com/hjq/demo/ui/activity/RegisterActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/RegisterActivity.java index d86fcaf5..259a6c5d 100644 --- a/app/src/main/java/com/hjq/demo/ui/activity/RegisterActivity.java +++ b/app/src/main/java/com/hjq/demo/ui/activity/RegisterActivity.java @@ -1,19 +1,24 @@ package com.hjq.demo.ui.activity; -import android.content.Intent; import android.view.View; import android.widget.Button; import android.widget.EditText; +import android.widget.TextView; import com.gyf.immersionbar.ImmersionBar; import com.hjq.demo.R; +import com.hjq.demo.aop.SingleClick; import com.hjq.demo.common.MyActivity; import com.hjq.demo.helper.InputTextHelper; -import com.hjq.demo.other.IntentKey; +import com.hjq.demo.http.model.HttpData; +import com.hjq.demo.http.request.GetCodeApi; +import com.hjq.demo.http.request.RegisterApi; +import com.hjq.demo.http.response.RegisterBean; +import com.hjq.http.EasyHttp; +import com.hjq.http.listener.HttpCallback; import com.hjq.widget.view.CountdownView; import butterknife.BindView; -import butterknife.OnClick; /** * author : Android 轮子哥 @@ -23,6 +28,9 @@ */ public final class RegisterActivity extends MyActivity { + @BindView(R.id.tv_register_title) + TextView mTitleView; + @BindView(R.id.et_register_phone) EditText mPhoneView; @BindView(R.id.cv_register_countdown) @@ -46,22 +54,21 @@ protected int getLayoutId() { @Override protected void initView() { + // 给这个 View 设置沉浸式,避免状态栏遮挡 + ImmersionBar.setTitleBar(this, mTitleView); + InputTextHelper.with(this) .addView(mPhoneView) .addView(mCodeView) .addView(mPasswordView1) .addView(mPasswordView2) .setMain(mCommitView) - .setListener(new InputTextHelper.OnInputTextListener() { - - @Override - public boolean onInputChange(InputTextHelper helper) { - return mPhoneView.getText().toString().length() == 11 && - mPasswordView1.getText().toString().length() >= 6 && - mPasswordView1.getText().toString().equals(mPasswordView2.getText().toString()); - } - }) + .setListener(helper -> mPhoneView.getText().toString().length() == 11 && + mPasswordView1.getText().toString().length() >= 6 && + mPasswordView1.getText().toString().equals(mPasswordView2.getText().toString())) .build(); + + setOnClickListener(R.id.cv_register_countdown, R.id.btn_register_commit); } @Override @@ -70,34 +77,77 @@ protected void initData() { } @Override - protected ImmersionBar statusBarConfig() { + public ImmersionBar createStatusBarConfig() { // 不要把整个布局顶上去 - return super.statusBarConfig().keyboardEnable(true); + return super.createStatusBarConfig().keyboardEnable(true); } - @OnClick({R.id.cv_register_countdown, R.id.btn_register_commit}) + @SingleClick + @Override public void onClick(View v) { switch (v.getId()) { case R.id.cv_register_countdown: // 获取验证码 if (mPhoneView.getText().toString().length() != 11) { - // 重置验证码倒计时控件 - mCountdownView.resetState(); toast(R.string.common_phone_input_error); - } else { + return; + } + + if (true) { toast(R.string.common_code_send_hint); + mCountdownView.start(); + return; } + + // 获取验证码 + EasyHttp.post(this) + .api(new GetCodeApi() + .setPhone(mPhoneView.getText().toString())) + .request(new HttpCallback>(this) { + + @Override + public void onSucceed(HttpData data) { + toast(R.string.common_code_send_hint); + mCountdownView.start(); + } + + @Override + public void onFail(Exception e) { + super.onFail(e); + mCountdownView.start(); + } + }); break; case R.id.btn_register_commit: + if (true) { + LoginActivity.start(getActivity(), mPhoneView.getText().toString(), mPasswordView1.getText().toString()); + setResult(RESULT_OK); + finish(); + return; + } // 提交注册 - Intent intent = new Intent(); - intent.putExtra(IntentKey.PHONE, mPhoneView.getText().toString()); - intent.putExtra(IntentKey.PASSWORD, mPasswordView1.getText().toString()); - setResult(RESULT_OK, intent); - finish(); + EasyHttp.post(this) + .api(new RegisterApi() + .setPhone(mPhoneView.getText().toString()) + .setCode(mCodeView.getText().toString()) + .setPassword(mPasswordView1.getText().toString())) + .request(new HttpCallback>(this) { + + @Override + public void onSucceed(HttpData data) { + LoginActivity.start(getActivity(), mPhoneView.getText().toString(), mPasswordView1.getText().toString()); + setResult(RESULT_OK); + finish(); + } + }); break; default: break; } } + + @Override + public boolean isSwipeEnable() { + return false; + } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/activity/SettingActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/SettingActivity.java index d90a4a37..a28051f3 100644 --- a/app/src/main/java/com/hjq/demo/ui/activity/SettingActivity.java +++ b/app/src/main/java/com/hjq/demo/ui/activity/SettingActivity.java @@ -3,20 +3,24 @@ import android.view.Gravity; import android.view.View; -import com.hjq.base.BaseDialog; +import com.hjq.base.action.AnimAction; import com.hjq.demo.R; +import com.hjq.demo.aop.SingleClick; import com.hjq.demo.common.MyActivity; import com.hjq.demo.helper.ActivityStackManager; import com.hjq.demo.helper.CacheDataManager; +import com.hjq.demo.http.glide.GlideApp; +import com.hjq.demo.http.model.HttpData; +import com.hjq.demo.http.request.LogoutApi; import com.hjq.demo.other.AppConfig; import com.hjq.demo.ui.dialog.MenuDialog; import com.hjq.demo.ui.dialog.UpdateDialog; -import com.hjq.image.ImageLoader; +import com.hjq.http.EasyHttp; +import com.hjq.http.listener.HttpCallback; import com.hjq.widget.layout.SettingBar; import com.hjq.widget.view.SwitchButton; import butterknife.BindView; -import butterknife.OnClick; /** * author : Android 轮子哥 @@ -30,8 +34,6 @@ public final class SettingActivity extends MyActivity @BindView(R.id.sb_setting_cache) SettingBar mCleanCacheView; - @BindView(R.id.sb_setting_auto) - SettingBar mAutoLoginView; @BindView(R.id.sb_setting_switch) SwitchButton mAutoSwitchView; @@ -44,6 +46,9 @@ protected int getLayoutId() { protected void initView() { // 设置切换按钮的监听 mAutoSwitchView.setOnCheckedChangeListener(this); + + setOnClickListener(R.id.sb_setting_language, R.id.sb_setting_update, R.id.sb_setting_agreement, R.id.sb_setting_about, + R.id.sb_setting_cache, R.id.sb_setting_auto, R.id.sb_setting_exit); } @Override @@ -52,8 +57,8 @@ protected void initData() { mCleanCacheView.setRightText(CacheDataManager.getTotalCacheSize(this)); } - @OnClick({R.id.sb_setting_language, R.id.sb_setting_update, R.id.sb_setting_agreement, R.id.sb_setting_about, - R.id.sb_setting_cache, R.id.sb_setting_auto, R.id.sb_setting_exit}) + @SingleClick + @Override public void onClick(View v) { switch (v.getId()) { case R.id.sb_setting_language: @@ -62,18 +67,9 @@ public void onClick(View v) { // 设置点击按钮后不关闭对话框 //.setAutoDismiss(false) .setList(R.string.setting_language_simple, R.string.setting_language_complex) - .setListener(new MenuDialog.OnListener() { - - @Override - public void onSelected(BaseDialog dialog, int position, String string) { - WebActivity.start(getActivity(), "https://github.com/getActivity/MultiLanguages"); - } - - @Override - public void onCancel(BaseDialog dialog) {} - }) + .setListener((MenuDialog.OnListener) (dialog, position, string) -> BrowserActivity.start(getActivity(), "https://github.com/getActivity/MultiLanguages")) .setGravity(Gravity.BOTTOM) - .setAnimStyle(BaseDialog.AnimStyle.BOTTOM) + .setAnimStyle(AnimAction.BOTTOM) .show(); break; case R.id.sb_setting_update: @@ -82,13 +78,11 @@ public void onCancel(BaseDialog dialog) {} new UpdateDialog.Builder(this) // 版本名 - .setVersionName("v 2.0") - // 文件大小 - .setFileSize("10 M") + .setVersionName("2.0") // 是否强制更新 .setForceUpdate(false) // 更新日志 - .setUpdateLog("到底更新了啥\n到底更新了啥\n到底更新了啥\n到底更新了啥\n到底更新了啥") + .setUpdateLog("修复Bug\n优化用户体验") // 下载 url .setDownloadUrl("https://raw.githubusercontent.com/getActivity/AndroidProject/master/AndroidProject.apk") .show(); @@ -97,7 +91,7 @@ public void onCancel(BaseDialog dialog) {} } break; case R.id.sb_setting_agreement: - WebActivity.start(this, "https://github.com/getActivity/Donate"); + BrowserActivity.start(this, "https://github.com/getActivity/Donate"); break; case R.id.sb_setting_about: startActivity(AboutActivity.class); @@ -107,17 +101,38 @@ public void onCancel(BaseDialog dialog) {} mAutoSwitchView.setChecked(!mAutoSwitchView.isChecked()); break; case R.id.sb_setting_cache: - // 清空缓存 - ImageLoader.clear(this); + // 清除内存缓存(必须在主线程) + GlideApp.get(getActivity()).clearMemory(); + new Thread(() -> { + // 清除本地缓存(必须在子线程) + GlideApp.get(getActivity()).clearDiskCache(); + }).start(); CacheDataManager.clearAllCache(this); - // 重新获取应用缓存大小 - mCleanCacheView.setRightText(CacheDataManager.getTotalCacheSize(this)); + postDelayed(() -> { + // 重新获取应用缓存大小 + mCleanCacheView.setRightText(CacheDataManager.getTotalCacheSize(getActivity())); + }, 500); break; case R.id.sb_setting_exit: + if (true) { + startActivity(LoginActivity.class); + // 进行内存优化,销毁除登录页之外的所有界面 + ActivityStackManager.getInstance().finishAllActivities(LoginActivity.class); + return; + } + // 退出登录 - startActivity(LoginActivity.class); - // 进行内存优化,销毁掉所有的界面 - ActivityStackManager.getInstance().finishAllActivities(LoginActivity.class); + EasyHttp.post(this) + .api(new LogoutApi()) + .request(new HttpCallback>(this) { + + @Override + public void onSucceed(HttpData data) { + startActivity(LoginActivity.class); + // 进行内存优化,销毁除登录页之外的所有界面 + ActivityStackManager.getInstance().finishAllActivities(LoginActivity.class); + } + }); break; default: break; diff --git a/app/src/main/java/com/hjq/demo/ui/activity/SplashActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/SplashActivity.java index e6acaea0..b6c32735 100644 --- a/app/src/main/java/com/hjq/demo/ui/activity/SplashActivity.java +++ b/app/src/main/java/com/hjq/demo/ui/activity/SplashActivity.java @@ -1,20 +1,20 @@ package com.hjq.demo.ui.activity; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.view.View; -import android.view.animation.AlphaAnimation; -import android.view.animation.Animation; -import android.view.animation.RotateAnimation; -import android.view.animation.ScaleAnimation; +import com.airbnb.lottie.LottieAnimationView; import com.gyf.immersionbar.BarHide; +import com.gyf.immersionbar.ImmersionBar; import com.hjq.demo.R; import com.hjq.demo.common.MyActivity; +import com.hjq.demo.http.model.HttpData; +import com.hjq.demo.http.request.UserInfoApi; +import com.hjq.demo.http.response.UserInfoBean; import com.hjq.demo.other.AppConfig; -import com.hjq.permissions.OnPermission; -import com.hjq.permissions.Permission; -import com.hjq.permissions.XXPermissions; - -import java.util.List; +import com.hjq.http.EasyHttp; +import com.hjq.http.listener.HttpCallback; import butterknife.BindView; @@ -24,17 +24,10 @@ * time : 2018/10/18 * desc : 闪屏界面 */ -public final class SplashActivity extends MyActivity - implements OnPermission, Animation.AnimationListener { - - private static final int ANIM_TIME = 1000; +public final class SplashActivity extends MyActivity { - @BindView(R.id.iv_splash_bg) - View mImageView; - @BindView(R.id.iv_splash_icon) - View mIconView; - @BindView(R.id.iv_splash_name) - View mNameView; + @BindView(R.id.iv_splash_lottie) + LottieAnimationView mLottieView; @BindView(R.id.tv_splash_debug) View mDebugView; @@ -46,29 +39,15 @@ protected int getLayoutId() { @Override protected void initView() { - // 初始化动画 - AlphaAnimation aa = new AlphaAnimation(0.4f, 1.0f); - aa.setDuration(ANIM_TIME * 2); - aa.setAnimationListener(this); - mImageView.startAnimation(aa); - - ScaleAnimation sa = new ScaleAnimation(0, 1, 0, 1, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); - sa.setDuration(ANIM_TIME); - mIconView.startAnimation(sa); - - RotateAnimation ra = new RotateAnimation(180, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); - ra.setDuration(ANIM_TIME); - mNameView.startAnimation(ra); - - // 设置状态栏和导航栏参数 - getStatusBarConfig() - // 有导航栏的情况下,activity全屏显示,也就是activity最下面被导航栏覆盖,不写默认非全屏 - .fullScreen(true) - // 隐藏状态栏 - .hideBar(BarHide.FLAG_HIDE_STATUS_BAR) - // 透明导航栏,不写默认黑色(设置此方法,fullScreen()方法自动为true) - .transparentNavigationBar() - .init(); + // 设置动画监听 + mLottieView.addAnimatorListener(new AnimatorListenerAdapter() { + + @Override + public void onAnimationEnd(Animator animation) { + startActivity(HomeActivity.class); + finish(); + } + }); } @Override @@ -78,32 +57,31 @@ protected void initData() { } else { mDebugView.setVisibility(View.INVISIBLE); } - } - private void requestPermission() { - XXPermissions.with(this) - .permission(Permission.Group.STORAGE) - .request(this); - } + if (true) { + return; + } + // 获取用户信息 + EasyHttp.post(this) + .api(new UserInfoApi()) + .request(new HttpCallback>(this) { - /** - * {@link OnPermission} - */ + @Override + public void onSucceed(HttpData data) { - @Override - public void hasPermission(List granted, boolean isAll) { - startActivityFinish(HomeActivity.class); + } + }); } @Override - public void noPermission(List denied, boolean quick) { - if (quick) { - toast(R.string.common_permission_fail); - XXPermissions.gotoPermissionSettings(SplashActivity.this, true); - } else { - toast(R.string.common_permission_hint); - postDelayed(this::requestPermission, 1000); - } + public ImmersionBar createStatusBarConfig() { + return super.createStatusBarConfig() + // 有导航栏的情况下,activity全屏显示,也就是activity最下面被导航栏覆盖,不写默认非全屏 + .fullScreen(true) + // 隐藏状态栏 + .hideBar(BarHide.FLAG_HIDE_STATUS_BAR) + // 透明导航栏,不写默认黑色(设置此方法,fullScreen()方法自动为true) + .transparentNavigationBar(); } @Override @@ -113,27 +91,7 @@ public void onBackPressed() { } @Override - protected void onRestart() { - super.onRestart(); - if (XXPermissions.isHasPermission(SplashActivity.this, Permission.Group.STORAGE)) { - hasPermission(null, true); - } else { - requestPermission(); - } - } - - /** - * {@link Animation.AnimationListener} - */ - - @Override - public void onAnimationStart(Animation animation) {} - - @Override - public void onAnimationEnd(Animation animation) { - requestPermission(); + public boolean isSwipeEnable() { + return false; } - - @Override - public void onAnimationRepeat(Animation animation) {} } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/activity/StatusActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/StatusActivity.java index b9e95e4d..a1a0501d 100644 --- a/app/src/main/java/com/hjq/demo/ui/activity/StatusActivity.java +++ b/app/src/main/java/com/hjq/demo/ui/activity/StatusActivity.java @@ -1,11 +1,28 @@ package com.hjq.demo.ui.activity; +import android.view.View; +import android.widget.TextView; + +import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; +import androidx.recyclerview.widget.RecyclerView; -import com.hjq.base.BaseDialog; +import com.hjq.base.BaseAdapter; import com.hjq.demo.R; +import com.hjq.demo.action.StatusAction; import com.hjq.demo.common.MyActivity; +import com.hjq.demo.ui.adapter.StatusAdapter; import com.hjq.demo.ui.dialog.MenuDialog; +import com.hjq.demo.widget.HintLayout; +import com.hjq.widget.layout.WrapRecyclerView; +import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.scwang.smartrefresh.layout.api.RefreshLayout; +import com.scwang.smartrefresh.layout.listener.OnRefreshLoadMoreListener; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.BindView; /** * author : Android 轮子哥 @@ -13,7 +30,19 @@ * time : 2019/04/17 * desc : 加载使用案例 */ -public final class StatusActivity extends MyActivity { +public final class StatusActivity extends MyActivity + implements StatusAction, + OnRefreshLoadMoreListener, + BaseAdapter.OnItemClickListener { + + @BindView(R.id.hl_status_hint) + HintLayout mHintLayout; + @BindView(R.id.rl_status_refresh) + SmartRefreshLayout mRefreshLayout; + @BindView(R.id.rv_status_list) + WrapRecyclerView mRecyclerView; + + private StatusAdapter mAdapter; @Override protected int getLayoutId() { @@ -22,46 +51,101 @@ protected int getLayoutId() { @Override protected void initView() { + mAdapter = new StatusAdapter(this); + mAdapter.setOnItemClickListener(this); + mRecyclerView.setAdapter(mAdapter); + + TextView headerView = mRecyclerView.addHeaderView(R.layout.item_picker); + headerView.setText("我是头部"); + headerView.setOnClickListener(v -> toast("点击了头部")); + + TextView footerView = mRecyclerView.addFooterView(R.layout.item_picker); + footerView.setText("我是尾部"); + footerView.setOnClickListener(v -> toast("点击了尾部")); + mRefreshLayout.setOnRefreshLoadMoreListener(this); } @Override protected void initData() { + mAdapter.setData(analogData()); + new MenuDialog.Builder(this) - .setCancelable(false) //.setAutoDismiss(false) // 设置点击按钮后不关闭对话框 .setList("加载中", "请求错误", "空数据提示", "自定义提示") - .setListener(new MenuDialog.OnListener() { - - @Override - public void onSelected(BaseDialog dialog, int position, Object object) { - switch (position) { - case 0: + .setListener((dialog, position, object) -> { + switch (position) { + case 0: + showLoading(); + postDelayed(this::showComplete, 2000); + break; + case 1: + showError(v -> { showLoading(); - postDelayed(new Runnable() { - @Override - public void run() { - showComplete(); - } - }, 2000); - break; - case 1: - showError(); - break; - case 2: - showEmpty(); - break; - case 3: - showLayout(ContextCompat.getDrawable(getActivity(), R.drawable.icon_hint_address), "还没有添加地址"); - break; - default: - break; - } + postDelayed(this::showEmpty, 2000); + }); + break; + case 2: + showEmpty(); + break; + case 3: + showLayout(ContextCompat.getDrawable(getActivity(), R.drawable.ic_hint_order), "暂无订单", null); + break; + default: + break; } - - @Override - public void onCancel(BaseDialog dialog) {} }) .show(); } + + @Override + public HintLayout getHintLayout() { + return mHintLayout; + } + + /** + * 模拟数据 + */ + private List analogData() { + List data = new ArrayList<>(); + for (int i = mAdapter.getItemCount(); i < mAdapter.getItemCount() + 20; i++) { + data.add("我是第" + i + "条目"); + } + return data; + } + + /** + * {@link BaseAdapter.OnItemClickListener} + * + * @param recyclerView RecyclerView对象 + * @param itemView 被点击的条目对象 + * @param position 被点击的条目位置 + */ + @Override + public void onItemClick(RecyclerView recyclerView, View itemView, int position) { + toast(mAdapter.getItem(position)); + } + + /** + * {@link OnRefreshLoadMoreListener} + */ + + @Override + public void onRefresh(@NonNull RefreshLayout refreshLayout) { + postDelayed(() -> { + mAdapter.clearData(); + mAdapter.setData(analogData()); + mRefreshLayout.finishRefresh(); + toast("刷新完成"); + }, 1000); + } + + @Override + public void onLoadMore(@NonNull RefreshLayout refreshLayout) { + postDelayed(() -> { + mAdapter.addData(analogData()); + mRefreshLayout.finishLoadMore(); + toast("加载完成"); + }, 1000); + } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/activity/WebActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/WebActivity.java deleted file mode 100644 index 0d204d94..00000000 --- a/app/src/main/java/com/hjq/demo/ui/activity/WebActivity.java +++ /dev/null @@ -1,224 +0,0 @@ -package com.hjq.demo.ui.activity; - -import android.annotation.SuppressLint; -import android.annotation.TargetApi; -import android.content.Context; -import android.content.Intent; -import android.graphics.Bitmap; -import android.net.Uri; -import android.net.http.SslError; -import android.os.Build; -import android.view.KeyEvent; -import android.view.View; -import android.webkit.SslErrorHandler; -import android.webkit.WebChromeClient; -import android.webkit.WebResourceError; -import android.webkit.WebResourceRequest; -import android.webkit.WebSettings; -import android.webkit.WebView; -import android.webkit.WebViewClient; -import android.widget.ProgressBar; - -import com.hjq.demo.R; -import com.hjq.demo.common.MyActivity; -import com.hjq.demo.helper.WebViewLifecycleUtils; -import com.hjq.demo.other.IntentKey; - -import butterknife.BindView; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/10/18 - * desc : 浏览器界面 - */ -public final class WebActivity extends MyActivity { - - public static void start(Context context, String url) { - if (url == null || "".equals(url)) { - return; - } - Intent intent = new Intent(context, WebActivity.class); - intent.putExtra(IntentKey.URL, url); - context.startActivity(intent); - } - - @BindView(R.id.pb_web_progress) - ProgressBar mProgressBar; - @BindView(R.id.wv_web_view) - WebView mWebView; - - @Override - protected int getLayoutId() { - return R.layout.activity_web; - } - - @SuppressLint("SetJavaScriptEnabled") - @Override - protected void initView() { - // 不显示滚动条 - mWebView.setVerticalScrollBarEnabled(false); - mWebView.setHorizontalScrollBarEnabled(false); - - WebSettings settings = mWebView.getSettings(); - // 允许文件访问 - settings.setAllowFileAccess(true); - // 允许网页定位 - settings.setGeolocationEnabled(true); - // 允许保存密码 - settings.setSavePassword(true); - // 开启 JavaScript - settings.setJavaScriptEnabled(true); - // 允许网页弹对话框 - settings.setJavaScriptCanOpenWindowsAutomatically(true); - // 加快网页加载完成的速度,等页面完成再加载图片 - settings.setLoadsImagesAutomatically(true); - // 本地 DOM 存储(解决加载某些网页出现白板现象) - settings.setDomStorageEnabled(true); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - // 解决 Android 5.0 上 WebView 默认不允许加载 Http 与 Https 混合内容 - settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); - } - } - - @Override - protected void initData() { - mWebView.setWebViewClient(new MyWebViewClient()); - mWebView.setWebChromeClient(new MyWebChromeClient()); - - String url = getIntent().getStringExtra(IntentKey.URL); - mWebView.loadUrl(url); - } - - @Override - public void onLeftClick(View v) { - finish(); - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK && mWebView.canGoBack()) { - // 后退网页并且拦截该事件 - mWebView.goBack(); - return true; - } - return super.onKeyDown(keyCode, event); - } - - @Override - protected void onResume() { - WebViewLifecycleUtils.onResume(mWebView); - super.onResume(); - } - - @Override - protected void onPause() { - WebViewLifecycleUtils.onPause(mWebView); - super.onPause(); - } - - @Override - protected void onDestroy() { - WebViewLifecycleUtils.onDestroy(mWebView); - super.onDestroy(); - } - - private class MyWebViewClient extends WebViewClient { - - /** - * 同名 API 兼容 - */ - @TargetApi(Build.VERSION_CODES.M) - @Override - public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { - if (request.isForMainFrame()) { - onReceivedError(view, - error.getErrorCode(), error.getDescription().toString(), - request.getUrl().toString()); - } - } - - /** - * 网页加载错误时回调,这个方法会在 onPageFinished 之前调用 - */ - @Override - public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { - // 这里为什么要用延迟呢?因为加载出错之后会先调用 onReceivedError 再调用 onPageFinished - post(WebActivity.this::showError); - } - - /** - * 开始加载网页 - */ - @Override - public void onPageStarted(final WebView view, final String url, Bitmap favicon) { - mProgressBar.setVisibility(View.VISIBLE); - } - - /** - * 完成加载网页 - */ - @Override - public void onPageFinished(WebView view, String url) { - mProgressBar.setVisibility(View.GONE); - showComplete(); - } - - @Override - public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { - // 注意一定要去除这行代码,否则设置无效。 - //super.onReceivedSslError(view, handler, error); - // Android默认的处理方式 - //handler.cancel(); - // 接受所有网站的证书 - handler.proceed(); - } - - /** - * 同名 API 兼容 - */ - @TargetApi(Build.VERSION_CODES.N) - @Override - public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { - return shouldOverrideUrlLoading(view, request.getUrl().toString()); - } - - /** - * 跳转到其他链接 - */ - @Override - public boolean shouldOverrideUrlLoading(WebView view, final String url) { - String scheme = Uri.parse(url).getScheme(); - if (scheme != null) { - scheme = scheme.toLowerCase(); - } - if ("http".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme)) { - mWebView.loadUrl(url); - } - // 已经处理该链接请求 - return true; - } - } - - private class MyWebChromeClient extends WebChromeClient { - - /** - * 收到网页标题 - */ - @Override - public void onReceivedTitle(WebView view, String title) { - if (title != null) { - setTitle(title); - } - } - - /** - * 收到加载进度变化 - */ - @Override - public void onProgressChanged(WebView view, int newProgress) { - mProgressBar.setProgress(newProgress); - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/adapter/CopyAdapter.java b/app/src/main/java/com/hjq/demo/ui/adapter/CopyAdapter.java index 07205dea..2ed9bad7 100644 --- a/app/src/main/java/com/hjq/demo/ui/adapter/CopyAdapter.java +++ b/app/src/main/java/com/hjq/demo/ui/adapter/CopyAdapter.java @@ -5,7 +5,7 @@ import android.view.ViewGroup; import com.hjq.demo.R; -import com.hjq.demo.common.MyRecyclerViewAdapter; +import com.hjq.demo.common.MyAdapter; /** * author : Android 轮子哥 @@ -13,7 +13,7 @@ * time : 2018/11/05 * desc : 可进行拷贝的副本 */ -public final class CopyAdapter extends MyRecyclerViewAdapter { +public final class CopyAdapter extends MyAdapter { public CopyAdapter(Context context) { super(context); @@ -30,7 +30,7 @@ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new ViewHolder(); } - final class ViewHolder extends MyRecyclerViewAdapter.ViewHolder { + final class ViewHolder extends MyAdapter.ViewHolder { ViewHolder() { super(R.layout.item_copy); diff --git a/app/src/main/java/com/hjq/demo/ui/adapter/PhotoAdapter.java b/app/src/main/java/com/hjq/demo/ui/adapter/PhotoAdapter.java index da01c7ec..0b470c73 100644 --- a/app/src/main/java/com/hjq/demo/ui/adapter/PhotoAdapter.java +++ b/app/src/main/java/com/hjq/demo/ui/adapter/PhotoAdapter.java @@ -10,8 +10,8 @@ import androidx.recyclerview.widget.RecyclerView; import com.hjq.demo.R; -import com.hjq.demo.common.MyRecyclerViewAdapter; -import com.hjq.image.ImageLoader; +import com.hjq.demo.common.MyAdapter; +import com.hjq.demo.http.glide.GlideApp; import java.util.List; @@ -23,7 +23,7 @@ * time : 2019/07/24 * desc : 图片选择适配器 */ -public final class PhotoAdapter extends MyRecyclerViewAdapter { +public final class PhotoAdapter extends MyAdapter { private final List mSelectPhoto; @@ -38,7 +38,7 @@ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new ViewHolder(); } - final class ViewHolder extends MyRecyclerViewAdapter.ViewHolder { + final class ViewHolder extends MyAdapter.ViewHolder { @BindView(R.id.iv_photo_image) ImageView mImageView; @@ -51,7 +51,7 @@ final class ViewHolder extends MyRecyclerViewAdapter.ViewHolder { @Override public void onBindView(int position) { - ImageLoader.with(getContext()) + GlideApp.with(getContext()) .load(getItem(position)) .into(mImageView); @@ -60,7 +60,7 @@ public void onBindView(int position) { } @Override - protected RecyclerView.LayoutManager getDefaultLayoutManager(Context context) { + protected RecyclerView.LayoutManager generateDefaultLayoutManager(Context context) { return new GridLayoutManager(context, 3); } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/adapter/StatusAdapter.java b/app/src/main/java/com/hjq/demo/ui/adapter/StatusAdapter.java new file mode 100644 index 00000000..f06f6e38 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/ui/adapter/StatusAdapter.java @@ -0,0 +1,46 @@ +package com.hjq.demo.ui.adapter; + +import android.content.Context; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +import com.hjq.demo.R; +import com.hjq.demo.common.MyAdapter; + +import butterknife.BindView; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/09/22 + * desc : 状态数据列表 + */ +public final class StatusAdapter extends MyAdapter { + + public StatusAdapter(Context context) { + super(context); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(); + } + + final class ViewHolder extends MyAdapter.ViewHolder { + + @BindView(R.id.tv_status_text) + TextView mTextView; + + ViewHolder() { + super(R.layout.item_status); + } + + @Override + public void onBindView(int position) { + mTextView.setText(getItem(position)); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/dialog/AddressDialog.java b/app/src/main/java/com/hjq/demo/ui/dialog/AddressDialog.java index 27d86b7d..0c9ad371 100644 --- a/app/src/main/java/com/hjq/demo/ui/dialog/AddressDialog.java +++ b/app/src/main/java/com/hjq/demo/ui/dialog/AddressDialog.java @@ -1,7 +1,6 @@ package com.hjq.demo.ui.dialog; import android.content.Context; -import android.os.Build; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.Gravity; @@ -13,15 +12,14 @@ import androidx.annotation.NonNull; import androidx.annotation.StringRes; -import androidx.fragment.app.FragmentActivity; import androidx.recyclerview.widget.RecyclerView; +import androidx.viewpager2.widget.ViewPager2; import com.google.android.material.tabs.TabLayout; import com.hjq.base.BaseDialog; -import com.hjq.base.BaseRecyclerViewAdapter; import com.hjq.demo.R; -import com.hjq.demo.common.MyDialogFragment; -import com.hjq.demo.common.MyRecyclerViewAdapter; +import com.hjq.demo.aop.SingleClick; +import com.hjq.demo.common.MyAdapter; import org.json.JSONArray; import org.json.JSONException; @@ -33,6 +31,10 @@ import java.util.ArrayList; import java.util.List; +import static androidx.viewpager.widget.ViewPager.SCROLL_STATE_DRAGGING; +import static androidx.viewpager.widget.ViewPager.SCROLL_STATE_IDLE; +import static androidx.viewpager.widget.ViewPager.SCROLL_STATE_SETTLING; + /** * author : Android 轮子哥 * github : https://github.com/getActivity/AndroidProject @@ -43,22 +45,18 @@ public final class AddressDialog { public static final class Builder - extends MyDialogFragment.Builder - implements BaseRecyclerViewAdapter.OnItemClickListener, - View.OnClickListener, TabLayout.OnTabSelectedListener, Runnable { + extends BaseDialog.Builder + implements TabLayout.OnTabSelectedListener, + Runnable, RecyclerViewAdapter.OnSelectListener, + BaseDialog.OnShowListener, BaseDialog.OnDismissListener { private final TextView mTitleView; private final ImageView mCloseView; private final TabLayout mTabLayout; - private final ImageView mHintView; - - private final RecyclerView mProvinceView; - private final RecyclerView mCityView; - private final RecyclerView mAreaView; - private final AddressDialogAdapter mProvinceAdapter; - private final AddressDialogAdapter mCityAdapter; - private final AddressDialogAdapter mAreaAdapter; + private final ViewPager2 mViewPager; + private final RecyclerViewAdapter mAdapter; + private final ViewPager2.OnPageChangeCallback mCallback; private OnListener mListener; @@ -68,42 +66,56 @@ public static final class Builder private boolean mIgnoreArea; - public Builder(FragmentActivity activity) { - super(activity); + @SuppressWarnings("all") + public Builder(Context context) { + super(context); setContentView(R.layout.dialog_address); DisplayMetrics displayMetrics = new DisplayMetrics(); getSystemService(WindowManager.class).getDefaultDisplay().getMetrics(displayMetrics); setHeight(displayMetrics.heightPixels / 2); + mViewPager = findViewById(R.id.vp_address_province); + mAdapter = new RecyclerViewAdapter(context); + mAdapter.setOnSelectListener(this); + mViewPager.setAdapter(mAdapter); + mTitleView = findViewById(R.id.tv_address_title); mCloseView = findViewById(R.id.iv_address_closer); mTabLayout = findViewById(R.id.tb_address_tab); - mHintView = findViewById(R.id.iv_address_hint); - - mProvinceView = findViewById(R.id.rv_address_province); - mCityView = findViewById(R.id.rv_address_city); - mAreaView = findViewById(R.id.rv_address_area); - mProvinceAdapter = new AddressDialogAdapter(getContext()); - mCityAdapter = new AddressDialogAdapter(getContext()); - mAreaAdapter = new AddressDialogAdapter(getContext()); + mTabLayout.addTab(mTabLayout.newTab().setText(getString(R.string.address_hint)), true); + mTabLayout.addOnTabSelectedListener(this); - mCloseView.setOnClickListener(this); + mCallback = new ViewPager2.OnPageChangeCallback() { - mProvinceAdapter.setOnItemClickListener(this); - mCityAdapter.setOnItemClickListener(this); - mAreaAdapter.setOnItemClickListener(this); + private int mPreviousScrollState, mScrollState = SCROLL_STATE_IDLE; - mProvinceView.setAdapter(mProvinceAdapter); - mCityView.setAdapter(mCityAdapter); - mAreaView.setAdapter(mAreaAdapter); + @Override + public void onPageScrollStateChanged(int state) { + mPreviousScrollState = mScrollState; + mScrollState = state; + if (state == ViewPager2.SCROLL_STATE_IDLE && mTabLayout.getSelectedTabPosition() != mViewPager.getCurrentItem()) { + final boolean updateIndicator = mScrollState == SCROLL_STATE_IDLE || (mScrollState == SCROLL_STATE_SETTLING && mPreviousScrollState == SCROLL_STATE_IDLE); + mTabLayout.selectTab(mTabLayout.getTabAt(mViewPager.getCurrentItem()), updateIndicator); + } + } - mTabLayout.addTab(mTabLayout.newTab().setText(getString(R.string.address_hint)), true); - mTabLayout.addOnTabSelectedListener(this); + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + final boolean updateText = mScrollState != SCROLL_STATE_SETTLING || mPreviousScrollState == SCROLL_STATE_DRAGGING; + final boolean updateIndicator = !(mScrollState == SCROLL_STATE_SETTLING && mPreviousScrollState == SCROLL_STATE_IDLE); + mTabLayout.setScrollPosition(position, positionOffset, updateText, updateIndicator); + } + }; // 显示省份列表 - mProvinceAdapter.setData(AddressManager.getProvinceList(getContext())); + mAdapter.addItem(AddressManager.getProvinceList(getContext())); + + setOnClickListener(R.id.iv_address_closer); + + addOnShowListener(this); + addOnDismissListener(this); } public Builder setTitle(@StringRes int id) { @@ -119,11 +131,11 @@ public Builder setTitle(CharSequence text) { */ public Builder setProvince(String province) { if (province != null && !"".equals(province)) { - List data = mProvinceAdapter.getData(); + List data = mAdapter.getItem(0); if (data != null && !data.isEmpty()) { for (int i = 0; i < data.size(); i++) { if (province.equals(data.get(i).getName())) { - onItemClick(mProvinceView, null, i); + onSelected(0, i); break; } } @@ -141,11 +153,14 @@ public Builder setCity(String city) { throw new IllegalStateException("The selection of county-level regions has been ignored. The designated city cannot be selected"); } if (city != null && !"".equals(city)) { - List data = mCityAdapter.getData(); + List data = mAdapter.getItem(1); if (data != null && !data.isEmpty()) { for (int i = 0; i < data.size(); i++) { if (city.equals(data.get(i).getName())) { - onItemClick(mCityView, null, i); + // 避开直辖市,因为选择省的时候已经自动跳过市区了 + if (mAdapter.getItem(1).size() > 1) { + onSelected(1, i); + } break; } } @@ -158,7 +173,7 @@ public Builder setCity(String city) { * 不选择县级区域 */ public Builder setIgnoreArea() { - List data = mCityAdapter.getData(); + List data = mAdapter.getItem(1); if (data != null && !data.isEmpty()) { // 已经指定了城市,不能再忽略县级区域 throw new IllegalStateException("Cities have been designated and county-level areas can no longer be ignored"); @@ -173,96 +188,73 @@ public Builder setListener(OnListener listener) { } /** - * {@link BaseRecyclerViewAdapter.OnItemClickListener} + * {@link RecyclerViewAdapter.OnSelectListener} */ @SuppressWarnings("all") @Override - public synchronized void onItemClick(RecyclerView recyclerView, View itemView, int position) { - if (recyclerView == mProvinceView) { - - // 记录当前选择的省份 - mProvince = mProvinceAdapter.getItem(position).getName(); - - mTabLayout.getTabAt(mTabLayout.getSelectedTabPosition()).setText(mProvince); - mTabLayout.addTab(mTabLayout.newTab().setText(getString(R.string.address_hint)), true); + public void onSelected(int recyclerViewPosition, int clickItemPosition) { + switch (recyclerViewPosition) { + case 0: + // 记录当前选择的省份 + mProvince = mAdapter.getItem(recyclerViewPosition).get(clickItemPosition).getName(); + mTabLayout.getTabAt(mTabLayout.getSelectedTabPosition()).setText(mProvince); - mCityAdapter.setData(AddressManager.getCityList(mProvinceAdapter.getItem(position).getNext())); + mTabLayout.addTab(mTabLayout.newTab().setText(getString(R.string.address_hint)), true); + mAdapter.addItem(AddressManager.getCityList(mAdapter.getItem(recyclerViewPosition).get(clickItemPosition).getNext())); + mViewPager.setCurrentItem(recyclerViewPosition + 1); - mProvinceView.setVisibility(View.GONE); - mCityView.setVisibility(View.VISIBLE); + // 如果当前选择的是直辖市,就直接跳过选择城市,直接选择区域 + if (mAdapter.getItem(recyclerViewPosition + 1).size() == 1) { + onSelected(recyclerViewPosition + 1, 0); + } + break; + case 1: + // 记录当前选择的城市 + mCity = mAdapter.getItem(recyclerViewPosition).get(clickItemPosition).getName(); + mTabLayout.getTabAt(mTabLayout.getSelectedTabPosition()).setText(mCity); - // 如果当前选择的是直辖市,就直接跳过选择城市,直接选择区域 - if (mCityAdapter.getItemCount() == 1) { - onItemClick(mCityView, null, 0); - } + if (mIgnoreArea) { - } else if (recyclerView == mCityView) { + if (mListener != null) { + mListener.onSelected(getDialog(), mProvince, mCity, mArea); + } - // 记录当前选择的城市 - mCity = mCityAdapter.getItem(position).getName(); + // 延迟关闭 + postDelayed(this::dismiss, 300); - mTabLayout.getTabAt(mTabLayout.getSelectedTabPosition()).setText(mCity); + } else { + mTabLayout.addTab(mTabLayout.newTab().setText(getString(R.string.address_hint)), true); + mAdapter.addItem(AddressManager.getAreaList(mAdapter.getItem(recyclerViewPosition).get(clickItemPosition).getNext())); + mViewPager.setCurrentItem(recyclerViewPosition + 1); + } - if (mIgnoreArea) { + break; + case 2: + // 记录当前选择的区域 + mArea = mAdapter.getItem(recyclerViewPosition).get(clickItemPosition).getName(); + mTabLayout.getTabAt(mTabLayout.getSelectedTabPosition()).setText(mArea); if (mListener != null) { mListener.onSelected(getDialog(), mProvince, mCity, mArea); } // 延迟关闭 - postDelayed(this, 300); - - } else { - mTabLayout.addTab(mTabLayout.newTab().setText(getString(R.string.address_hint)), true); - mAreaAdapter.setData(AddressManager.getAreaList(mCityAdapter.getItem(position).getNext())); - } - - mCityView.setVisibility(View.GONE); - - if (mIgnoreArea) { - mHintView.setVisibility(View.VISIBLE); - } else { - mAreaView.setVisibility(View.VISIBLE); - } - - } else if (recyclerView == mAreaView) { - - // 记录当前选择的区域 - mArea = mAreaAdapter.getItem(position).getName(); - - mTabLayout.getTabAt(mTabLayout.getSelectedTabPosition()).setText(mArea); - - mAreaView.setVisibility(View.INVISIBLE); - mHintView.setVisibility(View.VISIBLE); - - if (mListener != null) { - mListener.onSelected(getDialog(), mProvince, mCity, mArea); - } - - // 延迟关闭 - postDelayed(this, 300); + postDelayed(this::dismiss, 300); + break; + default: + break; } } - /** - * {@link Runnable} - */ - @Override public void run() { - if (getDialogFragment() != null && - getDialogFragment().isAdded() && - getDialog() != null && - getDialog().isShowing()) { + if (isShowing()) { dismiss(); } } - /** - * {@link View.OnClickListener} - */ - + @SingleClick @Override public void onClick(View v) { if (v == mCloseView) { @@ -277,55 +269,122 @@ public void onClick(View v) { * {@link TabLayout.OnTabSelectedListener} */ - /** Tab条目被选中 */ @Override public void onTabSelected(TabLayout.Tab tab) { - tab.setText(getString(R.string.address_hint)); - switch (tab.getPosition()) { - case 0: - mProvince = mCity = mArea = null; - if (mTabLayout.getTabAt(2) != null) { - mTabLayout.removeTabAt(2); - } - if (mTabLayout.getTabAt(1) != null) { - mTabLayout.removeTabAt(1); - } - mProvinceView.setVisibility(View.VISIBLE); - mCityView.setVisibility(View.GONE); - mAreaView.setVisibility(View.GONE); - break; - case 1: - mCity = mArea = null; - if (mTabLayout.getTabAt(2) != null) { - mTabLayout.removeTabAt(2); - } - mProvinceView.setVisibility(View.GONE); - mCityView.setVisibility(View.VISIBLE); - mAreaView.setVisibility(View.GONE); - break; - case 2: - mArea = null; - mProvinceView.setVisibility(View.GONE); - mCityView.setVisibility(View.GONE); - mAreaView.setVisibility(View.VISIBLE); - break; - default: - break; + synchronized (this) { + if (mViewPager.getCurrentItem() != tab.getPosition()) { + mViewPager.setCurrentItem(tab.getPosition()); + } + + tab.setText(getString(R.string.address_hint)); + switch (tab.getPosition()) { + case 0: + mProvince = mCity = mArea = null; + if (mTabLayout.getTabAt(2) != null) { + mTabLayout.removeTabAt(2); + mAdapter.removeItem(2); + } + if (mTabLayout.getTabAt(1) != null) { + mTabLayout.removeTabAt(1); + mAdapter.removeItem(1); + } + break; + case 1: + mCity = mArea = null; + if (mTabLayout.getTabAt(2) != null) { + mTabLayout.removeTabAt(2); + mAdapter.removeItem(2); + } + break; + case 2: + mArea = null; + break; + default: + break; + } } } - /** Tab条目被取消选中 */ @Override public void onTabUnselected(TabLayout.Tab tab) {} - /** Tab条目被重复点击 */ @Override public void onTabReselected(TabLayout.Tab tab) {} + + /** + * {@link BaseDialog.OnShowListener} + */ + + @Override + public void onShow(BaseDialog dialog) { + // 注册 ViewPager 滑动监听 + mViewPager.registerOnPageChangeCallback(mCallback); + } + + /** + * {@link BaseDialog.OnDismissListener} + */ + + @Override + public void onDismiss(BaseDialog dialog) { + // 反注册 ViewPager 滑动监听 + mViewPager.unregisterOnPageChangeCallback(mCallback); + } + } + + private final static class RecyclerViewAdapter extends MyAdapter> { + + private OnSelectListener mListener; + + private RecyclerViewAdapter(Context context) { + super(context); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(); + } + + final class ViewHolder extends MyAdapter.ViewHolder implements OnItemClickListener { + + private final AddressAdapter mAdapter; + + ViewHolder() { + super(new RecyclerView(getContext())); + RecyclerView recyclerView = (RecyclerView) getItemView(); + recyclerView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + mAdapter = new AddressAdapter(getContext()); + mAdapter.setOnItemClickListener(this); + recyclerView.setAdapter(mAdapter); + } + + @Override + public void onBindView(int position) { + mAdapter.setData(getItem(position)); + } + + @Override + public void onItemClick(RecyclerView recyclerView, View itemView, int position) { + if (mListener != null) { + mListener.onSelected(getViewHolderPosition(), position); + } + } + } + + private void setOnSelectListener(OnSelectListener listener) { + mListener = listener; + } + + public interface OnSelectListener { + + void onSelected(int recyclerViewPosition, int clickItemPosition); + } } - private static final class AddressDialogAdapter extends MyRecyclerViewAdapter { + private static final class AddressAdapter extends MyAdapter { - private AddressDialogAdapter(Context context) { + private AddressAdapter(Context context) { super(context); } @@ -334,14 +393,7 @@ private AddressDialogAdapter(Context context) { public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) { TextView textView = new TextView(parent.getContext()); textView.setGravity(Gravity.CENTER_VERTICAL); - TypedValue typedValue = new TypedValue(); - if (getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, typedValue, true)) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - textView.setBackground(getDrawable(typedValue.resourceId)); - } else { - textView.setBackgroundDrawable(getDrawable(typedValue.resourceId)); - } - } + textView.setBackgroundResource(R.drawable.selector_transparent); textView.setTextColor(0xFF222222); textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); textView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); @@ -352,11 +404,11 @@ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) { return new ViewHolder(textView); } - final class ViewHolder extends MyRecyclerViewAdapter.ViewHolder { + final class ViewHolder extends MyAdapter.ViewHolder { private final TextView mTextView; - public ViewHolder(View itemView) { + private ViewHolder(View itemView) { super(itemView); mTextView = (TextView) getItemView(); } @@ -371,9 +423,9 @@ public void onBindView(int position) { private static final class AddressBean { /** (省\市\区)的名称 */ - private String name; + private final String name; /** 下一级的 Json */ - private JSONObject next; + private final JSONObject next; private AddressBean(String name, JSONObject next) { this.name = name; @@ -471,7 +523,7 @@ private static List getAreaList(JSONObject jsonObject) { */ private static JSONArray getProvinceJson(Context context) { try { - InputStream inputStream = context.getAssets().open("province.json"); + InputStream inputStream = context.getResources().openRawResource(R.raw.province); ByteArrayOutputStream outStream = new ByteArrayOutputStream(); byte[] buffer = new byte[512]; int length; @@ -481,9 +533,7 @@ private static JSONArray getProvinceJson(Context context) { outStream.close(); inputStream.close(); return new JSONArray(outStream.toString()); - } catch (IOException e) { - e.printStackTrace(); - } catch (JSONException e) { + } catch (IOException | JSONException e) { e.printStackTrace(); } return null; @@ -504,6 +554,6 @@ public interface OnListener { /** * 点击取消时回调 */ - void onCancel(BaseDialog dialog); + default void onCancel(BaseDialog dialog) {} } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/dialog/AlbumDialog.java b/app/src/main/java/com/hjq/demo/ui/dialog/AlbumDialog.java index 7aad3c06..fc09a14a 100644 --- a/app/src/main/java/com/hjq/demo/ui/dialog/AlbumDialog.java +++ b/app/src/main/java/com/hjq/demo/ui/dialog/AlbumDialog.java @@ -8,15 +8,13 @@ import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.fragment.app.FragmentActivity; import androidx.recyclerview.widget.RecyclerView; +import com.hjq.base.BaseAdapter; import com.hjq.base.BaseDialog; -import com.hjq.base.BaseRecyclerViewAdapter; import com.hjq.demo.R; -import com.hjq.demo.common.MyDialogFragment; -import com.hjq.demo.common.MyRecyclerViewAdapter; -import com.hjq.image.ImageLoader; +import com.hjq.demo.common.MyAdapter; +import com.hjq.demo.http.glide.GlideApp; import java.util.List; @@ -29,22 +27,22 @@ public final class AlbumDialog { public static final class Builder - extends MyDialogFragment.Builder - implements BaseRecyclerViewAdapter.OnItemClickListener { + extends BaseDialog.Builder + implements BaseAdapter.OnItemClickListener { private OnListener mListener; private final RecyclerView mRecyclerView; private final AlbumAdapter mAdapter; - public Builder(FragmentActivity activity) { - super(activity); + public Builder(Context context) { + super(context); setContentView(R.layout.dialog_album); setHeight(getResources().getDisplayMetrics().heightPixels / 2); mRecyclerView = findViewById(R.id.rv_album_list); - mAdapter = new AlbumAdapter(activity); + mAdapter = new AlbumAdapter(context); mAdapter.setOnItemClickListener(this); mRecyclerView.setAdapter(mAdapter); } @@ -82,20 +80,18 @@ public void onItemClick(RecyclerView recyclerView, View itemView, int position) mAdapter.notifyDataSetChanged(); // 延迟消失 - postDelayed(new Runnable() { - @Override - public void run() { - if (mListener != null) { - mListener.onSelected(getDialog(), position, mAdapter.getItem(position)); - } - - dismiss(); + postDelayed(() -> { + + if (mListener != null) { + mListener.onSelected(getDialog(), position, mAdapter.getItem(position)); } + dismiss(); + }, 300); } } - private static final class AlbumAdapter extends MyRecyclerViewAdapter { + private static final class AlbumAdapter extends MyAdapter { private AlbumAdapter(Context context) { super(context); @@ -107,7 +103,7 @@ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new ViewHolder(); } - final class ViewHolder extends MyRecyclerViewAdapter.ViewHolder { + final class ViewHolder extends MyAdapter.ViewHolder { private final ImageView mIconView; private final TextView mNameView; @@ -126,7 +122,7 @@ private ViewHolder() { public void onBindView(int position) { AlbumBean bean = getItem(position); - ImageLoader.with(getContext()) + GlideApp.with(getContext()) .load(bean.getIcon()) .into(mIconView); diff --git a/app/src/main/java/com/hjq/demo/ui/dialog/CopyDialog.java b/app/src/main/java/com/hjq/demo/ui/dialog/CopyDialog.java index 0a6d853d..34c1d8f4 100644 --- a/app/src/main/java/com/hjq/demo/ui/dialog/CopyDialog.java +++ b/app/src/main/java/com/hjq/demo/ui/dialog/CopyDialog.java @@ -1,12 +1,11 @@ package com.hjq.demo.ui.dialog; +import android.content.Context; import android.view.Gravity; -import androidx.fragment.app.FragmentActivity; - import com.hjq.base.BaseDialog; +import com.hjq.base.action.AnimAction; import com.hjq.demo.R; -import com.hjq.demo.common.MyDialogFragment; /** * author : Android 轮子哥 @@ -17,13 +16,13 @@ public final class CopyDialog { public static final class Builder - extends MyDialogFragment.Builder { + extends BaseDialog.Builder { - public Builder(FragmentActivity activity) { - super(activity); + public Builder(Context context) { + super(context); setContentView(R.layout.dialog_copy); - setAnimStyle(BaseDialog.AnimStyle.BOTTOM); + setAnimStyle(AnimAction.BOTTOM); setGravity(Gravity.BOTTOM); } } diff --git a/app/src/main/java/com/hjq/demo/ui/dialog/DateDialog.java b/app/src/main/java/com/hjq/demo/ui/dialog/DateDialog.java index 929c4bd0..f6f534bc 100644 --- a/app/src/main/java/com/hjq/demo/ui/dialog/DateDialog.java +++ b/app/src/main/java/com/hjq/demo/ui/dialog/DateDialog.java @@ -1,15 +1,18 @@ package com.hjq.demo.ui.dialog; +import android.content.Context; import android.view.View; +import android.view.ViewGroup; import android.widget.TextView; -import androidx.annotation.StringRes; -import androidx.fragment.app.FragmentActivity; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; import com.hjq.base.BaseDialog; import com.hjq.demo.R; -import com.hjq.demo.common.MyDialogFragment; -import com.hjq.demo.widget.LoopView; +import com.hjq.demo.aop.SingleClick; +import com.hjq.demo.common.MyAdapter; +import com.hjq.demo.other.PickerLayoutManager; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -26,36 +29,39 @@ public final class DateDialog { public static final class Builder - extends MyDialogFragment.Builder - implements LoopView.LoopScrollListener, - View.OnClickListener { + extends UIDialog.Builder + implements PickerLayoutManager.OnPickerListener { - private final int mStartYear = 1920; - private final int mEndYear = 2019; + private final int mStartYear = Calendar.getInstance().get(Calendar.YEAR) - 100; + private final int mEndYear = Calendar.getInstance().get(Calendar.YEAR); - private final TextView mTitleView; - private final TextView mCancelView; - private final TextView mConfirmView; + private final RecyclerView mYearView; + private final RecyclerView mMonthView; + private final RecyclerView mDayView; - private final LoopView mYearView; - private final LoopView mMonthView; - private final LoopView mDayView; + private final PickerLayoutManager mYearManager; + private final PickerLayoutManager mMonthManager; + private final PickerLayoutManager mDayManager; + + private final PickerAdapter mYearAdapter; + private final PickerAdapter mMonthAdapter; + private final PickerAdapter mDayAdapter; private OnListener mListener; - private boolean mAutoDismiss = true; - @SuppressWarnings("all") - public Builder(FragmentActivity activity) { - super(activity); - setContentView(R.layout.dialog_date); + public Builder(Context context) { + super(context); + + setCustomView(R.layout.dialog_date); + setTitle(R.string.time_title); - mTitleView = findViewById(R.id.tv_date_title); - mCancelView = findViewById(R.id.tv_date_cancel); - mConfirmView = findViewById(R.id.tv_date_confirm); + mYearView = findViewById(R.id.rv_date_year); + mMonthView = findViewById(R.id.rv_date_month); + mDayView = findViewById(R.id.rv_date_day); - mYearView = findViewById(R.id.lv_date_year); - mMonthView = findViewById(R.id.lv_date_month); - mDayView = findViewById(R.id.lv_date_day); + mYearAdapter = new PickerAdapter(context); + mMonthAdapter = new PickerAdapter(context); + mDayAdapter = new PickerAdapter(context); // 生产年份 ArrayList yearData = new ArrayList<>(10); @@ -69,43 +75,40 @@ public Builder(FragmentActivity activity) { monthData.add(i + " " + getString(R.string.common_month)); } - mYearView.setData(yearData); - mMonthView.setData(monthData); - - mYearView.setLoopListener(this); - mMonthView.setLoopListener(this); + Calendar calendar = Calendar.getInstance(Locale.CHINA); - mCancelView.setOnClickListener(this); - mConfirmView.setOnClickListener(this); + int day = calendar.getActualMaximum(Calendar.DATE); + // 生产天数 + ArrayList dayData = new ArrayList<>(day); + for (int i = 1; i <= day; i++) { + dayData.add(i + " " + getString(R.string.common_day)); + } - Calendar calendar = Calendar.getInstance(); + mYearAdapter.setData(yearData); + mMonthAdapter.setData(monthData); + mDayAdapter.setData(dayData); + + mYearManager = new PickerLayoutManager.Builder(context) + .build(); + mMonthManager = new PickerLayoutManager.Builder(context) + .build(); + mDayManager = new PickerLayoutManager.Builder(context) + .build(); + + mYearView.setLayoutManager(mYearManager); + mMonthView.setLayoutManager(mMonthManager); + mDayView.setLayoutManager(mDayManager); + + mYearView.setAdapter(mYearAdapter); + mMonthView.setAdapter(mMonthAdapter); + mDayView.setAdapter(mDayAdapter); + setYear(calendar.get(Calendar.YEAR)); setMonth(calendar.get(Calendar.MONTH) + 1); setDay(calendar.get(Calendar.DAY_OF_MONTH)); - } - - public Builder setTitle(@StringRes int id) { - return setTitle(getString(id)); - } - public Builder setTitle(CharSequence text) { - mTitleView.setText(text); - return this; - } - public Builder setCancel(@StringRes int id) { - return setCancel(getString(id)); - } - public Builder setCancel(CharSequence text) { - mCancelView.setText(text); - return this; - } - - public Builder setConfirm(@StringRes int id) { - return setConfirm(getString(id)); - } - public Builder setConfirm(CharSequence text) { - mConfirmView.setText(text); - return this; + mYearManager.setOnPickerListener(this); + mMonthManager.setOnPickerListener(this); } public Builder setListener(OnListener listener) { @@ -113,11 +116,6 @@ public Builder setListener(OnListener listener) { return this; } - public Builder setAutoDismiss(boolean dismiss) { - mAutoDismiss = dismiss; - return this; - } - /** * 不选择天数 */ @@ -156,10 +154,10 @@ public Builder setYear(int year) { int index = year - mStartYear; if (index < 0) { index = 0; - } else if (index > mYearView.getSize() - 1) { - index = mYearView.getSize() - 1; + } else if (index > mYearAdapter.getItemCount() - 1) { + index = mYearAdapter.getItemCount() - 1; } - mYearView.setInitPosition(index); + mYearView.scrollToPosition(index); return this; } @@ -171,10 +169,10 @@ public Builder setMonth(int month) { int index = month - 1; if (index < 0) { index = 0; - } else if (index > mMonthView.getSize() - 1) { - index = mMonthView.getSize() - 1; + } else if (index > mMonthAdapter.getItemCount() - 1) { + index = mMonthAdapter.getItemCount() - 1; } - mMonthView.setInitPosition(index); + mMonthView.scrollToPosition(index); return this; } @@ -186,48 +184,84 @@ public Builder setDay(int day) { int index = day - 1; if (index < 0) { index = 0; - } else if (index > mDayView.getSize() - 1) { - index = mDayView.getSize() - 1; + } else if (index > mDayAdapter.getItemCount() - 1) { + index = mDayAdapter.getItemCount() - 1; } - mDayView.setInitPosition(index); + mDayView.scrollToPosition(index); return this; } + /** + * {@link PickerLayoutManager.OnPickerListener} + * + * @param recyclerView RecyclerView 对象 + * @param position 当前滚动的位置 + */ @Override - public void onItemSelect(LoopView loopView, int position) { + public void onPicked(RecyclerView recyclerView, int position) { // 获取这个月最多有多少天 Calendar calendar = Calendar.getInstance(Locale.CHINA); - if (loopView == mYearView) { - calendar.set(mStartYear + mYearView.getSelectedItem(), mMonthView.getSelectedItem(), 1); - } else if (loopView == mMonthView) { - calendar.set(mStartYear + mYearView.getSelectedItem(), mMonthView.getSelectedItem(), 1); + if (recyclerView == mYearView) { + calendar.set(mStartYear + position, mMonthManager.getPickedPosition(), 1); + } else if (recyclerView == mMonthView) { + calendar.set(mStartYear + mYearManager.getPickedPosition(), position, 1); } int day = calendar.getActualMaximum(Calendar.DATE); - - ArrayList dayData = new ArrayList<>(day); - for (int i = 1; i <= day; i++) { - dayData.add(i + " " + getString(R.string.common_day)); + if (mDayAdapter.getItemCount() != day) { + ArrayList dayData = new ArrayList<>(day); + for (int i = 1; i <= day; i++) { + dayData.add(i + " " + getString(R.string.common_day)); + } + mDayAdapter.setData(dayData); } - - mDayView.setData(dayData); } - /** - * {@link View.OnClickListener} - */ - + @SingleClick @Override public void onClick(View v) { - if (mAutoDismiss) { - dismiss(); + switch (v.getId()) { + case R.id.tv_ui_confirm: + autoDismiss(); + if (mListener != null) { + mListener.onSelected(getDialog(), mStartYear + mYearManager.getPickedPosition(), mMonthManager.getPickedPosition() + 1, mDayManager.getPickedPosition() + 1); + } + break; + case R.id.tv_ui_cancel: + autoDismiss(); + if (mListener != null) { + mListener.onCancel(getDialog()); + } + break; + default: + break; + } + } + + private static final class PickerAdapter extends MyAdapter { + + private PickerAdapter(Context context) { + super(context); } - if (mListener != null) { - if (v == mConfirmView) { - mListener.onSelected(getDialog(), mStartYear + mYearView.getSelectedItem(), mMonthView.getSelectedItem() + 1, mDayView.getSelectedItem() + 1); - } else if (v == mCancelView) { - mListener.onCancel(getDialog()); + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(); + } + + final class ViewHolder extends MyAdapter.ViewHolder { + + private final TextView mPickerView; + + ViewHolder() { + super(R.layout.item_picker); + mPickerView = (TextView) findViewById(R.id.tv_picker_name); + } + + @Override + public void onBindView(int position) { + mPickerView.setText(getItem(position)); } } } @@ -247,6 +281,6 @@ public interface OnListener { /** * 点击取消时回调 */ - void onCancel(BaseDialog dialog); + default void onCancel(BaseDialog dialog) {} } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/dialog/InputDialog.java b/app/src/main/java/com/hjq/demo/ui/dialog/InputDialog.java index 5d20a110..9ab24ba8 100644 --- a/app/src/main/java/com/hjq/demo/ui/dialog/InputDialog.java +++ b/app/src/main/java/com/hjq/demo/ui/dialog/InputDialog.java @@ -1,16 +1,15 @@ package com.hjq.demo.ui.dialog; +import android.content.Context; import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; -import android.widget.TextView; import androidx.annotation.StringRes; -import androidx.fragment.app.FragmentActivity; import com.hjq.base.BaseDialog; import com.hjq.demo.R; -import com.hjq.demo.common.MyDialogFragment; +import com.hjq.demo.aop.SingleClick; /** * author : Android 轮子哥 @@ -21,48 +20,23 @@ public final class InputDialog { public static final class Builder - extends MyDialogFragment.Builder - implements View.OnClickListener, - BaseDialog.OnShowListener, + extends UIDialog.Builder + implements BaseDialog.OnShowListener, BaseDialog.OnDismissListener { private OnListener mListener; - private boolean mAutoDismiss = true; - - private final TextView mTitleView; private final EditText mInputView; - private final TextView mCancelView; - private final View mLineView; - private final TextView mConfirmView; - - public Builder(FragmentActivity activity) { - super(activity); - setContentView(R.layout.dialog_input); - setAnimStyle(BaseDialog.AnimStyle.IOS); + public Builder(Context context) { + super(context); + setCustomView(R.layout.dialog_input); - mTitleView = findViewById(R.id.tv_input_title); mInputView = findViewById(R.id.tv_input_message); - mCancelView = findViewById(R.id.tv_input_cancel); - mLineView = findViewById(R.id.v_input_line); - mConfirmView = findViewById(R.id.tv_input_confirm); - - mCancelView.setOnClickListener(this); - mConfirmView.setOnClickListener(this); - addOnShowListener(this); addOnDismissListener(this); } - public Builder setTitle(@StringRes int id) { - return setTitle(getString(id)); - } - public Builder setTitle(CharSequence text) { - mTitleView.setText(text); - return this; - } - public Builder setHint(@StringRes int id) { return setHint(getString(id)); } @@ -84,29 +58,6 @@ public Builder setContent(CharSequence text) { return this; } - public Builder setConfirm(@StringRes int id) { - return setConfirm(getString(id)); - } - public Builder setConfirm(CharSequence text) { - mConfirmView.setText(text); - return this; - } - - public Builder setCancel(@StringRes int id) { - return setCancel(getString(id)); - } - public Builder setCancel(CharSequence text) { - mCancelView.setText(text); - - mLineView.setVisibility((text == null || "".equals(text.toString())) ? View.GONE : View.VISIBLE); - return this; - } - - public Builder setAutoDismiss(boolean dismiss) { - mAutoDismiss = dismiss; - return this; - } - public Builder setListener(OnListener listener) { mListener = listener; return this; @@ -117,12 +68,7 @@ public Builder setListener(OnListener listener) { */ @Override public void onShow(BaseDialog dialog) { - postDelayed(new Runnable() { - @Override - public void run() { - getSystemService(InputMethodManager.class).showSoftInput(mInputView, 0); - } - }, 500); + postDelayed(() -> getSystemService(InputMethodManager.class).showSoftInput(mInputView, 0), 500); } /** @@ -133,22 +79,24 @@ public void onDismiss(BaseDialog dialog) { getSystemService(InputMethodManager.class).hideSoftInputFromWindow(mInputView.getWindowToken(), 0); } - /** - * {@link View.OnClickListener} - */ + @SingleClick @Override public void onClick(View v) { - if (mAutoDismiss) { - dismiss(); - } - - if (mListener != null) { - if (v == mConfirmView) { - // 判断输入是否为空 - mListener.onConfirm(getDialog(), mInputView.getText().toString()); - } else if (v == mCancelView) { - mListener.onCancel(getDialog()); - } + switch (v.getId()) { + case R.id.tv_ui_confirm: + autoDismiss(); + if (mListener != null) { + mListener.onConfirm(getDialog(), mInputView.getText().toString()); + } + break; + case R.id.tv_ui_cancel: + autoDismiss(); + if (mListener != null) { + mListener.onCancel(getDialog()); + } + break; + default: + break; } } } @@ -163,6 +111,6 @@ public interface OnListener { /** * 点击取消时回调 */ - void onCancel(BaseDialog dialog); + default void onCancel(BaseDialog dialog) {} } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/dialog/MenuDialog.java b/app/src/main/java/com/hjq/demo/ui/dialog/MenuDialog.java index e67f8bf6..b27dd970 100644 --- a/app/src/main/java/com/hjq/demo/ui/dialog/MenuDialog.java +++ b/app/src/main/java/com/hjq/demo/ui/dialog/MenuDialog.java @@ -8,15 +8,14 @@ import androidx.annotation.NonNull; import androidx.annotation.StringRes; -import androidx.fragment.app.FragmentActivity; -import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.hjq.base.BaseAdapter; import com.hjq.base.BaseDialog; -import com.hjq.base.BaseRecyclerViewAdapter; +import com.hjq.base.action.AnimAction; import com.hjq.demo.R; -import com.hjq.demo.common.MyDialogFragment; -import com.hjq.demo.common.MyRecyclerViewAdapter; +import com.hjq.demo.aop.SingleClick; +import com.hjq.demo.common.MyAdapter; import java.util.ArrayList; import java.util.Arrays; @@ -31,31 +30,28 @@ public final class MenuDialog { public static final class Builder - extends MyDialogFragment.Builder - implements View.OnClickListener, - BaseRecyclerViewAdapter.OnItemClickListener { + extends BaseDialog.Builder + implements BaseAdapter.OnItemClickListener { private OnListener mListener; private boolean mAutoDismiss = true; - private final RecyclerView mRecyclerView; private final MenuAdapter mAdapter; private final TextView mCancelView; - public Builder(FragmentActivity activity) { - super(activity); + public Builder(Context context) { + super(context); setContentView(R.layout.dialog_menu); - setAnimStyle(BaseDialog.AnimStyle.BOTTOM); + setAnimStyle(AnimAction.BOTTOM); - mRecyclerView = findViewById(R.id.rv_menu_list); + RecyclerView recyclerView = findViewById(R.id.rv_menu_list); mCancelView = findViewById(R.id.tv_menu_cancel); - mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); mAdapter = new MenuAdapter(getContext()); mAdapter.setOnItemClickListener(this); - mRecyclerView.setAdapter(mAdapter); + recyclerView.setAdapter(mAdapter); - mCancelView.setOnClickListener(this); + setOnClickListener(R.id.tv_menu_cancel); } @Override @@ -67,7 +63,7 @@ public Builder setGravity(int gravity) { // 不显示取消按钮 setCancel(null); // 重新设置动画 - setAnimStyle(BaseDialog.AnimStyle.SCALE); + setAnimStyle(AnimAction.SCALE); break; default: break; @@ -112,9 +108,7 @@ public Builder setListener(OnListener listener) { return this; } - /** - * {@link View.OnClickListener} - */ + @SingleClick @Override public void onClick(View v) { if (mAutoDismiss) { @@ -129,7 +123,7 @@ public void onClick(View v) { } /** - * {@link BaseRecyclerViewAdapter.OnItemClickListener} + * {@link BaseAdapter.OnItemClickListener} */ @SuppressWarnings("all") @Override @@ -144,7 +138,7 @@ public void onItemClick(RecyclerView recyclerView, View itemView, int position) } } - private static final class MenuAdapter extends MyRecyclerViewAdapter { + private static final class MenuAdapter extends MyAdapter { private MenuAdapter(Context context) { super(context); @@ -156,15 +150,15 @@ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new ViewHolder(); } - final class ViewHolder extends MyRecyclerViewAdapter.ViewHolder { + final class ViewHolder extends MyAdapter.ViewHolder { private final TextView mTextView; - private final View mView; + private final View mLineView; - public ViewHolder() { + ViewHolder() { super(R.layout.item_menu); - mTextView = (TextView) findViewById(R.id.tv_menu_name); - mView = findViewById(R.id.v_menu_line); + mTextView = (TextView) findViewById(R.id.tv_menu_text); + mLineView = findViewById(R.id.v_menu_line); } @Override @@ -174,14 +168,14 @@ public void onBindView(int position) { if (position == 0) { // 当前是否只有一个条目 if (getItemCount() == 1) { - mView.setVisibility(View.GONE); + mLineView.setVisibility(View.GONE); } else { - mView.setVisibility(View.VISIBLE); + mLineView.setVisibility(View.VISIBLE); } } else if (position == getItemCount() - 1) { - mView.setVisibility(View.GONE); + mLineView.setVisibility(View.GONE); } else { - mView.setVisibility(View.VISIBLE); + mLineView.setVisibility(View.VISIBLE); } } } @@ -197,6 +191,6 @@ public interface OnListener { /** * 点击取消时回调 */ - void onCancel(BaseDialog dialog); + default void onCancel(BaseDialog dialog) {} } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/dialog/MessageDialog.java b/app/src/main/java/com/hjq/demo/ui/dialog/MessageDialog.java index a1e324db..3869cba6 100644 --- a/app/src/main/java/com/hjq/demo/ui/dialog/MessageDialog.java +++ b/app/src/main/java/com/hjq/demo/ui/dialog/MessageDialog.java @@ -1,14 +1,14 @@ package com.hjq.demo.ui.dialog; +import android.content.Context; import android.view.View; import android.widget.TextView; import androidx.annotation.StringRes; -import androidx.fragment.app.FragmentActivity; import com.hjq.base.BaseDialog; import com.hjq.demo.R; -import com.hjq.demo.common.MyDialogFragment; +import com.hjq.demo.aop.SingleClick; /** * author : Android 轮子哥 @@ -19,41 +19,16 @@ public final class MessageDialog { public static final class Builder - extends MyDialogFragment.Builder - implements View.OnClickListener { + extends UIDialog.Builder { private OnListener mListener; - private boolean mAutoDismiss = true; - private final TextView mTitleView; private final TextView mMessageView; - private final TextView mCancelView; - private final View mLineView; - private final TextView mConfirmView; - - public Builder(FragmentActivity activity) { - super(activity); - setContentView(R.layout.dialog_message); - setAnimStyle(BaseDialog.AnimStyle.IOS); - - mTitleView = findViewById(R.id.tv_message_title); + public Builder(Context context) { + super(context); + setCustomView(R.layout.dialog_message); mMessageView = findViewById(R.id.tv_message_message); - - mCancelView = findViewById(R.id.tv_message_cancel); - mLineView = findViewById(R.id.v_message_line); - mConfirmView = findViewById(R.id.tv_message_confirm); - - mCancelView.setOnClickListener(this); - mConfirmView.setOnClickListener(this); - } - - public Builder setTitle(@StringRes int id) { - return setTitle(getString(id)); - } - public Builder setTitle(CharSequence text) { - mTitleView.setText(text); - return this; } public Builder setMessage(@StringRes int id) { @@ -64,28 +39,6 @@ public Builder setMessage(CharSequence text) { return this; } - public Builder setCancel(@StringRes int id) { - return setCancel(getString(id)); - } - public Builder setCancel(CharSequence text) { - mCancelView.setText(text); - mLineView.setVisibility((text == null || "".equals(text.toString())) ? View.GONE : View.VISIBLE); - return this; - } - - public Builder setConfirm(@StringRes int id) { - return setConfirm(getString(id)); - } - public Builder setConfirm(CharSequence text) { - mConfirmView.setText(text); - return this; - } - - public Builder setAutoDismiss(boolean dismiss) { - mAutoDismiss = dismiss; - return this; - } - public Builder setListener(OnListener listener) { mListener = listener; return this; @@ -100,21 +53,24 @@ public BaseDialog create() { return super.create(); } - /** - * {@link View.OnClickListener} - */ + @SingleClick @Override public void onClick(View v) { - if (mAutoDismiss) { - dismiss(); - } - - if (mListener != null) { - if (v == mConfirmView) { - mListener.onConfirm(getDialog()); - } else if (v == mCancelView) { - mListener.onCancel(getDialog()); - } + switch (v.getId()) { + case R.id.tv_ui_confirm: + autoDismiss(); + if (mListener != null) { + mListener.onConfirm(getDialog()); + } + break; + case R.id.tv_ui_cancel: + autoDismiss(); + if (mListener != null) { + mListener.onCancel(getDialog()); + } + break; + default: + break; } } } @@ -129,6 +85,6 @@ public interface OnListener { /** * 点击取消时回调 */ - void onCancel(BaseDialog dialog); + default void onCancel(BaseDialog dialog) {} } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/dialog/PayPasswordDialog.java b/app/src/main/java/com/hjq/demo/ui/dialog/PayPasswordDialog.java index da77e265..7fadca66 100644 --- a/app/src/main/java/com/hjq/demo/ui/dialog/PayPasswordDialog.java +++ b/app/src/main/java/com/hjq/demo/ui/dialog/PayPasswordDialog.java @@ -8,15 +8,14 @@ import androidx.annotation.NonNull; import androidx.annotation.StringRes; -import androidx.fragment.app.FragmentActivity; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.hjq.base.BaseAdapter; import com.hjq.base.BaseDialog; -import com.hjq.base.BaseRecyclerViewAdapter; import com.hjq.demo.R; -import com.hjq.demo.common.MyDialogFragment; -import com.hjq.demo.common.MyRecyclerViewAdapter; +import com.hjq.demo.aop.SingleClick; +import com.hjq.demo.common.MyAdapter; import com.hjq.demo.widget.PasswordView; import java.util.Arrays; @@ -31,9 +30,8 @@ public final class PayPasswordDialog { public static final class Builder - extends MyDialogFragment.Builder - implements BaseRecyclerViewAdapter.OnItemClickListener, - View.OnClickListener { + extends BaseDialog.Builder + implements BaseAdapter.OnItemClickListener { /** 输入键盘文本 */ private static final String[] KEYBOARD_TEXT = new String[]{"1", "2", "3", "4", "5", "6", "7", "8", "9", "", "0", ""}; @@ -50,12 +48,10 @@ public static final class Builder private final PasswordView mPasswordView; - private final RecyclerView mRecyclerView; - private final KeyboardAdapter mAdapter; - public Builder(FragmentActivity activity) { - super(activity); + public Builder(Context context) { + super(context); setContentView(R.layout.dialog_pay_password); setCancelable(false); @@ -66,15 +62,14 @@ public Builder(FragmentActivity activity) { mMoneyView = findViewById(R.id.tv_pay_money); mPasswordView = findViewById(R.id.pw_pay_view); - mRecyclerView = findViewById(R.id.rv_pay_list); - - mCloseView.setOnClickListener(this); - mRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 3)); + RecyclerView recyclerView = findViewById(R.id.rv_pay_list); mAdapter = new KeyboardAdapter(getContext()); mAdapter.setData(Arrays.asList(KEYBOARD_TEXT)); mAdapter.setOnItemClickListener(this); - mRecyclerView.setAdapter(mAdapter); + recyclerView.setAdapter(mAdapter); + + setOnClickListener(R.id.iv_pay_close); } public Builder setTitle(@StringRes int id) { @@ -115,7 +110,7 @@ public Builder setListener(OnListener listener) { } /** - * {@link BaseRecyclerViewAdapter.OnItemClickListener} + * {@link BaseAdapter.OnItemClickListener} */ @Override public void onItemClick(RecyclerView recyclerView, View itemView, int position) { @@ -139,20 +134,18 @@ public void onItemClick(RecyclerView recyclerView, View itemView, int position) // 判断密码是否已经输入完毕 if (mRecordList.size() == PasswordView.PASSWORD_COUNT) { if (mListener != null) { - postDelayed(new Runnable() { - @Override - public void run() { - - if (mAutoDismiss) { - dismiss(); - } - // 获取输入的支付密码 - StringBuilder password = new StringBuilder(); - for (String s : mRecordList) { - password.append(s); - } - mListener.onCompleted(getDialog(), password.toString()); + postDelayed(() -> { + + if (mAutoDismiss) { + dismiss(); } + // 获取输入的支付密码 + StringBuilder password = new StringBuilder(); + for (String s : mRecordList) { + password.append(s); + } + mListener.onCompleted(getDialog(), password.toString()); + }, 300); } } @@ -161,6 +154,7 @@ public void run() { mPasswordView.setPassWordLength(mRecordList.size()); } + @SingleClick @Override public void onClick(View v) { if (v == mCloseView) { @@ -175,7 +169,7 @@ public void onClick(View v) { } } - private static final class KeyboardAdapter extends MyRecyclerViewAdapter { + private static final class KeyboardAdapter extends MyAdapter { /** 数字按钮条目 */ private static final int TYPE_NORMAL = 0; @@ -202,18 +196,18 @@ public int getItemViewType(int position) { @NonNull @Override - public MyRecyclerViewAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + public MyAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { switch (viewType) { case TYPE_DELETE: - return new MyRecyclerViewAdapter.SimpleHolder(R.layout.item_pay_password_delete); + return new MyAdapter.SimpleHolder(R.layout.item_pay_password_delete); case TYPE_EMPTY: - return new MyRecyclerViewAdapter.SimpleHolder(R.layout.item_pay_password_empty); + return new MyAdapter.SimpleHolder(R.layout.item_pay_password_empty); default: return new KeyboardAdapter.ViewHolder(); } } - final class ViewHolder extends MyRecyclerViewAdapter.ViewHolder { + final class ViewHolder extends MyAdapter.ViewHolder { private final TextView mTextView; @@ -227,6 +221,11 @@ public void onBindView(int position) { mTextView.setText(getItem(position)); } } + + @Override + protected RecyclerView.LayoutManager generateDefaultLayoutManager(Context context) { + return new GridLayoutManager(getContext(), 3); + } } public interface OnListener { @@ -234,13 +233,13 @@ public interface OnListener { /** * 输入完成时回调 * - * @param password 六位支付密码 + * @param password 输入的密码 */ void onCompleted(BaseDialog dialog, String password); /** * 点击取消时回调 */ - void onCancel(BaseDialog dialog); + default void onCancel(BaseDialog dialog) {} } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/dialog/SelectDialog.java b/app/src/main/java/com/hjq/demo/ui/dialog/SelectDialog.java new file mode 100644 index 00000000..1adf0cd2 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/ui/dialog/SelectDialog.java @@ -0,0 +1,254 @@ +package com.hjq.demo.ui.dialog; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.hjq.base.BaseAdapter; +import com.hjq.base.BaseDialog; +import com.hjq.demo.R; +import com.hjq.demo.aop.SingleClick; +import com.hjq.demo.common.MyAdapter; +import com.hjq.toast.ToastUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/10/09 + * desc : 单选或者多选对话框 + */ +public final class SelectDialog { + + public static final class Builder + extends UIDialog.Builder { + + private OnListener mListener; + + private final SelectAdapter mAdapter; + + public Builder(Context context) { + super(context); + + setCustomView(R.layout.dialog_select); + RecyclerView recyclerView = findViewById(R.id.rv_select_list); + recyclerView.setItemAnimator(null); + + mAdapter = new SelectAdapter(getContext()); + recyclerView.setAdapter(mAdapter); + } + + public Builder setList(int... ids) { + List data = new ArrayList<>(ids.length); + for (int id : ids) { + data.add(getString(id)); + } + return setList(data); + } + + public Builder setList(String... data) { + return setList(Arrays.asList(data)); + } + + @SuppressWarnings("all") + public Builder setList(List data) { + mAdapter.setData(data); + return this; + } + + /** + * 设置默认选中的位置 + */ + public Builder setSelect(int... positions) { + mAdapter.setSelect(positions); + return this; + } + + /** + * 设置最大选择数量 + */ + public Builder setMaxSelect(int count) { + mAdapter.setMaxSelect(count); + return this; + } + + /** + * 设置最小选择数量 + */ + public Builder setMinSelect(int count) { + mAdapter.setMinSelect(count); + return this; + } + + /** + * 设置单选模式 + */ + public Builder setSingleSelect() { + mAdapter.setSingleSelect(); + return this; + } + + public Builder setListener(OnListener listener) { + mListener = listener; + return this; + } + + @SingleClick + @SuppressWarnings("all") + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.tv_ui_confirm: + HashMap data = mAdapter.getSelectSet(); + if (data.size() >= mAdapter.getMinSelect()) { + autoDismiss(); + if (mListener != null) { + mListener.onSelected(getDialog(), data); + } + } else { + ToastUtils.show(String.format(getString(R.string.select_min_hint), mAdapter.getMinSelect())); + } + break; + case R.id.tv_ui_cancel: + autoDismiss(); + if (mListener != null) { + mListener.onCancel(getDialog()); + } + break; + default: + break; + } + } + } + + private static final class SelectAdapter extends MyAdapter + implements BaseAdapter.OnItemClickListener { + + /** 最小选择数量 */ + private int mMinSelect = 1; + /** 最大选择数量 */ + private int mMaxSelect = Integer.MAX_VALUE; + + /** 选择对象集合 */ + @SuppressLint("UseSparseArrays") + private final HashMap mSelectSet = new HashMap<>(); + + private SelectAdapter(Context context) { + super(context); + setOnItemClickListener(this); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(); + } + + private void setSelect(int... positions) { + for (int position : positions) { + mSelectSet.put(position, getItem(position)); + } + notifyDataSetChanged(); + } + + private void setMaxSelect(int count) { + mMaxSelect = count; + } + + private void setMinSelect(int count) { + mMinSelect = count; + } + + private int getMinSelect() { + return mMinSelect; + } + + private void setSingleSelect() { + setMaxSelect(1); + setMinSelect(1); + } + + private boolean isSingleSelect() { + return mMaxSelect == 1 && mMinSelect == 1; + } + + private HashMap getSelectSet() { + return mSelectSet; + } + + /** + * {@link BaseAdapter.OnItemClickListener} + */ + + @Override + public void onItemClick(RecyclerView recyclerView, View itemView, int position) { + if (mSelectSet.containsKey(position)) { + // 当前必须不是单选模式才能取消选中 + if (!isSingleSelect()) { + mSelectSet.remove(position); + notifyItemChanged(position); + } + } else { + if (mMaxSelect == 1) { + mSelectSet.clear(); + notifyDataSetChanged(); + } + + if (mSelectSet.size() < mMaxSelect) { + mSelectSet.put(position, getItem(position)); + notifyItemChanged(position); + } else { + ToastUtils.show(String.format(getString(R.string.select_max_hint), mMaxSelect)); + } + } + } + + final class ViewHolder extends MyAdapter.ViewHolder { + + private final TextView mTextView; + private final CheckBox mCheckBox; + + ViewHolder() { + super(R.layout.item_select); + mTextView = (TextView) findViewById(R.id.tv_select_text); + mCheckBox = (CheckBox) findViewById(R.id.tv_select_checkbox); + } + + @Override + public void onBindView(int position) { + mTextView.setText(getItem(position).toString()); + mCheckBox.setChecked(mSelectSet.containsKey(position)); + if (mMaxSelect == 1) { + mCheckBox.setClickable(false); + } else { + mCheckBox.setEnabled(false); + } + } + } + } + + public interface OnListener { + + /** + * 选择回调 + * + * @param data 选择的位置和数据 + */ + void onSelected(BaseDialog dialog, HashMap data); + + /** + * 取消回调 + */ + default void onCancel(BaseDialog dialog) {} + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/dialog/ShareDialog.java b/app/src/main/java/com/hjq/demo/ui/dialog/ShareDialog.java index e2430a00..b71ad4b8 100644 --- a/app/src/main/java/com/hjq/demo/ui/dialog/ShareDialog.java +++ b/app/src/main/java/com/hjq/demo/ui/dialog/ShareDialog.java @@ -11,14 +11,14 @@ import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; -import androidx.fragment.app.FragmentActivity; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.hjq.base.BaseRecyclerViewAdapter; +import com.hjq.base.BaseAdapter; +import com.hjq.base.BaseDialog; import com.hjq.demo.R; -import com.hjq.demo.common.MyDialogFragment; -import com.hjq.demo.common.MyRecyclerViewAdapter; +import com.hjq.demo.common.MyAdapter; +import com.hjq.toast.ToastUtils; import com.hjq.umeng.Platform; import com.hjq.umeng.UmengClient; import com.hjq.umeng.UmengShare; @@ -35,18 +35,17 @@ public final class ShareDialog { public static final class Builder - extends MyDialogFragment.Builder - implements BaseRecyclerViewAdapter.OnItemClickListener { + extends BaseDialog.Builder + implements BaseAdapter.OnItemClickListener { private final ShareAdapter mAdapter; - private final RecyclerView mRecyclerView; private final UmengShare.ShareData mData; private UmengShare.OnShareListener mListener; - public Builder(FragmentActivity activity) { - super(activity); + public Builder(Context context) { + super(context); setContentView(R.layout.dialog_share); @@ -57,14 +56,15 @@ public Builder(FragmentActivity activity) { data.add(new ShareBean(getDrawable(R.drawable.ic_share_qzone), getString(R.string.share_platform_qzone), Platform.QZONE)); data.add(new ShareBean(getDrawable(R.drawable.ic_share_link), getString(R.string.share_platform_link), null)); - mRecyclerView = findViewById(R.id.rv_share_list); - mAdapter = new ShareAdapter(activity); + mAdapter = new ShareAdapter(context); mAdapter.setData(data); mAdapter.setOnItemClickListener(this); - mRecyclerView.setLayoutManager(new GridLayoutManager(activity, data.size())); - mRecyclerView.setAdapter(mAdapter); - mData = new UmengShare.ShareData(getActivity()); + RecyclerView recyclerView = findViewById(R.id.rv_share_list); + recyclerView.setLayoutManager(new GridLayoutManager(context, data.size())); + recyclerView.setAdapter(mAdapter); + + mData = new UmengShare.ShareData(context); } public Builder setShareTitle(String title) { @@ -98,7 +98,7 @@ public Builder setListener(UmengShare.OnShareListener listener) { } /** - * {@link BaseRecyclerViewAdapter.OnItemClickListener} + * {@link BaseAdapter.OnItemClickListener} */ @Override public void onItemClick(RecyclerView recyclerView, View itemView, int position) { @@ -108,13 +108,13 @@ public void onItemClick(RecyclerView recyclerView, View itemView, int position) } else { // 复制到剪贴板 getSystemService(ClipboardManager.class).setPrimaryClip(ClipData.newPlainText("url", mData.getShareUrl())); - toast(R.string.share_platform_copy_hint); + ToastUtils.show(R.string.share_platform_copy_hint); } dismiss(); } } - private static class ShareAdapter extends MyRecyclerViewAdapter { + private static class ShareAdapter extends MyAdapter { private ShareAdapter(Context context) { super(context); @@ -126,10 +126,10 @@ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new ViewHolder(); } - final class ViewHolder extends MyRecyclerViewAdapter.ViewHolder { + final class ViewHolder extends MyAdapter.ViewHolder { - private ImageView mImageView; - private TextView mTextView; + private final ImageView mImageView; + private final TextView mTextView; private ViewHolder() { super(R.layout.item_share); @@ -149,28 +149,28 @@ public void onBindView(int position) { private static class ShareBean { /** 分享图标 */ - private final Drawable shareIcon; + private final Drawable mShareIcon; /** 分享名称 */ - private final String shareName; + private final String mShareName; /** 分享平台 */ - private final Platform sharePlatform; + private final Platform mSharePlatform; private ShareBean(Drawable icon, String name, Platform platform) { - shareIcon = icon; - shareName = name; - sharePlatform = platform; + mShareIcon = icon; + mShareName = name; + mSharePlatform = platform; } private Drawable getShareIcon() { - return shareIcon; + return mShareIcon; } private String getShareName() { - return shareName; + return mShareName; } private Platform getSharePlatform() { - return sharePlatform; + return mSharePlatform; } } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/dialog/TimeDialog.java b/app/src/main/java/com/hjq/demo/ui/dialog/TimeDialog.java index 8d9cd90b..d7d134cb 100644 --- a/app/src/main/java/com/hjq/demo/ui/dialog/TimeDialog.java +++ b/app/src/main/java/com/hjq/demo/ui/dialog/TimeDialog.java @@ -1,15 +1,18 @@ package com.hjq.demo.ui.dialog; +import android.content.Context; import android.view.View; +import android.view.ViewGroup; import android.widget.TextView; -import androidx.annotation.StringRes; -import androidx.fragment.app.FragmentActivity; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; import com.hjq.base.BaseDialog; import com.hjq.demo.R; -import com.hjq.demo.common.MyDialogFragment; -import com.hjq.demo.widget.LoopView; +import com.hjq.demo.aop.SingleClick; +import com.hjq.demo.common.MyAdapter; +import com.hjq.demo.other.PickerLayoutManager; import java.util.ArrayList; import java.util.Calendar; @@ -23,35 +26,35 @@ public final class TimeDialog { public static final class Builder - extends MyDialogFragment.Builder - implements View.OnClickListener { + extends UIDialog.Builder implements Runnable { - private final TextView mTitleView; - private final TextView mCancelView; - private final View mLineView; - private final TextView mConfirmView; + private final RecyclerView mHourView; + private final RecyclerView mMinuteView; + private final RecyclerView mSecondView; - private final LoopView mHourView; - private final LoopView mMinuteView; - private final LoopView mSecondView; + private final PickerLayoutManager mHourManager; + private final PickerLayoutManager mMinuteManager; + private final PickerLayoutManager mSecondManager; + + private final PickerAdapter mHourAdapter; + private final PickerAdapter mMinuteAdapter; + private final PickerAdapter mSecondAdapter; private OnListener mListener; - private boolean mAutoDismiss = true; @SuppressWarnings("all") - public Builder(FragmentActivity activity) { - super(activity); - setContentView(R.layout.dialog_time); - setAnimStyle(BaseDialog.AnimStyle.IOS); + public Builder(Context context) { + super(context); + setCustomView(R.layout.dialog_time); + setTitle(R.string.time_title); - mTitleView = findViewById(R.id.tv_time_title); - mCancelView = findViewById(R.id.tv_time_cancel); - mLineView = findViewById(R.id.v_time_line); - mConfirmView = findViewById(R.id.tv_time_confirm); + mHourView = findViewById(R.id.rv_time_hour); + mMinuteView = findViewById(R.id.rv_time_minute); + mSecondView = findViewById(R.id.rv_time_second); - mHourView = findViewById(R.id.lv_time_hour); - mMinuteView = findViewById(R.id.lv_time_minute); - mSecondView = findViewById(R.id.lv_time_second); + mHourAdapter = new PickerAdapter(context); + mMinuteAdapter = new PickerAdapter(context); + mSecondAdapter = new PickerAdapter(context); // 生产小时 ArrayList hourData = new ArrayList<>(24); @@ -71,43 +74,50 @@ public Builder(FragmentActivity activity) { secondData.add((i < 10 ? "0" : "") + i + " " + getString(R.string.common_second)); } - mHourView.setData(hourData); - mMinuteView.setData(minuteData); - mSecondView.setData(secondData); + mHourAdapter.setData(hourData); + mMinuteAdapter.setData(minuteData); + mSecondAdapter.setData(secondData); + + mHourManager = new PickerLayoutManager.Builder(context) + .build(); + mMinuteManager = new PickerLayoutManager.Builder(context) + .build(); + mSecondManager = new PickerLayoutManager.Builder(context) + .build(); + + mHourView.setLayoutManager(mHourManager); + mMinuteView.setLayoutManager(mMinuteManager); + mSecondView.setLayoutManager(mSecondManager); - mCancelView.setOnClickListener(this); - mConfirmView.setOnClickListener(this); + mHourView.setAdapter(mHourAdapter); + mMinuteView.setAdapter(mMinuteAdapter); + mSecondView.setAdapter(mSecondAdapter); Calendar calendar = Calendar.getInstance(); setHour(calendar.get(Calendar.HOUR_OF_DAY)); setMinute(calendar.get(Calendar.MINUTE)); setSecond(calendar.get(Calendar.SECOND)); - } - - public Builder setTitle(@StringRes int id) { - return setTitle(getString(id)); - } - public Builder setTitle(CharSequence text) { - mTitleView.setText(text); - return this; - } - - public Builder setConfirm(@StringRes int id) { - return setConfirm(getString(id)); - } - public Builder setConfirm(CharSequence text) { - mConfirmView.setText(text); - mLineView.setVisibility((text == null || "".equals(text.toString())) ? View.GONE : View.VISIBLE); - return this; + postDelayed(this, 1000); } - public Builder setCancel(@StringRes int id) { - return setCancel(getString(id)); - } - public Builder setCancel(CharSequence text) { - mCancelView.setText(text); - return this; + @Override + public void run() { + if (mHourView.getScrollState() == RecyclerView.SCROLL_STATE_IDLE && + mMinuteView.getScrollState() == RecyclerView.SCROLL_STATE_IDLE && + mSecondView.getScrollState() == RecyclerView.SCROLL_STATE_IDLE) { + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR_OF_DAY, mHourManager.getPickedPosition()); + calendar.set(Calendar.MINUTE, mMinuteManager.getPickedPosition()); + calendar.set(Calendar.SECOND, mSecondManager.getPickedPosition()); + if (System.currentTimeMillis() - calendar.getTimeInMillis() < 3000) { + calendar = Calendar.getInstance(); + setHour(calendar.get(Calendar.HOUR_OF_DAY)); + setMinute(calendar.get(Calendar.MINUTE)); + setSecond(calendar.get(Calendar.SECOND)); + postDelayed(this, 1000); + } + } } public Builder setListener(OnListener listener) { @@ -123,11 +133,6 @@ public Builder setIgnoreSecond() { return this; } - public Builder setAutoDismiss(boolean dismiss) { - mAutoDismiss = dismiss; - return this; - } - public Builder setTime(String time) { // 102030 if (time.matches("\\d{6}")) { @@ -151,10 +156,10 @@ public Builder setHour(int hour) { int index = hour; if (index < 0 || hour == 24) { index = 0; - } else if (index > mHourView.getSize() - 1) { - index = mHourView.getSize() - 1; + } else if (index > mHourAdapter.getItemCount() - 1) { + index = mHourAdapter.getItemCount() - 1; } - mHourView.setInitPosition(index); + mHourView.scrollToPosition(index); return this; } @@ -166,10 +171,10 @@ public Builder setMinute(int minute) { int index = minute; if (index < 0) { index = 0; - } else if (index > mMinuteView.getSize() - 1) { - index = mMinuteView.getSize() - 1; + } else if (index > mMinuteAdapter.getItemCount() - 1) { + index = mMinuteAdapter.getItemCount() - 1; } - mMinuteView.setInitPosition(index); + mMinuteView.scrollToPosition(index); return this; } @@ -181,29 +186,59 @@ public Builder setSecond(int second) { int index = second; if (index < 0) { index = 0; - } else if (index > mSecondView.getSize() - 1) { - index = mSecondView.getSize() - 1; + } else if (index > mSecondAdapter.getItemCount() - 1) { + index = mSecondAdapter.getItemCount() - 1; } - mSecondView.setInitPosition(index); + mSecondView.scrollToPosition(index); return this; } - /** - * {@link View.OnClickListener} - */ - + @SingleClick @Override public void onClick(View v) { - if (mAutoDismiss) { - dismiss(); + switch (v.getId()) { + case R.id.tv_ui_confirm: + autoDismiss(); + if (mListener != null) { + mListener.onSelected(getDialog(), mHourManager.getPickedPosition(), mMinuteManager.getPickedPosition(), mSecondManager.getPickedPosition()); + } + break; + case R.id.tv_ui_cancel: + autoDismiss(); + if (mListener != null) { + mListener.onCancel(getDialog()); + } + break; + default: + break; } + } + } - if (mListener != null) { - if (v == mConfirmView) { - mListener.onSelected(getDialog(), mHourView.getSelectedItem(), mMinuteView.getSelectedItem(), mSecondView.getSelectedItem()); - } else if (v == mCancelView) { - mListener.onCancel(getDialog()); - } + private static final class PickerAdapter extends MyAdapter { + + private PickerAdapter(Context context) { + super(context); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(); + } + + final class ViewHolder extends MyAdapter.ViewHolder { + + private final TextView mPickerView; + + ViewHolder() { + super(R.layout.item_picker); + mPickerView = (TextView) findViewById(R.id.tv_picker_name); + } + + @Override + public void onBindView(int position) { + mPickerView.setText(getItem(position)); } } } @@ -213,7 +248,7 @@ public interface OnListener { /** * 选择完时间后回调 * - * @param hour 小时 + * @param hour 时钟 * @param minute 分钟 * @param second 秒钟 */ @@ -222,6 +257,6 @@ public interface OnListener { /** * 点击取消时回调 */ - void onCancel(BaseDialog dialog); + default void onCancel(BaseDialog dialog) {} } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/dialog/ToastDialog.java b/app/src/main/java/com/hjq/demo/ui/dialog/ToastDialog.java index 379d5ab4..4b309f3f 100644 --- a/app/src/main/java/com/hjq/demo/ui/dialog/ToastDialog.java +++ b/app/src/main/java/com/hjq/demo/ui/dialog/ToastDialog.java @@ -1,14 +1,14 @@ package com.hjq.demo.ui.dialog; +import android.content.Context; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.StringRes; -import androidx.fragment.app.FragmentActivity; import com.hjq.base.BaseDialog; +import com.hjq.base.action.AnimAction; import com.hjq.demo.R; -import com.hjq.demo.common.MyDialogFragment; /** * author : Android 轮子哥 @@ -19,7 +19,7 @@ public final class ToastDialog { public static final class Builder - extends MyDialogFragment.Builder + extends BaseDialog.Builder implements Runnable, BaseDialog.OnShowListener { private final TextView mMessageView; @@ -28,10 +28,10 @@ public static final class Builder private Type mType = Type.WARN; private int mDuration = 2000; - public Builder(FragmentActivity activity) { - super(activity); + public Builder(Context context) { + super(context); setContentView(R.layout.dialog_toast); - setAnimStyle(BaseDialog.AnimStyle.TOAST); + setAnimStyle(AnimAction.TOAST); setBackgroundDimEnabled(false); setCancelable(false); @@ -94,10 +94,7 @@ public void onShow(BaseDialog dialog) { @Override public void run() { - if (getDialogFragment() != null && - getDialogFragment().isAdded() && - getDialog() != null && - getDialog().isShowing()) { + if (isShowing()) { dismiss(); } } diff --git a/app/src/main/java/com/hjq/demo/ui/dialog/UIDialog.java b/app/src/main/java/com/hjq/demo/ui/dialog/UIDialog.java new file mode 100644 index 00000000..14988e1d --- /dev/null +++ b/app/src/main/java/com/hjq/demo/ui/dialog/UIDialog.java @@ -0,0 +1,100 @@ +package com.hjq.demo.ui.dialog; + +import android.content.Context; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.LayoutRes; +import androidx.annotation.StringRes; + +import com.hjq.base.BaseDialog; +import com.hjq.base.action.AnimAction; +import com.hjq.demo.R; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/09/21 + * desc : 项目通用 Dialog 布局封装 + */ +public final class UIDialog { + + @SuppressWarnings("unchecked") + public static class Builder + extends BaseDialog.Builder { + + private boolean mAutoDismiss = true; + + private final ViewGroup mContainerLayout; + private final TextView mTitleView; + + private final TextView mCancelView; + private final View mLineView; + private final TextView mConfirmView; + + public Builder(Context context) { + super(context); + + setContentView(R.layout.dialog_ui); + setAnimStyle(AnimAction.IOS); + setGravity(Gravity.CENTER); + + mContainerLayout = findViewById(R.id.ll_ui_container); + + mTitleView = findViewById(R.id.tv_ui_title); + mCancelView = findViewById(R.id.tv_ui_cancel); + mLineView = findViewById(R.id.v_ui_line); + mConfirmView = findViewById(R.id.tv_ui_confirm); + + setOnClickListener(R.id.tv_ui_cancel, R.id.tv_ui_confirm); + } + + public B setCustomView(@LayoutRes int id) { + return setCustomView(LayoutInflater.from(getContext()).inflate(id, mContainerLayout, false)); + } + + public B setCustomView(View view) { + mContainerLayout.addView(view, 1); + return (B) this; + } + + public B setTitle(@StringRes int id) { + return setTitle(getString(id)); + } + public B setTitle(CharSequence text) { + mTitleView.setText(text); + return (B) this; + } + + public B setCancel(@StringRes int id) { + return setCancel(getString(id)); + } + public B setCancel(CharSequence text) { + mCancelView.setText(text); + mLineView.setVisibility((text == null || "".equals(text.toString())) ? View.GONE : View.VISIBLE); + return (B) this; + } + + public B setConfirm(@StringRes int id) { + return setConfirm(getString(id)); + } + public B setConfirm(CharSequence text) { + mConfirmView.setText(text); + return (B) this; + } + + public B setAutoDismiss(boolean dismiss) { + mAutoDismiss = dismiss; + return (B) this; + } + + public void autoDismiss() { + if (mAutoDismiss) { + dismiss(); + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/dialog/UpdateDialog.java b/app/src/main/java/com/hjq/demo/ui/dialog/UpdateDialog.java index 24c3f977..c15e6368 100644 --- a/app/src/main/java/com/hjq/demo/ui/dialog/UpdateDialog.java +++ b/app/src/main/java/com/hjq/demo/ui/dialog/UpdateDialog.java @@ -1,34 +1,31 @@ package com.hjq.demo.ui.dialog; -import android.app.DownloadManager; import android.content.Context; import android.content.Intent; -import android.database.ContentObserver; -import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Environment; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.text.format.Formatter; import android.view.View; -import android.view.ViewGroup; +import android.widget.ProgressBar; import android.widget.TextView; import androidx.core.content.FileProvider; -import androidx.fragment.app.FragmentActivity; import com.hjq.base.BaseDialog; +import com.hjq.base.action.AnimAction; import com.hjq.demo.R; -import com.hjq.demo.common.MyDialogFragment; -import com.hjq.demo.widget.NumberProgressBar; -import com.hjq.permissions.OnPermission; +import com.hjq.demo.aop.Permissions; +import com.hjq.demo.aop.SingleClick; +import com.hjq.demo.other.AppConfig; +import com.hjq.http.EasyHttp; +import com.hjq.http.listener.OnDownloadListener; +import com.hjq.http.model.DownloadInfo; +import com.hjq.http.model.HttpMethod; import com.hjq.permissions.Permission; -import com.hjq.permissions.XXPermissions; import java.io.File; -import java.util.List; + +import okhttp3.Call; /** * author : Android 轮子哥 @@ -39,46 +36,41 @@ public final class UpdateDialog { public static final class Builder - extends MyDialogFragment.Builder - implements OnDownloadListener, OnPermission, - View.OnClickListener { + extends BaseDialog.Builder { - private TextView mNameView; - private TextView mSizeView; - private TextView mContentView; - private NumberProgressBar mProgressView; + private final TextView mNameView; + private final TextView mContentView; + private final ProgressBar mProgressView; - private TextView mUpdateView; - private ViewGroup mCancelLayout; - private View mCloseView; + private final TextView mUpdateView; + private final TextView mCloseView; + /** Apk 文件 */ + private File mApkFile; /** 下载地址 */ private String mDownloadUrl; + /** 文件 MD5 */ + private String mFileMD5; + /** 当前是否下载中 */ + private boolean mDownloading; + /** 当前是否下载完毕 */ + private boolean mDownloadComplete; - /** 当前下载状态 */ - private int mDownloadStatus = -1; - - /** 下载处理对象 */ - private DownloadHandler mDownloadHandler; - - public Builder(FragmentActivity activity) { - super(activity); + public Builder(Context context) { + super(context); setContentView(R.layout.dialog_update); - setAnimStyle(BaseDialog.AnimStyle.BOTTOM); + setAnimStyle(AnimAction.BOTTOM); setCancelable(false); mNameView = findViewById(R.id.tv_update_name); - mSizeView = findViewById(R.id.tv_update_size); mContentView = findViewById(R.id.tv_update_content); mProgressView = findViewById(R.id.pb_update_progress); mUpdateView = findViewById(R.id.tv_update_update); - mCancelLayout = findViewById(R.id.ll_update_cancel); - mCloseView = findViewById(R.id.iv_update_close); + mCloseView = findViewById(R.id.tv_update_close); - mUpdateView.setOnClickListener(this); - mCloseView.setOnClickListener(this); + setOnClickListener(R.id.tv_update_update, R.id.tv_update_close); } /** @@ -89,18 +81,6 @@ public Builder setVersionName(CharSequence name) { return this; } - /** - * 设置文件大小 - */ - public Builder setFileSize(long size) { - return setFileSize(Formatter.formatFileSize(getContext(), size)); - } - - public Builder setFileSize(CharSequence text) { - mSizeView.setText(text); - return this; - } - /** * 设置更新日志 */ @@ -114,10 +94,8 @@ public Builder setUpdateLog(CharSequence text) { * 设置强制更新 */ public Builder setForceUpdate(boolean force) { - mCancelLayout.setVisibility(force ? View.GONE : View.VISIBLE); - if (force) { - setCancelable(true); - } + mCloseView.setVisibility(force ? View.GONE : View.VISIBLE); + setCancelable(!force); return this; } @@ -130,309 +108,111 @@ public Builder setDownloadUrl(String url) { } /** - * {@link OnDownloadListener} + * 设置文件 md5 */ - - @Override - public void downloadProgressChange(int progress) { - mProgressView.setProgress(progress); - } - - @Override - public void downloadStateChange(int state) { - // 记录本次的下载状态 - mDownloadStatus = state; - - // 判断下载状态 - switch (state) { - // 下载中 - case DownloadManager.STATUS_RUNNING: - mUpdateView.setText(R.string.update_status_running); - // 显示进度条 - mProgressView.setVisibility(View.VISIBLE); - break; - // 下载成功 - case DownloadManager.STATUS_SUCCESSFUL: - mUpdateView.setText(R.string.update_status_successful); - // 隐藏进度条 - mProgressView.setVisibility(View.GONE); - // 安装 Apk - mDownloadHandler.openDownloadFile(); - break; - // 下载失败 - case DownloadManager.STATUS_FAILED: - mUpdateView.setText(R.string.update_status_failed); - // 删除下载的文件 - mDownloadHandler.deleteDownloadFile(); - break; - // 下载暂停 - case DownloadManager.STATUS_PAUSED: - mUpdateView.setText(R.string.update_status_paused); - break; - // 等待下载 - case DownloadManager.STATUS_PENDING: - mUpdateView.setText(R.string.update_status_pending); - break; - default: - break; - } + public Builder setFileMD5(String md5) { + mFileMD5 = md5; + return this; } + @SingleClick @Override public void onClick(View v) { if (v == mCloseView) { dismiss(); } else if (v == mUpdateView) { // 判断下载状态 - switch (mDownloadStatus) { - // 没有任何状态 - case -1: - // 下载失败 - case DownloadManager.STATUS_FAILED: - // 重新下载 - requestPermission(); - break; - // 下载成功 - case DownloadManager.STATUS_SUCCESSFUL: - // 安装 Apk - mDownloadHandler.openDownloadFile(); - break; - default: - break; + if (mDownloadComplete) { + // 下载完毕,安装 Apk + installApk(); + } else if (!mDownloading) { + // 没有下载,开启下载 + downloadApk(); } } } /** - * 请求权限 - */ - private void requestPermission() { - XXPermissions.with(getActivity()) - // 可设置被拒绝后继续申请,直到用户授权或者永久拒绝 - .constantRequest() - // 安装包权限 - .permission(Permission.REQUEST_INSTALL_PACKAGES) - // 存储权限 - .permission(Permission.Group.STORAGE) - .request(this); - } - - /** - * {@link OnPermission} - */ - @Override - public void hasPermission(List granted, boolean isAll) { - if (isAll) { - - mDownloadHandler = new DownloadHandler(getActivity()); - mDownloadHandler.setDownloadListener(this); - if (!mDownloadHandler.createDownload(mDownloadUrl, getString(R.string.app_name) + - " " + mNameView.getText().toString() + ".apk", null)) { - mUpdateView.setText(R.string.update_download_fail); - } else { - // 设置对话框不能被取消 - setCancelable(false); - // 隐藏取消按钮 - mCancelLayout.setVisibility(View.GONE); - } - } - } - - @Override - public void noPermission(List denied, boolean quick) { - toast(R.string.update_permission_hint); - } - } - - private static final class DownloadHandler extends Handler { - - private final Context mContext; - - /** 下载管理器对象 */ - private final DownloadManager mDownloadManager; - /** 下载内容观察者 */ - private DownloadObserver mDownloadObserver; - - /** 下载文件 id */ - private long mDownloadId; - - /** 下载监听 */ - private OnDownloadListener mListener; - - /** 下载的文件 */ - private File mDownloadFile; - - private DownloadHandler(Context context) { - super(Looper.getMainLooper()); - mContext = context; - mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); - } - - private void setDownloadListener(OnDownloadListener listener) { - mListener = listener; - } - - @Override - public void handleMessage(Message msg) { - if (mListener == null) { - return; - } - - // 判断下载状态 - switch (msg.what) { - // 下载中 - case DownloadManager.STATUS_RUNNING: - // 计算下载百分比,这里踩了两个坑 - // 当 apk 文件很大的时候:下载字节数 * 100 会超过 int 最大值,计算结果会变成负数 - // 还有需要注意的是,int 除以 int 等于 int,这里的下载字节数除以总字节数应该要 double 类型的 - int progress = (int) (((double) msg.arg2 / msg.arg1) * 100); - mListener.downloadProgressChange(progress); - break; - // 下载成功 - case DownloadManager.STATUS_SUCCESSFUL: - // 下载失败 - case DownloadManager.STATUS_FAILED: - // 移除内容观察者 - if (mDownloadObserver != null) { - mContext.getContentResolver().unregisterContentObserver(mDownloadObserver); - } - break; - default: - break; - } - - mListener.downloadStateChange(msg.what); - } - - /** - * 创建下载任务 - * - * @param downloadUrl 下载地址 - * @param fileName 文件命名 - * @param notificationTitle 通知栏标题 - * @return 下载 id + * 下载 Apk */ - @SuppressWarnings("ResultOfMethodCallIgnored") - private boolean createDownload(String downloadUrl, String fileName, String notificationTitle) { - if (fileName == null) { - throw new IllegalArgumentException("The filename cannot be empty"); - } - - // 记录下载的文件 - mDownloadFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName); - // 如果这个文件已经下载过,就先删除这个文件 - if (mDownloadFile.exists()) { - mDownloadFile.delete(); - } - - try { - DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downloadUrl)); - request.allowScanningByMediaScanner(); - //设置WIFI下进行更新 - //request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI); - request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); - request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName); - - if (notificationTitle != null) { - request.setTitle(notificationTitle); - } - - request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); - - mDownloadId = mDownloadManager.enqueue(request); + @Permissions({Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE}) + private void downloadApk() { + // 创建要下载的文件对象 + mApkFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), getString(R.string.app_name) + "_v" + mNameView.getText().toString() + ".apk"); + // 设置对话框不能被取消 + setCancelable(false); - mDownloadObserver = new DownloadObserver(this, mDownloadManager, new DownloadManager.Query().setFilterById(mDownloadId)); - // 添加内容观察者 - mContext.getContentResolver().registerContentObserver(Uri.parse("content://downloads/"), true, mDownloadObserver); - return true; - } catch (Exception e) { - return false; - } + EasyHttp.download(getActivity()) + .method(HttpMethod.GET) + .file(mApkFile) + .url(mDownloadUrl) + .md5(mFileMD5) + .listener(new OnDownloadListener() { + + @Override + public void onStart(Call call) { + // 标记为下载中 + mDownloading = true; + // 标记成未下载完成 + mDownloadComplete = false; + // 后台更新 + mCloseView.setText(R.string.update_background); + // 显示进度条 + mProgressView.setVisibility(View.VISIBLE); + } + + @Override + public void onProgress(DownloadInfo info) { + mUpdateView.setText(String.format(getString(R.string.update_status_running), info.getDownloadProgress())); + mProgressView.setProgress(info.getDownloadProgress()); + } + + @Override + public void onComplete(DownloadInfo info) { + mCloseView.setText(R.string.update_no); + mUpdateView.setText(R.string.update_status_successful); + // 标记成下载完成 + mDownloadComplete = true; + // 安装 Apk + installApk(); + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + @Override + public void onError(DownloadInfo info, Exception e) { + mUpdateView.setText(R.string.update_status_failed); + // 删除下载的文件 + info.getFile().delete(); + } + + @Override + public void onEnd(Call call) { + // 隐藏进度条 + mProgressView.setVisibility(View.GONE); + // 标记当前不是下载 + mDownloading = false; + } + }).start(); } /** - * 打开下载的文件 + * 安装 Apk */ - private void openDownloadFile() { - // 这里需要特别说明的是,这个 API 其实不是打开文件的,我也不知道干什么用的 - // 测试前必须要加权限,否则会崩溃: - // mDownloadManager.openDownloadedFile(mDownloadId); + @Permissions({Permission.REQUEST_INSTALL_PACKAGES}) + private void installApk() { Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); Uri uri; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - uri = FileProvider.getUriForFile(mContext, mContext.getPackageName() + ".provider", mDownloadFile); + uri = FileProvider.getUriForFile(getContext(), AppConfig.getPackageName() + ".provider", mApkFile); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); } else { - uri = Uri.fromFile(mDownloadFile); + uri = Uri.fromFile(mApkFile); } intent.setDataAndType(uri, "application/vnd.android.package-archive"); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - mContext.startActivity(intent); + getContext().startActivity(intent); } - - /** - * 删除下载的文件 - */ - void deleteDownloadFile() { - mDownloadManager.remove(mDownloadId); - } - } - - private static class DownloadObserver extends ContentObserver { - - private final Handler mHandler; - private final DownloadManager mDownloadManager; - private final DownloadManager.Query mQuery; - - DownloadObserver(Handler handler, DownloadManager manager, DownloadManager.Query query) { - super(handler); - mHandler = handler; - mDownloadManager = manager; - mQuery = query; - } - - /** - * 每当 /data/data/com.android.providers.download/database/database.db 变化后就会触发onChange方法 - * - * @param selfChange 是否是当前应用自己操作了数据库 - */ - @Override - public void onChange(boolean selfChange) { - // 查询数据库 - Cursor cursor = mDownloadManager.query(mQuery); - // 游标定位到第一个,因为 Cursor 总数只有一个 - cursor.moveToFirst(); - - // 总需下载的字节数 - int totalBytes = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); - // 已经下载的字节数 - int downloadBytes = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); - // 下载状态 - int downloadStatus = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS)); - - // 关闭游标 - cursor.close(); - - // 发送更新消息 - Message msg = mHandler.obtainMessage(); - msg.arg1 = totalBytes; - msg.arg2 = downloadBytes; - msg.what = downloadStatus; - mHandler.sendMessage(msg); - } - } - - private interface OnDownloadListener { - /** - * 下载进度改变 - */ - void downloadProgressChange(int progress); - - /** - * 下载状态改变 - */ - void downloadStateChange(int state); } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/dialog/WaitDialog.java b/app/src/main/java/com/hjq/demo/ui/dialog/WaitDialog.java index d4b70205..87a135f5 100644 --- a/app/src/main/java/com/hjq/demo/ui/dialog/WaitDialog.java +++ b/app/src/main/java/com/hjq/demo/ui/dialog/WaitDialog.java @@ -1,14 +1,14 @@ package com.hjq.demo.ui.dialog; +import android.content.Context; import android.view.View; import android.widget.TextView; import androidx.annotation.StringRes; -import androidx.fragment.app.FragmentActivity; import com.hjq.base.BaseDialog; +import com.hjq.base.action.AnimAction; import com.hjq.demo.R; -import com.hjq.demo.common.MyDialogFragment; /** * author : Android 轮子哥 @@ -19,14 +19,14 @@ public final class WaitDialog { public static final class Builder - extends MyDialogFragment.Builder { + extends BaseDialog.Builder { private final TextView mMessageView; - public Builder(FragmentActivity activity) { - super(activity); + public Builder(Context context) { + super(context); setContentView(R.layout.dialog_wait); - setAnimStyle(BaseDialog.AnimStyle.TOAST); + setAnimStyle(AnimAction.TOAST); setBackgroundDimEnabled(false); setCancelable(false); diff --git a/app/src/main/java/com/hjq/demo/ui/fragment/CopyFragment.java b/app/src/main/java/com/hjq/demo/ui/fragment/CopyFragment.java index 989849f1..cbe8b808 100644 --- a/app/src/main/java/com/hjq/demo/ui/fragment/CopyFragment.java +++ b/app/src/main/java/com/hjq/demo/ui/fragment/CopyFragment.java @@ -1,7 +1,7 @@ package com.hjq.demo.ui.fragment; import com.hjq.demo.R; -import com.hjq.demo.common.MyLazyFragment; +import com.hjq.demo.common.MyFragment; import com.hjq.demo.ui.activity.CopyActivity; /** @@ -10,7 +10,7 @@ * time : 2018/10/18 * desc : 可进行拷贝的副本 */ -public final class CopyFragment extends MyLazyFragment { +public final class CopyFragment extends MyFragment { public static CopyFragment newInstance() { return new CopyFragment(); diff --git a/app/src/main/java/com/hjq/demo/ui/fragment/TestFragmentA.java b/app/src/main/java/com/hjq/demo/ui/fragment/TestFragmentA.java index 0c0ae474..2f9d32cf 100644 --- a/app/src/main/java/com/hjq/demo/ui/fragment/TestFragmentA.java +++ b/app/src/main/java/com/hjq/demo/ui/fragment/TestFragmentA.java @@ -1,14 +1,14 @@ package com.hjq.demo.ui.fragment; +import android.widget.ImageView; import android.widget.TextView; import androidx.appcompat.widget.Toolbar; import androidx.core.content.ContextCompat; -import com.google.android.material.appbar.AppBarLayout; import com.gyf.immersionbar.ImmersionBar; import com.hjq.demo.R; -import com.hjq.demo.common.MyLazyFragment; +import com.hjq.demo.common.MyFragment; import com.hjq.demo.ui.activity.HomeActivity; import com.hjq.demo.widget.XCollapsingToolbarLayout; @@ -20,11 +20,9 @@ * time : 2018/10/18 * desc : 项目炫酷效果示例 */ -public final class TestFragmentA extends MyLazyFragment +public final class TestFragmentA extends MyFragment implements XCollapsingToolbarLayout.OnScrimsListener { - @BindView(R.id.abl_test_bar) - AppBarLayout mAppBarLayout; @BindView(R.id.ctl_test_bar) XCollapsingToolbarLayout mCollapsingToolbarLayout; @BindView(R.id.t_test_title) @@ -32,8 +30,10 @@ public final class TestFragmentA extends MyLazyFragment @BindView(R.id.tv_test_address) TextView mAddressView; - @BindView(R.id.tv_test_search) - TextView mSearchView; + @BindView(R.id.tv_test_hint) + TextView mHintView; + @BindView(R.id.iv_test_search) + ImageView mSearchView; public static TestFragmentA newInstance() { return new TestFragmentA(); @@ -46,7 +46,7 @@ protected int getLayoutId() { @Override protected void initView() { - // 给这个ToolBar设置顶部内边距,才能和TitleBar进行对齐 + // 给这个 ToolBar 设置顶部内边距,才能和 TitleBar 进行对齐 ImmersionBar.setTitleBar(getAttachActivity(), mToolbar); //设置渐变监听 @@ -78,11 +78,15 @@ public boolean statusBarDarkFont() { public void onScrimsStateChange(XCollapsingToolbarLayout layout, boolean shown) { if (shown) { mAddressView.setTextColor(ContextCompat.getColor(getAttachActivity(), R.color.black)); - mSearchView.setBackgroundResource(R.drawable.bg_home_search_bar_gray); + mHintView.setBackgroundResource(R.drawable.bg_home_search_bar_gray); + mHintView.setTextColor(ContextCompat.getColor(getAttachActivity(), R.color.black60)); + mSearchView.setImageResource(R.drawable.ic_search_black); getStatusBarConfig().statusBarDarkFont(true).init(); } else { mAddressView.setTextColor(ContextCompat.getColor(getAttachActivity(), R.color.white)); - mSearchView.setBackgroundResource(R.drawable.bg_home_search_bar_transparent); + mHintView.setBackgroundResource(R.drawable.bg_home_search_bar_transparent); + mHintView.setTextColor(ContextCompat.getColor(getAttachActivity(), R.color.white60)); + mSearchView.setImageResource(R.drawable.ic_search_white); getStatusBarConfig().statusBarDarkFont(false).init(); } } diff --git a/app/src/main/java/com/hjq/demo/ui/fragment/TestFragmentB.java b/app/src/main/java/com/hjq/demo/ui/fragment/TestFragmentB.java index a55838e5..f15c7db1 100644 --- a/app/src/main/java/com/hjq/demo/ui/fragment/TestFragmentB.java +++ b/app/src/main/java/com/hjq/demo/ui/fragment/TestFragmentB.java @@ -1,15 +1,17 @@ package com.hjq.demo.ui.fragment; import android.view.View; +import android.widget.ImageView; import com.hjq.demo.R; -import com.hjq.demo.common.MyLazyFragment; +import com.hjq.demo.aop.SingleClick; +import com.hjq.demo.common.MyFragment; +import com.hjq.demo.http.glide.GlideApp; import com.hjq.demo.ui.activity.HomeActivity; import com.hjq.widget.view.CountdownView; import com.hjq.widget.view.SwitchButton; import butterknife.BindView; -import butterknife.OnClick; /** * author : Android 轮子哥 @@ -17,12 +19,14 @@ * time : 2018/10/18 * desc : 项目自定义控件展示 */ -public final class TestFragmentB extends MyLazyFragment +public final class TestFragmentB extends MyFragment implements SwitchButton.OnCheckedChangeListener { + @BindView(R.id.iv_test_circle) + ImageView mCircleView; + @BindView(R.id.sb_test_switch) SwitchButton mSwitchButton; - @BindView(R.id.cv_test_countdown) CountdownView mCountdownView; @@ -38,21 +42,24 @@ protected int getLayoutId() { @Override protected void initView() { mSwitchButton.setOnCheckedChangeListener(this); + + setOnClickListener(R.id.cv_test_countdown); } @Override protected void initData() { - + GlideApp.with(this) + .load(R.drawable.bg_launcher) + .circleCrop() + .into(mCircleView); } - @OnClick(R.id.cv_test_countdown) + @SingleClick + @Override public void onClick(View v) { - switch (v.getId()) { - case R.id.cv_test_countdown: - toast(R.string.common_code_send_hint); - break; - default: - break; + if (v.getId() == R.id.cv_test_countdown) { + toast(R.string.common_code_send_hint); + mCountdownView.start(); } } diff --git a/app/src/main/java/com/hjq/demo/ui/fragment/TestFragmentC.java b/app/src/main/java/com/hjq/demo/ui/fragment/TestFragmentC.java index dc87e6bb..23822050 100644 --- a/app/src/main/java/com/hjq/demo/ui/fragment/TestFragmentC.java +++ b/app/src/main/java/com/hjq/demo/ui/fragment/TestFragmentC.java @@ -4,11 +4,13 @@ import android.view.View; import android.widget.ImageView; +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import com.hjq.demo.R; -import com.hjq.demo.common.MyLazyFragment; +import com.hjq.demo.aop.SingleClick; +import com.hjq.demo.common.MyFragment; +import com.hjq.demo.http.glide.GlideApp; import com.hjq.demo.ui.activity.HomeActivity; import com.hjq.demo.ui.activity.PhotoActivity; -import com.hjq.image.ImageLoader; import com.hjq.permissions.OnPermission; import com.hjq.permissions.Permission; import com.hjq.permissions.XXPermissions; @@ -16,7 +18,6 @@ import java.util.List; import butterknife.BindView; -import butterknife.OnClick; /** * author : Android 轮子哥 @@ -24,7 +25,7 @@ * time : 2018/10/18 * desc : 项目框架使用示例 */ -public final class TestFragmentC extends MyLazyFragment { +public final class TestFragmentC extends MyFragment { @BindView(R.id.iv_test_image) ImageView mImageView; @@ -40,7 +41,8 @@ protected int getLayoutId() { @Override protected void initView() { - + setOnClickListener(R.id.btn_test_image1, R.id.btn_test_image2, R.id.btn_test_image3, R.id.btn_test_image4, R.id.btn_test_toast, + R.id.btn_test_permission, R.id.btn_test_setting, R.id.btn_test_state_black, R.id.btn_test_state_white); } @Override @@ -54,37 +56,37 @@ public boolean isStatusBarEnabled() { return !super.isStatusBarEnabled(); } - @OnClick({R.id.btn_test_image1, R.id.btn_test_image2, R.id.btn_test_image3, R.id.btn_test_image4, - R.id.btn_test_toast, R.id.btn_test_permission, R.id.btn_test_state_black, R.id.btn_test_state_white}) + @SingleClick + @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_test_image1: mImageView.setVisibility(View.VISIBLE); - ImageLoader.with(this) + GlideApp.with(this) .load("https://www.baidu.com/img/bd_logo.png") .into(mImageView); break; case R.id.btn_test_image2: mImageView.setVisibility(View.VISIBLE); - ImageLoader.with(this) - .circle() + GlideApp.with(this) .load("https://www.baidu.com/img/bd_logo.png") + .circleCrop() .into(mImageView); break; case R.id.btn_test_image3: mImageView.setVisibility(View.VISIBLE); - ImageLoader.with(this) - .circle((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, this.getResources().getDisplayMetrics())) + GlideApp.with(this) .load("https://www.baidu.com/img/bd_logo.png") + .transform(new RoundedCorners((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, this.getResources().getDisplayMetrics()))) .into(mImageView); break; case R.id.btn_test_image4: PhotoActivity.start(getAttachActivity(), new PhotoActivity.OnPhotoSelectListener() { @Override - public void onSelect(List data) { + public void onSelected(List data) { mImageView.setVisibility(View.VISIBLE); - ImageLoader.with(getAttachActivity()) + GlideApp.with(getAttachActivity()) .load(data.get(0)) .into(mImageView); } @@ -129,11 +131,18 @@ public void noPermission(List denied, boolean quick) { } }); break; + case R.id.btn_test_setting: + XXPermissions.gotoPermissionSettings(getAttachActivity()); + break; case R.id.btn_test_state_black: - getAttachActivity().getStatusBarConfig().statusBarDarkFont(true).init(); + if (getAttachActivity().getStatusBarConfig() != null) { + getAttachActivity().getStatusBarConfig().statusBarDarkFont(true).init(); + } break; case R.id.btn_test_state_white: - getAttachActivity().getStatusBarConfig().statusBarDarkFont(false).init(); + if (getAttachActivity().getStatusBarConfig() != null) { + getAttachActivity().getStatusBarConfig().statusBarDarkFont(false).init(); + } break; default: break; diff --git a/app/src/main/java/com/hjq/demo/ui/fragment/TestFragmentD.java b/app/src/main/java/com/hjq/demo/ui/fragment/TestFragmentD.java index 5d7108a7..fd88a251 100644 --- a/app/src/main/java/com/hjq/demo/ui/fragment/TestFragmentD.java +++ b/app/src/main/java/com/hjq/demo/ui/fragment/TestFragmentD.java @@ -4,11 +4,13 @@ import android.net.Uri; import android.view.View; -import com.hjq.base.BaseDialog; import com.hjq.demo.R; -import com.hjq.demo.common.MyLazyFragment; +import com.hjq.demo.aop.SingleClick; +import com.hjq.demo.common.MyFragment; import com.hjq.demo.ui.activity.AboutActivity; +import com.hjq.demo.ui.activity.BrowserActivity; import com.hjq.demo.ui.activity.DialogActivity; +import com.hjq.demo.ui.activity.GuideActivity; import com.hjq.demo.ui.activity.HomeActivity; import com.hjq.demo.ui.activity.ImageActivity; import com.hjq.demo.ui.activity.LoginActivity; @@ -20,20 +22,18 @@ import com.hjq.demo.ui.activity.RegisterActivity; import com.hjq.demo.ui.activity.SettingActivity; import com.hjq.demo.ui.activity.StatusActivity; -import com.hjq.demo.ui.activity.WebActivity; import com.hjq.demo.ui.dialog.MessageDialog; +import com.tencent.bugly.crashreport.CrashReport; import java.util.ArrayList; -import butterknife.OnClick; - /** * author : Android 轮子哥 * github : https://github.com/getActivity/AndroidProject * time : 2018/10/18 * desc : 项目界面跳转示例 */ -public final class TestFragmentD extends MyLazyFragment { +public final class TestFragmentD extends MyFragment { public static TestFragmentD newInstance() { return new TestFragmentD(); @@ -46,7 +46,9 @@ protected int getLayoutId() { @Override protected void initView() { - + setOnClickListener(R.id.btn_test_dialog, R.id.btn_test_hint, R.id.btn_test_login, R.id.btn_test_register, R.id.btn_test_forget, + R.id.btn_test_reset,R.id.btn_test_verify, R.id.btn_test_change, R.id.btn_test_personal, R.id.btn_test_setting, + R.id.btn_test_about, R.id.btn_test_guide, R.id.btn_test_browser, R.id.btn_test_image, R.id.btn_test_crash, R.id.btn_test_pay); } @Override @@ -54,9 +56,8 @@ protected void initData() { } - @OnClick({R.id.btn_test_dialog, R.id.btn_test_hint, R.id.btn_test_login, R.id.btn_test_register, R.id.btn_test_forget, - R.id.btn_test_reset,R.id.btn_test_verify, R.id.btn_test_change, R.id.btn_test_personal, R.id.btn_test_setting, - R.id.btn_test_about, R.id.btn_test_browser, R.id.btn_test_image, R.id.btn_test_crash, R.id.btn_test_pay}) + @SingleClick + @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_test_dialog: @@ -92,8 +93,11 @@ public void onClick(View v) { case R.id.btn_test_about: startActivity(AboutActivity.class); break; + case R.id.btn_test_guide: + startActivity(GuideActivity.class); + break; case R.id.btn_test_browser: - WebActivity.start(getAttachActivity(), "https://github.com/getActivity/"); + BrowserActivity.start(getAttachActivity(), "https://github.com/getActivity"); break; case R.id.btn_test_image: ArrayList images = new ArrayList<>(); @@ -102,30 +106,25 @@ public void onClick(View v) { ImageActivity.start(getAttachActivity(), images, images.size() - 1); break; case R.id.btn_test_crash: + // 关闭 Bugly 异常捕捉 + CrashReport.closeBugly(); throw new IllegalStateException("are you ok?"); case R.id.btn_test_pay: new MessageDialog.Builder(getAttachActivity()) .setTitle("捐赠") - .setMessage("如果您觉得这个开源项目很棒,希望它能更好地坚持开发下去,可否愿意花一点点钱(推荐 10.24 元)作为对于开发者的激励") + .setMessage("如果你觉得这个开源项目很棒,希望它能更好地坚持开发下去,可否愿意花一点点钱(推荐 10.24 元)作为对于开发者的激励") .setConfirm("支付宝") .setCancel(null) //.setAutoDismiss(false) - .setListener(new MessageDialog.OnListener() { - - @Override - public void onConfirm(BaseDialog dialog) { - try { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("alipays://platformapi/startapp?saId=10000007&clientVersion=3.7.0.0718&qrcode=https%3A%2F%2Fqr.alipay.com%2FFKX04202G4K6AVCF5GIY66%3F_s%3Dweb-other")); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); - toast("这个开源项目因为您的支持而能够不断更新、完善,非常感谢您的支持"); - } catch (Exception e) { - toast("打开支付宝失败"); - } + .setListener(dialog -> { + try { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("alipays://platformapi/startapp?saId=10000007&clientVersion=3.7.0.0718&qrcode=https%3A%2F%2Fqr.alipay.com%2FFKX04202G4K6AVCF5GIY66%3F_s%3Dweb-other")); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + toast("这个开源项目因为你的支持而能够不断更新、完善,非常感谢你的支持"); + } catch (Exception e) { + toast("打开支付宝失败"); } - - @Override - public void onCancel(BaseDialog dialog) {} }) .show(); break; diff --git a/app/src/main/java/com/hjq/demo/ui/pager/GuidePagerAdapter.java b/app/src/main/java/com/hjq/demo/ui/pager/GuidePagerAdapter.java new file mode 100644 index 00000000..899ee9f9 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/ui/pager/GuidePagerAdapter.java @@ -0,0 +1,48 @@ +package com.hjq.demo.ui.pager; + +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.appcompat.widget.AppCompatImageView; +import androidx.viewpager.widget.PagerAdapter; + +import com.hjq.demo.R; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/09/21 + * desc : 引导页适配器 + */ +public final class GuidePagerAdapter extends PagerAdapter { + + private static final int[] DRAWABLES = {R.drawable.bg_guide_1, R.drawable.bg_guide_2, R.drawable.bg_guide_3}; + + @Override + public int getCount() { + return DRAWABLES.length; + } + + @Override + public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { + return view == object; + } + + @NonNull + @Override + public Object instantiateItem(@NonNull ViewGroup container, int position) { + AppCompatImageView imageView = new AppCompatImageView(container.getContext()); + imageView.setPaddingRelative(0, 0, 0, + (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, container.getContext().getResources().getDisplayMetrics())); + imageView.setImageResource(DRAWABLES[position]); + container.addView(imageView); + return imageView; + } + + @Override + public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { + container.removeView((View) object); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/adapter/ImagePagerAdapter.java b/app/src/main/java/com/hjq/demo/ui/pager/ImagePagerAdapter.java similarity index 90% rename from app/src/main/java/com/hjq/demo/ui/adapter/ImagePagerAdapter.java rename to app/src/main/java/com/hjq/demo/ui/pager/ImagePagerAdapter.java index 21c7dba8..11ae7ac2 100644 --- a/app/src/main/java/com/hjq/demo/ui/adapter/ImagePagerAdapter.java +++ b/app/src/main/java/com/hjq/demo/ui/pager/ImagePagerAdapter.java @@ -1,4 +1,4 @@ -package com.hjq.demo.ui.adapter; +package com.hjq.demo.ui.pager; import android.app.Activity; import android.view.View; @@ -8,7 +8,8 @@ import androidx.viewpager.widget.PagerAdapter; import com.github.chrisbanes.photoview.PhotoView; -import com.hjq.image.ImageLoader; +import com.hjq.demo.aop.SingleClick; +import com.hjq.demo.http.glide.GlideApp; import java.util.List; @@ -44,10 +45,11 @@ public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { public Object instantiateItem(@NonNull ViewGroup container, int position) { PhotoView view = new PhotoView(mActivity); view.setOnClickListener(this); - ImageLoader.with(container.getContext()) + + GlideApp.with(container.getContext()) .load(mData.get(position)) - .gif() .into(view); + container.addView(view); return view; } @@ -57,6 +59,7 @@ public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Obj container.removeView((View) object); } + @SingleClick @Override public void onClick(View v) { // 单击图片退出当前的 Activity diff --git a/app/src/main/java/com/hjq/demo/ui/popup/CopyPopup.java b/app/src/main/java/com/hjq/demo/ui/popup/CopyPopup.java new file mode 100644 index 00000000..2adabdef --- /dev/null +++ b/app/src/main/java/com/hjq/demo/ui/popup/CopyPopup.java @@ -0,0 +1,25 @@ +package com.hjq.demo.ui.popup; + +import android.content.Context; + +import com.hjq.base.BasePopupWindow; +import com.hjq.demo.R; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/10/18 + * desc : 可进行拷贝的副本 + */ +public final class CopyPopup { + + public static final class Builder + extends BasePopupWindow.Builder { + + public Builder(Context context) { + super(context); + + setContentView(R.layout.popup_copy); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/popup/MenuPopup.java b/app/src/main/java/com/hjq/demo/ui/popup/MenuPopup.java new file mode 100644 index 00000000..6bd2fc86 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/ui/popup/MenuPopup.java @@ -0,0 +1,151 @@ +package com.hjq.demo.ui.popup; + +import android.content.Context; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.hjq.base.BaseAdapter; +import com.hjq.base.BasePopupWindow; +import com.hjq.base.action.AnimAction; +import com.hjq.demo.R; +import com.hjq.demo.common.MyAdapter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/10/18 + * desc : 菜单 PopupWindow + */ +public final class MenuPopup { + + public static final class Builder + extends BasePopupWindow.Builder + implements BaseAdapter.OnItemClickListener { + + private OnListener mListener; + private boolean mAutoDismiss = true; + + private final MenuAdapter mAdapter; + + public Builder(Context context) { + super(context); + + setContentView(R.layout.popup_menu); + + RecyclerView recyclerView = findViewById(R.id.rv_menu_list); + mAdapter = new MenuAdapter(getContext()); + mAdapter.setOnItemClickListener(this); + recyclerView.setAdapter(mAdapter); + } + + @Override + public Builder setGravity(int gravity) { + switch (gravity) { + // 如果这个是在中间显示的 + case Gravity.CENTER: + case Gravity.CENTER_VERTICAL: + // 重新设置动画 + setAnimStyle(AnimAction.SCALE); + break; + default: + break; + } + return super.setGravity(gravity); + } + + public Builder setList(int... ids) { + List data = new ArrayList<>(ids.length); + for (int id : ids) { + data.add(getString(id)); + } + return setList(data); + } + + public Builder setList(String... data) { + return setList(Arrays.asList(data)); + } + + @SuppressWarnings("all") + public Builder setList(List data) { + mAdapter.setData(data); + return this; + } + + public Builder setAutoDismiss(boolean dismiss) { + mAutoDismiss = dismiss; + return this; + } + + public Builder setListener(OnListener listener) { + mListener = listener; + return this; + } + + /** + * {@link BaseAdapter.OnItemClickListener} + */ + @SuppressWarnings("all") + @Override + public void onItemClick(RecyclerView recyclerView, View itemView, int position) { + if (mAutoDismiss) { + dismiss(); + } + + if (mListener != null) { + mListener.onSelected(getPopupWindow(), position, mAdapter.getItem(position)); + } + } + } + + private static final class MenuAdapter extends MyAdapter { + + private MenuAdapter(Context context) { + super(context); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(); + } + + final class ViewHolder extends MyAdapter.ViewHolder { + + private final TextView mTextView; + + ViewHolder() { + super(new TextView(getContext())); + mTextView = (TextView) getItemView(); + mTextView.setTextColor(getColor(R.color.black50)); + mTextView.setTextSize(16); + mTextView.setPaddingRelative((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics()), + (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics()), + (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics()), + (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics())); + } + + @Override + public void onBindView(int position) { + mTextView.setText(getItem(position).toString()); + } + } + } + + public interface OnListener { + + /** + * 选择条目时回调 + */ + void onSelected(BasePopupWindow popupWindow, int position, T t); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/widget/BrowserView.java b/app/src/main/java/com/hjq/demo/widget/BrowserView.java new file mode 100644 index 00000000..1053c393 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/widget/BrowserView.java @@ -0,0 +1,346 @@ +package com.hjq.demo.widget; + +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.net.Uri; +import android.net.http.SslError; +import android.os.Build; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.JsPromptResult; +import android.webkit.JsResult; +import android.webkit.SslErrorHandler; +import android.webkit.WebChromeClient; +import android.webkit.WebResourceError; +import android.webkit.WebResourceRequest; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +import androidx.fragment.app.FragmentActivity; + +import com.hjq.base.BaseDialog; +import com.hjq.demo.ui.dialog.InputDialog; +import com.hjq.demo.ui.dialog.MessageDialog; +import com.hjq.demo.ui.dialog.ToastDialog; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/09/24 + * desc : 基于 WebView 封装 + */ +public final class BrowserView extends WebView { + + public BrowserView(Context context) { + this(context, null); + } + + public BrowserView(Context context, AttributeSet attrs) { + this(context, attrs, android.R.attr.webViewStyle); + } + + @SuppressLint("SetJavaScriptEnabled") + public BrowserView(Context context, AttributeSet attrs, int defStyleAttr) { + super(getFixedContext(context), attrs, defStyleAttr); + + WebSettings settings = getSettings(); + // 允许文件访问 + settings.setAllowFileAccess(true); + // 允许网页定位 + settings.setGeolocationEnabled(true); + // 允许保存密码 + //settings.setSavePassword(true); + // 开启 JavaScript + settings.setJavaScriptEnabled(true); + // 允许网页弹对话框 + settings.setJavaScriptCanOpenWindowsAutomatically(true); + // 加快网页加载完成的速度,等页面完成再加载图片 + settings.setLoadsImagesAutomatically(true); + // 本地 DOM 存储(解决加载某些网页出现白板现象) + settings.setDomStorageEnabled(true); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // 解决 Android 5.0 上 WebView 默认不允许加载 Http 与 Https 混合内容 + settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); + } + + // 不显示滚动条 + setVerticalScrollBarEnabled(false); + setHorizontalScrollBarEnabled(false); + } + + /** + * 修复原生 WebView 和 AndroidX 在 Android 5.x 上面崩溃的问题 + */ + public static Context getFixedContext(Context context) { + // 博客地址:https://blog.csdn.net/qq_34206863/article/details/103660307 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + // 不用上下文 + return context.createConfigurationContext(new Configuration()); + } + return context; + } + + /** + * 获取当前的url + * + * @return 返回原始的url,因为有些url是被WebView解码过的 + */ + @Override + public String getUrl() { + String originalUrl = super.getOriginalUrl(); + // 避免开始时同时加载两个地址而导致的崩溃 + if (originalUrl != null) { + return originalUrl; + } + return super.getUrl(); + } + + @Override + public void onResume() { + super.onResume(); + resumeTimers(); + } + + @Override + public void onPause() { + super.onPause(); + pauseTimers(); + } + + public void onDestroy() { + ((ViewGroup) getParent()).removeView(this); + //清除历史记录 + clearHistory(); + //停止加载 + stopLoading(); + //加载一个空白页 + loadUrl("about:blank"); + setBrowserChromeClient(null); + setBrowserViewClient(null); + //移除WebView所有的View对象 + removeAllViews(); + //销毁此的WebView的内部状态 + destroy(); + } + + /** + * 已过时,推荐使用 {@link BrowserViewClient} + */ + @Deprecated + @Override + public void setWebViewClient(WebViewClient client) { + super.setWebViewClient(client); + } + + public void setBrowserViewClient(BrowserViewClient client) { + super.setWebViewClient(client); + } + + /** + * 已过时,推荐使用 {@link BrowserChromeClient} + */ + @Deprecated + @Override + public void setWebChromeClient(WebChromeClient client) { + super.setWebChromeClient(client); + } + + public void setBrowserChromeClient(BrowserChromeClient client) { + super.setWebChromeClient(client); + } + + public static class BrowserViewClient extends WebViewClient { + + /** + * 同名 API 兼容 + */ + @TargetApi(Build.VERSION_CODES.M) + @Override + public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { + if (request.isForMainFrame()) { + onReceivedError(view, + error.getErrorCode(), error.getDescription().toString(), + request.getUrl().toString()); + } + } + + @Override + public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { + super.onReceivedError(view, errorCode, description, failingUrl); + } + + @Override + public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { + // 注意一定要去除这行代码,否则设置无效。 + //super.onReceivedSslError(view, handler, error); + // Android默认的处理方式 + //handler.cancel(); + // 接受所有网站的证书 + handler.proceed(); + } + + /** + * 同名 API 兼容 + */ + @TargetApi(Build.VERSION_CODES.N) + @Override + public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + return shouldOverrideUrlLoading(view, request.getUrl().toString()); + } + + /** + * 跳转到其他链接 + */ + @Override + public boolean shouldOverrideUrlLoading(WebView view, final String url) { + String scheme = Uri.parse(url).getScheme(); + if (scheme != null) { + scheme = scheme.toLowerCase(); + } + if ("http".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme)) { + view.loadUrl(url); + } + // 已经处理该链接请求 + return true; + } + } + + public static class BrowserChromeClient extends WebChromeClient { + + private final BrowserView mWebView; + private FragmentActivity mActivity; + + private View mCustomView; + private CustomViewCallback mCustomViewCallback; + + public BrowserChromeClient(BrowserView view) { + mWebView = view; + if (view.getContext() instanceof FragmentActivity) { + mActivity = (FragmentActivity) view.getContext(); + } + } + + /** + * 播放视频时进入全屏回调 + */ + @Override + public void onShowCustomView(View view, CustomViewCallback callback) { + if (mActivity == null) { + return; + } + mCustomViewCallback = callback; + // 给Activity设置横屏 + mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + + if (mCustomView != null) { + mWebView.setVisibility(View.GONE); + mCustomViewCallback.onCustomViewHidden(); + mWebView.setVisibility(View.VISIBLE); + return; + } + + mWebView.addView(view); + mCustomView = view; + } + + /** + * 播放视频时退出全屏回调 + */ + @Override + public void onHideCustomView() { + // 不是全屏播放状态就不往下执行 + if (mActivity == null || mCustomView == null || mCustomViewCallback == null) { + return; + } + + // 给Activity设置竖屏 + mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + + mWebView.removeView(mCustomView); + mCustomView = null; + + mWebView.setVisibility(View.GONE); + mCustomViewCallback.onCustomViewHidden(); + mWebView.setVisibility(View.VISIBLE); + } + + /** + * 弹出警告框 + */ + @Override + public boolean onJsAlert(WebView view, String url, String message, final JsResult result) { + if (mActivity == null) { + return super.onJsAlert(view, url, message, result); + } else { + new ToastDialog.Builder(mActivity) + .setType(ToastDialog.Type.WARN) + .setMessage(message) + .addOnDismissListener(dialog -> result.confirm()) + .show(); + return true; + } + } + + /** + * 弹出确定取消框 + */ + @Override + public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) { + if (mActivity == null) { + return super.onJsConfirm(view, url, message, result); + } else { + new MessageDialog.Builder(mActivity) + .setMessage(message) + .setCancelable(false) + .setListener(new MessageDialog.OnListener() { + + @Override + public void onConfirm(BaseDialog dialog) { + result.confirm(); + } + + @Override + public void onCancel(BaseDialog dialog) { + result.cancel(); + } + }) + .show(); + return true; + } + } + + /** + * 弹出输入框 + */ + @Override + public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, final JsPromptResult result) { + if (mActivity == null){ + return super.onJsPrompt(view, url, message, defaultValue, result); + } else { + new InputDialog.Builder(mActivity) + .setContent(defaultValue) + .setHint(message) + .setListener(new InputDialog.OnListener() { + + @Override + public void onConfirm(BaseDialog dialog, String content) { + result.confirm(content); + } + + @Override + public void onCancel(BaseDialog dialog) { + result.cancel(); + } + }) + .show(); + return true; + } + } + } +} \ No newline at end of file diff --git a/widget/src/main/java/com/hjq/widget/layout/HintLayout.java b/app/src/main/java/com/hjq/demo/widget/HintLayout.java similarity index 79% rename from widget/src/main/java/com/hjq/widget/layout/HintLayout.java rename to app/src/main/java/com/hjq/demo/widget/HintLayout.java index 6fb003d1..aa750d31 100644 --- a/widget/src/main/java/com/hjq/widget/layout/HintLayout.java +++ b/app/src/main/java/com/hjq/demo/widget/HintLayout.java @@ -1,24 +1,24 @@ -package com.hjq.widget.layout; +package com.hjq.demo.widget; import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; -import android.os.Build; import android.util.AttributeSet; import android.view.LayoutInflater; -import android.view.MotionEvent; import android.view.ViewGroup; -import android.widget.ImageView; +import android.widget.FrameLayout; import android.widget.TextView; import androidx.annotation.AttrRes; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.RawRes; import androidx.annotation.StringRes; import androidx.core.content.ContextCompat; -import com.hjq.widget.R; +import com.airbnb.lottie.LottieAnimationView; +import com.hjq.demo.R; /** * author : Android 轮子哥 @@ -26,25 +26,29 @@ * time : 2019/04/18 * desc : 状态布局(网络错误,异常错误,空数据) */ -public final class HintLayout extends SimpleLayout { +public final class HintLayout extends FrameLayout { /** 提示布局 */ private ViewGroup mMainLayout; /** 提示图标 */ - private ImageView mImageView; + private LottieAnimationView mImageView; /** 提示文本 */ private TextView mTextView; public HintLayout(@NonNull Context context) { - super(context); + this(context, null); } public HintLayout(@NonNull Context context, @Nullable AttributeSet attrs) { - super(context, attrs); + this(context, attrs, 0); } public HintLayout(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) { super(context, attrs, defStyleAttr); + + setClickable(true); + setFocusable(true); + setFocusableInTouchMode(true); } /** @@ -94,6 +98,13 @@ public void setIcon(Drawable drawable) { } } + /** + * 设置提示动画 + */ + public void setAnim(@RawRes int id) { + mImageView.setAnimation(id); + } + /** * 设置提示文本,请在show方法之后调用 */ @@ -120,11 +131,7 @@ private void initLayout() { if (mMainLayout.getBackground() == null) { // 默认使用 windowBackground 作为背景 TypedArray ta = getContext().obtainStyledAttributes(new int[]{android.R.attr.windowBackground}); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - mMainLayout.setBackground(ta.getDrawable(0)); - } else { - mMainLayout.setBackgroundDrawable(ta.getDrawable(0)); - } + mMainLayout.setBackground(ta.getDrawable(0)); ta.recycle(); } @@ -132,11 +139,11 @@ private void initLayout() { } @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { + public void setOnClickListener(@Nullable OnClickListener l) { if (isShow()) { - // 拦截布局中的触摸事件,拦截事件,防止传递 - return true; + mMainLayout.setOnClickListener(l); + } else { + super.setOnClickListener(l); } - return super.onInterceptTouchEvent(ev); } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/widget/LoopView.java b/app/src/main/java/com/hjq/demo/widget/LoopView.java deleted file mode 100644 index af10b1f6..00000000 --- a/app/src/main/java/com/hjq/demo/widget/LoopView.java +++ /dev/null @@ -1,574 +0,0 @@ -package com.hjq.demo.widget; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.Typeface; -import android.util.AttributeSet; -import android.util.Log; -import android.util.TypedValue; -import android.view.GestureDetector; -import android.view.MotionEvent; -import android.view.View; - -import com.hjq.demo.R; - -import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -/** - * author : brucetoo - * github : https://github.com/brucetoo/PickView - * time : 2019/02/17 - * desc : 循环滚动列表自定义控件 - */ -public final class LoopView extends View { - - private static final String TAG = "LoopView"; - - private final ScheduledExecutorService mExecutor = Executors.newSingleThreadScheduledExecutor(); - private ScheduledFuture mScheduledFuture; - - private LoopScrollListener mListener; - - private List mData; - - private final Paint mTopBottomTextPaint; - private final Paint mCenterTextPaint; - private final Paint mCenterLinePaint; - - private int mTotalScrollY; - private final GestureDetector mGestureDetector; - private int mSelectedItem; - private int mTextSize; - - private int mMaxTextWidth; - private int mMaxTextHeight; - - private int mTopBottomTextColor; - - private int mCenterTextColor; - private int mCenterLineColor; - - private float mLineSpacingMultiplier; - private boolean mCanLoop; - - private float mTopLineY; - private float mBottomLineY; - - private int mCurrentIndex; - private int mInitPosition; - - private float mHorizontalPadding; - private float mVerticalPadding; - - private float mItemHeight; - private int mDrawItemsCount; - private String[] mItemTempArray; - - private float mCircularDiameter; - private float mCircularRadius; - - public LoopView(Context context) { - this(context, null); - } - - public LoopView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public LoopView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - - TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.LoopView); - if (array != null) { - mTopBottomTextColor = array.getColor(R.styleable.LoopView_lv_topBottomTextColor, 0xffafafaf); - mCenterTextColor = array.getColor(R.styleable.LoopView_lv_centerTextColor, 0xff313131); - mCenterLineColor = array.getColor(R.styleable.LoopView_lv_lineColor, 0xffc5c5c5); - mCanLoop = array.getBoolean(R.styleable.LoopView_lv_canLoop, true); - mInitPosition = array.getInt(R.styleable.LoopView_lv_initPosition, -1); - mTextSize = array.getDimensionPixelSize(R.styleable.LoopView_lv_textSize, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 18, getContext().getResources().getDisplayMetrics())); - mDrawItemsCount = array.getInt(R.styleable.LoopView_lv_drawItemCount, 7); - mItemTempArray = new String[mDrawItemsCount]; - array.recycle(); - } - - mLineSpacingMultiplier = 3; - - mTopBottomTextPaint = new Paint(); - mCenterTextPaint = new Paint(); - mCenterLinePaint = new Paint(); - - setLayerType(LAYER_TYPE_SOFTWARE, null); - - mGestureDetector = new GestureDetector(context, new LoopViewGestureListener()); - mGestureDetector.setIsLongpressEnabled(false); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - switch (MeasureSpec.getMode(widthMeasureSpec)) { - case MeasureSpec.AT_MOST: - case MeasureSpec.UNSPECIFIED: - widthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxTextWidth, MeasureSpec.EXACTLY); - break; - case MeasureSpec.EXACTLY: - break; - default: - break; - } - - switch (MeasureSpec.getMode(heightMeasureSpec)) { - case MeasureSpec.AT_MOST: - case MeasureSpec.UNSPECIFIED: - heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) mCircularDiameter, MeasureSpec.EXACTLY); - break; - case MeasureSpec.EXACTLY: - break; - default: - break; - } - setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); - - int width = MeasureSpec.getSize(widthMeasureSpec); - int height = MeasureSpec.getSize(heightMeasureSpec); - - mItemHeight = mLineSpacingMultiplier * mMaxTextHeight; - // auto calculate the text's left/right value when draw - mHorizontalPadding = (width - (float) mMaxTextWidth) / 2; - mVerticalPadding = (height - mCircularDiameter) / 2; - - // topLineY = diameter/2 - itemHeight(mItemHeight) / 2 + mVerticalPadding - mTopLineY = ((mCircularDiameter - mItemHeight) / 2) + mVerticalPadding; - mBottomLineY = ((mCircularDiameter + mItemHeight) / 2) + mVerticalPadding; - } - - @Override - protected void onDraw(Canvas canvas) { - - if (mData == null) { - return; - } - - // the length of single item is mItemHeight - int mChangingItem = (int) (mTotalScrollY / (mItemHeight)); - mCurrentIndex = mInitPosition + mChangingItem % mData.size(); - // can loop - if (!mCanLoop) { - if (mCurrentIndex < 0) { - mCurrentIndex = 0; - } - if (mCurrentIndex > mData.size() - 1) { - mCurrentIndex = mData.size() - 1; - } - } else { // can not loop - if (mCurrentIndex < 0) { - mCurrentIndex = mData.size() + mCurrentIndex; - } - if (mCurrentIndex > mData.size() - 1) { - mCurrentIndex = mCurrentIndex - mData.size(); - } - } - - int count = 0; - // reconfirm each item's value from dataList according to currentIndex, - while (count < mDrawItemsCount) { - int templateItem = mCurrentIndex - (mDrawItemsCount / 2 - count); - if (mCanLoop) { - if (templateItem < 0) { - templateItem = templateItem + mData.size(); - } - if (templateItem > mData.size() - 1) { - templateItem = templateItem - mData.size(); - } - mItemTempArray[count] = mData.get(templateItem); - } else if (templateItem < 0) { - mItemTempArray[count] = ""; - } else if (templateItem > mData.size() - 1) { - mItemTempArray[count] = ""; - } else { - mItemTempArray[count] = mData.get(templateItem); - } - count++; - } - - // draw top and bottom line - canvas.drawLine(0, mTopLineY, getMeasuredWidth(), mTopLineY, mCenterLinePaint); - canvas.drawLine(0, mBottomLineY, getMeasuredWidth(), mBottomLineY, mCenterLinePaint); - - count = 0; - int changingLeftY = (int) (mTotalScrollY % (mItemHeight)); - while (count < mDrawItemsCount) { - canvas.save(); - // L= å * r -> å = rad - float itemHeight = mMaxTextHeight * mLineSpacingMultiplier; - // get radian L = (itemHeight * count - changingLeftY),r = mCircularRadius - double radian = (itemHeight * count - changingLeftY) / mCircularRadius; - // a = rad * 180 / π - // get angle - float angle = (float) (radian * 180 / Math.PI); - - // when angle >= 180 || angle <= 0 don't draw - if (angle >= 180F || angle <= 0F) { - canvas.restore(); - } else { - // translateY = r - r*cos(å) - - // (Math.sin(radian) * mMaxTextHeight) / 2 this is text offset - float translateY = (float) (mCircularRadius - Math.cos(radian) * mCircularRadius - (Math.sin(radian) * mMaxTextHeight) / 2) + mVerticalPadding; - canvas.translate(0.0F, translateY); - // scale offset = Math.sin(radian) -> 0 - 1 - canvas.scale(1.0F, (float) Math.sin(radian)); - if (translateY <= mTopLineY) { - // draw text y between 0 -> mTopLineY,include incomplete text - canvas.save(); - canvas.clipRect(0, 0, getMeasuredWidth(), mTopLineY - translateY); - canvas.drawText(mItemTempArray[count], mHorizontalPadding, mMaxTextHeight, mTopBottomTextPaint); - canvas.restore(); - canvas.save(); - canvas.clipRect(0, mTopLineY - translateY, getMeasuredWidth(), (int) (itemHeight)); - canvas.drawText(mItemTempArray[count], mHorizontalPadding, mMaxTextHeight, mCenterTextPaint); - canvas.restore(); - } else if (mMaxTextHeight + translateY >= mBottomLineY) { - // draw text y between mTopLineY -> mBottomLineY ,include incomplete text - canvas.save(); - canvas.clipRect(0, 0, getMeasuredWidth(), mBottomLineY - translateY); - canvas.drawText(mItemTempArray[count], mHorizontalPadding, mMaxTextHeight, mCenterTextPaint); - canvas.restore(); - canvas.save(); - canvas.clipRect(0, mBottomLineY - translateY, getMeasuredWidth(), (int) (itemHeight)); - canvas.drawText(mItemTempArray[count], mHorizontalPadding, mMaxTextHeight, mTopBottomTextPaint); - canvas.restore(); - } else if (translateY >= mTopLineY && mMaxTextHeight + translateY <= mBottomLineY) { - // draw center complete text - canvas.clipRect(0, 0, getMeasuredWidth(), (int) (itemHeight)); - canvas.drawText(mItemTempArray[count], mHorizontalPadding, mMaxTextHeight, mCenterTextPaint); - // center one indicate selected item - mSelectedItem = mData.indexOf(mItemTempArray[count]); - } - canvas.restore(); - } - count++; - } - } - - @SuppressLint("ClickableViewAccessibility") - @Override - public boolean onTouchEvent(MotionEvent motionevent) { - - if (!mGestureDetector.onTouchEvent(motionevent)) { - startSmoothScrollTo(); - } - return true; - } - - public final void setCanLoop(boolean canLoop) { - mCanLoop = canLoop; - invalidate(); - } - - /** - * set text size - * - * @param size size indicate sp,not px - */ - public final void setTextSize(float size) { - if (size > 0) { - mTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, size, getContext().getResources().getDisplayMetrics()); - } - } - - public void setLineSpacingMultiplier(float spacing) { - this.mLineSpacingMultiplier = spacing; - } - - public int getSelectedItem() { - return mSelectedItem; - } - - public void setInitPosition(int initPosition) { - if (mData == null) { - return; - } - if (initPosition > mData.size()) { - initPosition = mData.size() - 1; - } - mInitPosition = initPosition; - invalidate(); - if (mListener != null) { - mListener.onItemSelect(this, initPosition); - } - } - - public void setLoopListener(LoopScrollListener listener) { - mListener = listener; - } - - /** - * All public method must be called before this method - * @param data data list - */ - public final void setData(List data) { - mData = data; - - if (mData == null) { - throw new IllegalArgumentException("data list must not be null!"); - } - mTopBottomTextPaint.setColor(mTopBottomTextColor); - mTopBottomTextPaint.setAntiAlias(true); - mTopBottomTextPaint.setTypeface(Typeface.MONOSPACE); - mTopBottomTextPaint.setTextSize(mTextSize); - - mCenterTextPaint.setColor(mCenterTextColor); - mCenterTextPaint.setAntiAlias(true); - mCenterTextPaint.setTextScaleX(1.05F); - mCenterTextPaint.setTypeface(Typeface.MONOSPACE); - mCenterTextPaint.setTextSize(mTextSize); - - mCenterLinePaint.setColor(mCenterLineColor); - mCenterLinePaint.setAntiAlias(true); - mCenterLinePaint.setTypeface(Typeface.MONOSPACE); - mCenterLinePaint.setTextSize(mTextSize); - - // measureTextWidthHeight - Rect rect = new Rect(); - for (int i = 0; i < mData.size(); i++) { - String text = mData.get(i); - mCenterTextPaint.getTextBounds(text, 0, text.length(), rect); - - int textWidth = rect.width(); - if (textWidth > mMaxTextWidth) { - mMaxTextWidth = textWidth; - } - - //int textHeight = rect.height(); - //if (textHeight > mMaxTextHeight) { - // mMaxTextHeight = textHeight; - //} - - Paint.FontMetrics fontMetrics = mCenterTextPaint.getFontMetrics(); - mMaxTextHeight = (int) (fontMetrics.bottom - fontMetrics.top) * 2 / 3; - } - - // 计算半圆周 -- mMaxTextHeight * mLineSpacingMultiplier 表示每个item的高度 mDrawItemsCount = 7 - // 实际显示5个,留两个是在圆周的上下面 - // lineSpacingMultiplier是指text上下的距离的值和maxTextHeight一样的意思 所以 = 2 - // mDrawItemsCount - 1 代表圆周的上下两面各被剪切了一半 相当于高度少了一个 mMaxTextHeight - int halfCircumference = (int) (mMaxTextHeight * mLineSpacingMultiplier * (mDrawItemsCount - 1)); - // the diameter of circular 2πr = cir, 2r = height - mCircularDiameter = (int) ((halfCircumference * 2) / Math.PI); - // the radius of circular - mCircularRadius = (int) (halfCircumference / Math.PI); - // FIXME: 7/8/16 通过控件的高度来计算圆弧的周长 - - if (mInitPosition == -1) { - if (mCanLoop) { - mInitPosition = (mData.size() + 1) / 2; - } else { - mInitPosition = 0; - } - } - mCurrentIndex = mInitPosition; - invalidate(); - } - - public List getData() { - return mData; - } - - public int getSize() { - return mData != null ? mData.size() : 0; - } - - private void cancelSchedule() { - - if (mScheduledFuture != null && !mScheduledFuture.isCancelled()) { - mScheduledFuture.cancel(true); - mScheduledFuture = null; - } - } - - private void startSmoothScrollTo() { - int offset = (int) (mTotalScrollY % (mItemHeight)); - cancelSchedule(); - mScheduledFuture = mExecutor.scheduleWithFixedDelay(new HalfHeightRunnable(offset), 0, 10, TimeUnit.MILLISECONDS); - } - - private void startSmoothScrollTo(float velocityY) { - cancelSchedule(); - int velocityFling = 20; - mScheduledFuture = mExecutor.scheduleWithFixedDelay(new FlingRunnable(velocityY), 0, velocityFling, TimeUnit.MILLISECONDS); - } - - class LoopViewGestureListener extends android.view.GestureDetector.SimpleOnGestureListener { - - @Override - public final boolean onDown(MotionEvent motionevent) { - cancelSchedule(); - Log.i(TAG, "LoopViewGestureListener->onDown"); - return true; - } - - @Override - public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - startSmoothScrollTo(velocityY); - Log.i(TAG, "LoopViewGestureListener->onFling"); - return true; - } - - @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - Log.i(TAG, "LoopViewGestureListener->onScroll"); - mTotalScrollY = (int) ((float) mTotalScrollY + distanceY); - if (!mCanLoop) { - int initPositionCircleLength = (int) (mInitPosition * (mItemHeight)); - int initPositionStartY = -1 * initPositionCircleLength; - if (mTotalScrollY < initPositionStartY) { - mTotalScrollY = initPositionStartY; - } - - int circleLength = (int) ((float) (mData.size() - 1 - mInitPosition) * (mItemHeight)); - if (mTotalScrollY >= circleLength) { - mTotalScrollY = circleLength; - } - } - - invalidate(); - return true; - } - } - - class SelectedRunnable implements Runnable { - - @Override - public final void run() { - if (mListener != null) { - mListener.onItemSelect(LoopView.this, getSelectedItem()); - } - } - } - - /** - * Use in ACTION_UP - */ - private class HalfHeightRunnable implements Runnable { - - final int offset; - int realTotalOffset; - int realOffset; - - HalfHeightRunnable(int offset) { - this.offset = offset; - realTotalOffset = Integer.MAX_VALUE; - realOffset = 0; - } - - @Override - public void run() { - // first in - if (realTotalOffset == Integer.MAX_VALUE) { - - if ((float) offset > mItemHeight / 2.0F) { - // move to next item - realTotalOffset = (int) (mItemHeight - (float) offset); - } else { - // move to pre item - realTotalOffset = -offset; - } - } - - realOffset = (int) ((float) realTotalOffset * 0.1F); - - if (realOffset == 0) { - - if (realTotalOffset < 0) { - realOffset = -1; - } else { - realOffset = 1; - } - } - if (Math.abs(realTotalOffset) <= 0) { - cancelSchedule(); - post(new Runnable() { - @Override - public void run() { - if (mListener != null) { - postDelayed(new SelectedRunnable(), 200L); - } - } - }); - } else { - mTotalScrollY = mTotalScrollY + realOffset; - postInvalidate(); - realTotalOffset = realTotalOffset - realOffset; - } - } - } - - /** - * Use in {@link LoopViewGestureListener#onFling(MotionEvent, MotionEvent, float, float)} - */ - private class FlingRunnable implements Runnable { - - float velocity; - final float velocityY; - - FlingRunnable(float velocityY) { - this.velocityY = velocityY; - this.velocity = Integer.MAX_VALUE; - } - - @Override - public void run() { - if (velocity == Integer.MAX_VALUE) { - if (Math.abs(velocityY) > 2000F) { - if (velocityY > 0.0F) { - velocity = 2000F; - } else { - velocity = -2000F; - } - } else { - velocity = velocityY; - } - } - Log.i(TAG, "velocity->" + velocity); - if (Math.abs(velocity) >= 0.0F && Math.abs(velocity) <= 20F) { - cancelSchedule(); - post(new Runnable() { - @Override - public void run() { - startSmoothScrollTo(); - } - }); - return; - } - int i = (int) ((velocity * 10F) / 1000F); - mTotalScrollY = mTotalScrollY - i; - if (!mCanLoop) { - float itemHeight = mLineSpacingMultiplier * mMaxTextHeight; - if (mTotalScrollY <= (int) ((float) (-mInitPosition) * itemHeight)) { - velocity = 40F; - mTotalScrollY = (int) ((float) (-mInitPosition) * itemHeight); - } else if (mTotalScrollY >= (int) ((float) (mData.size() - 1 - mInitPosition) * itemHeight)) { - mTotalScrollY = (int) ((float) (mData.size() - 1 - mInitPosition) * itemHeight); - velocity = -40F; - } - } - if (velocity < 0.0F) { - velocity = velocity + 20F; - } else { - velocity = velocity - 20F; - } - postInvalidate(); - } - } - - public interface LoopScrollListener { - void onItemSelect(LoopView loopView, int position); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/widget/NumberProgressBar.java b/app/src/main/java/com/hjq/demo/widget/NumberProgressBar.java deleted file mode 100644 index ff97fe6f..00000000 --- a/app/src/main/java/com/hjq/demo/widget/NumberProgressBar.java +++ /dev/null @@ -1,258 +0,0 @@ -package com.hjq.demo.widget; - -import android.content.Context; -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.util.TypedValue; -import android.view.View; - -import com.hjq.demo.R; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2019/02/20 - * desc : 数字进度条 - */ -public final class NumberProgressBar extends View { - - /** 文本颜色 */ - private int mTextColor; - /** 文字大小 */ - private float mTextSize; - - /** 最大进度 */ - private int mMaxProgress = 100; - /** 当前进度 */ - private int mCurrentProgress = 0; - - /** 进度栏颜色 */ - private int mReachedBarColor; - /** 条未到达区域颜色 */ - private int mUnreachedBarColor; - - /** 到达区域的高度 */ - private float mReachedBarHeight; - /** 未到达区域的高度 */ - private float mUnreachedBarHeight; - - /** 到达区域的画笔 */ - private final Paint mReachedBarPaint; - /** 未触及区域的画笔 */ - private final Paint mUnreachedBarPaint; - - /** 进度文本的绘制 */ - private final Paint mTextPaint; - - /** 到达的栏区绘制矩形 */ - private final RectF mReachedBound = new RectF(0, 0, 0, 0); - /** 未到达的栏区绘制矩形 */ - private final RectF mUnreachedBound = new RectF(0, 0, 0, 0); - - /** 进度文本偏移量 */ - private final float mTextOffset; - - public NumberProgressBar(Context context) { - this(context, null); - } - - public NumberProgressBar(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public NumberProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - - final TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.NumberProgressBar, defStyleAttr, 0); - - mReachedBarColor = array.getColor(R.styleable.NumberProgressBar_pb_reachedColor, Color.rgb(66, 145, 241)); - mUnreachedBarColor = array.getColor(R.styleable.NumberProgressBar_pb_unreachedColor, Color.rgb(204, 204, 204)); - mTextColor = array.getColor(R.styleable.NumberProgressBar_pb_textColor, Color.rgb(66, 145, 241)); - mTextSize = array.getDimension(R.styleable.NumberProgressBar_pb_textSize, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10, getResources().getDisplayMetrics())); - - mReachedBarHeight = array.getDimension(R.styleable.NumberProgressBar_pb_reachedHeight, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1.5f, getResources().getDisplayMetrics())); - mUnreachedBarHeight = array.getDimension(R.styleable.NumberProgressBar_pb_unreachedHeight, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics())); - mTextOffset = array.getDimension(R.styleable.NumberProgressBar_pb_textOffset, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics())); - - setProgress(array.getInt(R.styleable.NumberProgressBar_pb_progress, 0)); - setMax(array.getInt(R.styleable.NumberProgressBar_pb_maxProgress, 100)); - array.recycle(); - - mReachedBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mReachedBarPaint.setColor(mReachedBarColor); - - mUnreachedBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mUnreachedBarPaint.setColor(mUnreachedBarColor); - - mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mTextPaint.setColor(mTextColor); - mTextPaint.setTextSize(mTextSize); - } - - @Override - protected int getSuggestedMinimumWidth() { - return (int) mTextSize; - } - - @Override - protected int getSuggestedMinimumHeight() { - return Math.max((int) mTextSize, Math.max((int) mReachedBarHeight, (int) mUnreachedBarHeight)); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - switch (MeasureSpec.getMode(widthMeasureSpec)) { - case MeasureSpec.AT_MOST: - case MeasureSpec.UNSPECIFIED: - widthMeasureSpec = MeasureSpec.makeMeasureSpec(getSuggestedMinimumWidth() - + getPaddingLeft() + getPaddingRight(), MeasureSpec.EXACTLY); - break; - case MeasureSpec.EXACTLY: - break; - default: - break; - } - switch (MeasureSpec.getMode(heightMeasureSpec)) { - case MeasureSpec.AT_MOST: - case MeasureSpec.UNSPECIFIED: - heightMeasureSpec = MeasureSpec.makeMeasureSpec(getSuggestedMinimumHeight() - + getPaddingTop() + getPaddingBottom(), MeasureSpec.EXACTLY); - break; - case MeasureSpec.EXACTLY: - break; - default: - break; - } - setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); - } - - @Override - protected void onDraw(Canvas canvas) { - // 要在onDraw()中绘制的文本 - String text = (getProgress() * 100 / getMax()) + "%"; - - // 要绘制的文本的宽度 - float textWidth = mTextPaint.measureText(text); - - // 文本开始位置 - float textStart; - - boolean drawReachedBar; - if (getProgress() == 0) { - drawReachedBar = false; - textStart = getPaddingLeft(); - } else { - drawReachedBar = true; - mReachedBound.left = getPaddingLeft(); - mReachedBound.top = getHeight() / 2.0f - mReachedBarHeight / 2.0f; - mReachedBound.right = (getWidth() - getPaddingLeft() - getPaddingRight()) / (getMax() * 1.0f) * getProgress() - mTextOffset + getPaddingLeft(); - mReachedBound.bottom = getHeight() / 2.0f + mReachedBarHeight / 2.0f; - textStart = (mReachedBound.right + mTextOffset); - } - - // 文本结束位置 - float textEnd = (int) ((getHeight() / 2.0f) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2.0f)); - - if ((textStart + textWidth) >= getWidth() - getPaddingRight()) { - textStart = getWidth() - getPaddingRight() - textWidth; - mReachedBound.right = textStart - mTextOffset; - } - - float unreachedBarStart = textStart + textWidth + mTextOffset; - // 确定是否需要绘制未到达区域 - boolean drawUnreachedBar; - if (unreachedBarStart >= getWidth() - getPaddingRight()) { - drawUnreachedBar = false; - } else { - drawUnreachedBar = true; - mUnreachedBound.left = unreachedBarStart; - mUnreachedBound.right = getWidth() - getPaddingRight(); - mUnreachedBound.top = getHeight() / 2.0f + -mUnreachedBarHeight / 2.0f; - mUnreachedBound.bottom = getHeight() / 2.0f + mUnreachedBarHeight / 2.0f; - } - - if (drawReachedBar) { - canvas.drawRect(mReachedBound, mReachedBarPaint); - } - - if (drawUnreachedBar) { - canvas.drawRect(mUnreachedBound, mUnreachedBarPaint); - } - - canvas.drawText(text, textStart, textEnd, mTextPaint); - } - - public int getProgress() { - return mCurrentProgress; - } - - public void setProgress(int progress) { - if (progress <= getMax() && progress >= 0) { - mCurrentProgress = progress; - invalidate(); - } - } - - public int getMax() { - return mMaxProgress; - } - - public void setMax(int maxProgress) { - if (maxProgress > 0) { - mMaxProgress = maxProgress; - invalidate(); - } - } - - public float getReachedBarHeight() { - return mReachedBarHeight; - } - - public void setReachedBarHeight(float height) { - mReachedBarHeight = height; - } - - public float getUnreachedBarHeight() { - return mUnreachedBarHeight; - } - - public void setUnreachedBarHeight(float height) { - mUnreachedBarHeight = height; - } - - public void setProgressTextColor(int textColor) { - mTextColor = textColor; - mTextPaint.setColor(mTextColor); - invalidate(); - } - - public void setProgressTextSize(float textSize) { - mTextSize = textSize; - mTextPaint.setTextSize(mTextSize); - invalidate(); - } - - public int getUnreachedBarColor() { - return mUnreachedBarColor; - } - - public void setUnreachedBarColor(int barColor) { - mUnreachedBarColor = barColor; - mUnreachedBarPaint.setColor(mUnreachedBarColor); - invalidate(); - } - - public int getReachedBarColor() { - return mReachedBarColor; - } - - public void setReachedBarColor(int progressColor) { - mReachedBarColor = progressColor; - mReachedBarPaint.setColor(mReachedBarColor); - invalidate(); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/widget/PasswordView.java b/app/src/main/java/com/hjq/demo/widget/PasswordView.java index 2cf50284..c94a5d8d 100644 --- a/app/src/main/java/com/hjq/demo/widget/PasswordView.java +++ b/app/src/main/java/com/hjq/demo/widget/PasswordView.java @@ -85,7 +85,6 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { widthMeasureSpec = MeasureSpec.makeMeasureSpec(mItemWidth * PASSWORD_COUNT, MeasureSpec.EXACTLY); break; case MeasureSpec.EXACTLY: - break; default: break; } @@ -96,7 +95,6 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { heightMeasureSpec = MeasureSpec.makeMeasureSpec(mItemHeight, MeasureSpec.EXACTLY); break; case MeasureSpec.EXACTLY: - break; default: break; } diff --git a/app/src/main/java/com/hjq/demo/widget/ProgressView.java b/app/src/main/java/com/hjq/demo/widget/ProgressView.java index 4ed08bcf..975f0bc2 100644 --- a/app/src/main/java/com/hjq/demo/widget/ProgressView.java +++ b/app/src/main/java/com/hjq/demo/widget/ProgressView.java @@ -6,7 +6,6 @@ import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.RectF; -import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; @@ -98,17 +97,7 @@ public ProgressView(Context context, AttributeSet attrs, int defStyleAttr) { } array.recycle(); - float animationValue; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - animationValue = Settings.Global.getFloat(getContext().getContentResolver(), Settings.Global.ANIMATOR_DURATION_SCALE, 1); - } else { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - animationValue = Settings.System.getFloat(getContext().getContentResolver(), Settings.System.ANIMATOR_DURATION_SCALE, 1); - } else { - animationValue = Settings.System.getFloat(getContext().getContentResolver(), "animator_duration_scale", 1); - } - } - + float animationValue = Settings.Global.getFloat(getContext().getContentResolver(), Settings.Global.ANIMATOR_DURATION_SCALE, 1); mShouldAnimate = animationValue != 0; } diff --git a/app/src/main/res/drawable-hdpi/bg_phone_verify.png b/app/src/main/res/drawable-hdpi/bg_phone_verify.png deleted file mode 100644 index a051bfb0..00000000 Binary files a/app/src/main/res/drawable-hdpi/bg_phone_verify.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_head_placeholder.png b/app/src/main/res/drawable-hdpi/ic_head_placeholder.png deleted file mode 100644 index 762f49c1..00000000 Binary files a/app/src/main/res/drawable-hdpi/ic_head_placeholder.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_small_phone.png b/app/src/main/res/drawable-hdpi/ic_small_phone.png deleted file mode 100644 index a0f1f722..00000000 Binary files a/app/src/main/res/drawable-hdpi/ic_small_phone.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_small_safe.png b/app/src/main/res/drawable-hdpi/ic_small_safe.png deleted file mode 100644 index 83bd0d6b..00000000 Binary files a/app/src/main/res/drawable-hdpi/ic_small_safe.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ico_go_black.png b/app/src/main/res/drawable-hdpi/ico_go_black.png deleted file mode 100644 index e3020ab0..00000000 Binary files a/app/src/main/res/drawable-hdpi/ico_go_black.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/icon_hint_address.png b/app/src/main/res/drawable-hdpi/icon_hint_address.png deleted file mode 100644 index f5dc5258..00000000 Binary files a/app/src/main/res/drawable-hdpi/icon_hint_address.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/icon_hint_empty.png b/app/src/main/res/drawable-hdpi/icon_hint_empty.png deleted file mode 100644 index 93c129c7..00000000 Binary files a/app/src/main/res/drawable-hdpi/icon_hint_empty.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/icon_hint_nerwork.png b/app/src/main/res/drawable-hdpi/icon_hint_nerwork.png deleted file mode 100644 index 76739785..00000000 Binary files a/app/src/main/res/drawable-hdpi/icon_hint_nerwork.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/icon_hint_request.png b/app/src/main/res/drawable-hdpi/icon_hint_request.png deleted file mode 100644 index 34e236f5..00000000 Binary files a/app/src/main/res/drawable-hdpi/icon_hint_request.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/tab_ico_found.png b/app/src/main/res/drawable-hdpi/tab_ico_found.png deleted file mode 100644 index 63941ee0..00000000 Binary files a/app/src/main/res/drawable-hdpi/tab_ico_found.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/tab_ico_found_off.png b/app/src/main/res/drawable-hdpi/tab_ico_found_off.png deleted file mode 100644 index 770a23d3..00000000 Binary files a/app/src/main/res/drawable-hdpi/tab_ico_found_off.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/tab_ico_home.png b/app/src/main/res/drawable-hdpi/tab_ico_home.png deleted file mode 100644 index 2cefddab..00000000 Binary files a/app/src/main/res/drawable-hdpi/tab_ico_home.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/tab_ico_home_off.png b/app/src/main/res/drawable-hdpi/tab_ico_home_off.png deleted file mode 100644 index 549e7c5f..00000000 Binary files a/app/src/main/res/drawable-hdpi/tab_ico_home_off.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/tab_ico_me.png b/app/src/main/res/drawable-hdpi/tab_ico_me.png deleted file mode 100644 index a3c23310..00000000 Binary files a/app/src/main/res/drawable-hdpi/tab_ico_me.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/tab_ico_me_off.png b/app/src/main/res/drawable-hdpi/tab_ico_me_off.png deleted file mode 100644 index 9b79fb7e..00000000 Binary files a/app/src/main/res/drawable-hdpi/tab_ico_me_off.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/tab_ico_message.png b/app/src/main/res/drawable-hdpi/tab_ico_message.png deleted file mode 100644 index e8326ae6..00000000 Binary files a/app/src/main/res/drawable-hdpi/tab_ico_message.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/tab_ico_message_off.png b/app/src/main/res/drawable-hdpi/tab_ico_message_off.png deleted file mode 100644 index d6c27c2d..00000000 Binary files a/app/src/main/res/drawable-hdpi/tab_ico_message_off.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/update_app_close.png b/app/src/main/res/drawable-hdpi/update_app_close.png deleted file mode 100644 index 4bb3e1f4..00000000 Binary files a/app/src/main/res/drawable-hdpi/update_app_close.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/update_app_top_bg.png b/app/src/main/res/drawable-hdpi/update_app_top_bg.png deleted file mode 100644 index ea6a0163..00000000 Binary files a/app/src/main/res/drawable-hdpi/update_app_top_bg.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/bg_phone_verify.png b/app/src/main/res/drawable-xhdpi/bg_phone_verify.png deleted file mode 100644 index 93c55c36..00000000 Binary files a/app/src/main/res/drawable-xhdpi/bg_phone_verify.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_head_placeholder.png b/app/src/main/res/drawable-xhdpi/ic_head_placeholder.png deleted file mode 100644 index eb84bcb2..00000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_head_placeholder.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_small_phone.png b/app/src/main/res/drawable-xhdpi/ic_small_phone.png deleted file mode 100644 index b42390f3..00000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_small_phone.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_small_safe.png b/app/src/main/res/drawable-xhdpi/ic_small_safe.png deleted file mode 100644 index 00aa5ff8..00000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_small_safe.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ico_go_black.png b/app/src/main/res/drawable-xhdpi/ico_go_black.png deleted file mode 100644 index e9bdd97b..00000000 Binary files a/app/src/main/res/drawable-xhdpi/ico_go_black.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/icon_hint_address.png b/app/src/main/res/drawable-xhdpi/icon_hint_address.png deleted file mode 100644 index fb8436f6..00000000 Binary files a/app/src/main/res/drawable-xhdpi/icon_hint_address.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/icon_hint_empty.png b/app/src/main/res/drawable-xhdpi/icon_hint_empty.png deleted file mode 100644 index cf194c9e..00000000 Binary files a/app/src/main/res/drawable-xhdpi/icon_hint_empty.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/icon_hint_nerwork.png b/app/src/main/res/drawable-xhdpi/icon_hint_nerwork.png deleted file mode 100644 index 5fc3ba2c..00000000 Binary files a/app/src/main/res/drawable-xhdpi/icon_hint_nerwork.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/icon_hint_request.png b/app/src/main/res/drawable-xhdpi/icon_hint_request.png deleted file mode 100644 index d04bb977..00000000 Binary files a/app/src/main/res/drawable-xhdpi/icon_hint_request.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/tab_ico_found.png b/app/src/main/res/drawable-xhdpi/tab_ico_found.png deleted file mode 100644 index ea767ee2..00000000 Binary files a/app/src/main/res/drawable-xhdpi/tab_ico_found.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/tab_ico_found_off.png b/app/src/main/res/drawable-xhdpi/tab_ico_found_off.png deleted file mode 100644 index c6a1d4d1..00000000 Binary files a/app/src/main/res/drawable-xhdpi/tab_ico_found_off.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/tab_ico_home.png b/app/src/main/res/drawable-xhdpi/tab_ico_home.png deleted file mode 100644 index 15795d7c..00000000 Binary files a/app/src/main/res/drawable-xhdpi/tab_ico_home.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/tab_ico_home_off.png b/app/src/main/res/drawable-xhdpi/tab_ico_home_off.png deleted file mode 100644 index 66829376..00000000 Binary files a/app/src/main/res/drawable-xhdpi/tab_ico_home_off.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/tab_ico_me.png b/app/src/main/res/drawable-xhdpi/tab_ico_me.png deleted file mode 100644 index 569d4102..00000000 Binary files a/app/src/main/res/drawable-xhdpi/tab_ico_me.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/tab_ico_me_off.png b/app/src/main/res/drawable-xhdpi/tab_ico_me_off.png deleted file mode 100644 index e4c59664..00000000 Binary files a/app/src/main/res/drawable-xhdpi/tab_ico_me_off.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/tab_ico_message.png b/app/src/main/res/drawable-xhdpi/tab_ico_message.png deleted file mode 100644 index d694be5f..00000000 Binary files a/app/src/main/res/drawable-xhdpi/tab_ico_message.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/tab_ico_message_off.png b/app/src/main/res/drawable-xhdpi/tab_ico_message_off.png deleted file mode 100644 index b6b34703..00000000 Binary files a/app/src/main/res/drawable-xhdpi/tab_ico_message_off.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/update_app_close.png b/app/src/main/res/drawable-xhdpi/update_app_close.png deleted file mode 100644 index d7e37601..00000000 Binary files a/app/src/main/res/drawable-xhdpi/update_app_close.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/update_app_top_bg.png b/app/src/main/res/drawable-xhdpi/update_app_top_bg.png deleted file mode 100644 index cf24bd38..00000000 Binary files a/app/src/main/res/drawable-xhdpi/update_app_top_bg.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_custom_dialog.webp b/app/src/main/res/drawable-xxhdpi/bg_custom_dialog.webp similarity index 100% rename from app/src/main/res/drawable-xxxhdpi/bg_custom_dialog.webp rename to app/src/main/res/drawable-xxhdpi/bg_custom_dialog.webp diff --git a/app/src/main/res/drawable-xxhdpi/bg_guide_1.webp b/app/src/main/res/drawable-xxhdpi/bg_guide_1.webp new file mode 100644 index 00000000..d347de55 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/bg_guide_1.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/bg_guide_2.webp b/app/src/main/res/drawable-xxhdpi/bg_guide_2.webp new file mode 100644 index 00000000..6828de4f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/bg_guide_2.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/bg_guide_3.webp b/app/src/main/res/drawable-xxhdpi/bg_guide_3.webp new file mode 100644 index 00000000..3118c088 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/bg_guide_3.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/bg_update_app_top.png b/app/src/main/res/drawable-xxhdpi/bg_update_app_top.png new file mode 100644 index 00000000..7f812f8d Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/bg_update_app_top.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_add_black.png b/app/src/main/res/drawable-xxhdpi/ic_add_black.png new file mode 100644 index 00000000..967b1776 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_add_black.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_back_black.png b/app/src/main/res/drawable-xxhdpi/ic_back_black.png new file mode 100644 index 00000000..d6142c07 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_back_black.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_custom_dialog.webp b/app/src/main/res/drawable-xxhdpi/ic_custom_dialog.webp similarity index 100% rename from app/src/main/res/drawable-xxxhdpi/ic_custom_dialog.webp rename to app/src/main/res/drawable-xxhdpi/ic_custom_dialog.webp diff --git a/app/src/main/res/drawable-xxhdpi/ic_debug.png b/app/src/main/res/drawable-xxhdpi/ic_debug.png new file mode 100644 index 00000000..3bc3d009 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_debug.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_dialog_close.png b/app/src/main/res/drawable-xxhdpi/ic_dialog_close.png new file mode 100644 index 00000000..db854193 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_dialog_close.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_dialog_delete.png b/app/src/main/res/drawable-xxhdpi/ic_dialog_delete.png new file mode 100644 index 00000000..25f641d1 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_dialog_delete.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_guide_3.png b/app/src/main/res/drawable-xxhdpi/ic_guide_3.png new file mode 100644 index 00000000..c2e54bda Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_guide_3.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_hint_empty.png b/app/src/main/res/drawable-xxhdpi/ic_hint_empty.png new file mode 100644 index 00000000..aa1349d4 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_hint_empty.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_hint_error.png b/app/src/main/res/drawable-xxhdpi/ic_hint_error.png new file mode 100644 index 00000000..f0a6bda3 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_hint_error.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_hint_nerwork.png b/app/src/main/res/drawable-xxhdpi/ic_hint_nerwork.png new file mode 100644 index 00000000..66263c1c Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_hint_nerwork.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_hint_order.png b/app/src/main/res/drawable-xxhdpi/ic_hint_order.png new file mode 100644 index 00000000..366c96e2 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_hint_order.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_search_black.png b/app/src/main/res/drawable-xxhdpi/ic_search_black.png new file mode 100644 index 00000000..a6fcef7c Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_search_black.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_search_white.png b/app/src/main/res/drawable-xxhdpi/ic_search_white.png new file mode 100644 index 00000000..241fbb05 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_search_white.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_hint_address.png b/app/src/main/res/drawable-xxhdpi/icon_hint_address.png deleted file mode 100644 index 528a582e..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/icon_hint_address.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_hint_empty.png b/app/src/main/res/drawable-xxhdpi/icon_hint_empty.png deleted file mode 100644 index a44de563..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/icon_hint_empty.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_hint_nerwork.png b/app/src/main/res/drawable-xxhdpi/icon_hint_nerwork.png deleted file mode 100644 index aff56b19..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/icon_hint_nerwork.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_hint_request.png b/app/src/main/res/drawable-xxhdpi/icon_hint_request.png deleted file mode 100644 index 848c3441..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/icon_hint_request.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/update_app_close.png b/app/src/main/res/drawable-xxhdpi/update_app_close.png deleted file mode 100644 index 27fc1eef..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/update_app_close.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/update_app_top_bg.png b/app/src/main/res/drawable-xxhdpi/update_app_top_bg.png deleted file mode 100644 index ddbee796..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/update_app_top_bg.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_launcher.jpg b/app/src/main/res/drawable-xxxhdpi/bg_launcher.jpg new file mode 100644 index 00000000..496fec14 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_launcher.jpg differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_launcher.webp b/app/src/main/res/drawable-xxxhdpi/bg_launcher.webp deleted file mode 100644 index 2cc55f47..00000000 Binary files a/app/src/main/res/drawable-xxxhdpi/bg_launcher.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_dialog_close.png b/app/src/main/res/drawable-xxxhdpi/ic_dialog_close.png deleted file mode 100644 index fb990055..00000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_dialog_close.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_dialog_delete.webp b/app/src/main/res/drawable-xxxhdpi/ic_dialog_delete.webp deleted file mode 100644 index 99bd8ca7..00000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_dialog_delete.webp and /dev/null differ diff --git a/app/src/main/res/drawable/bg_menu.png b/app/src/main/res/drawable/bg_menu.png new file mode 100644 index 00000000..54b5ad8e Binary files /dev/null and b/app/src/main/res/drawable/bg_menu.png differ diff --git a/app/src/main/res/drawable/bg_webview_progress_bar.xml b/app/src/main/res/drawable/bg_progress_gradient.xml similarity index 51% rename from app/src/main/res/drawable/bg_webview_progress_bar.xml rename to app/src/main/res/drawable/bg_progress_gradient.xml index b6a716ac..7ae56db8 100644 --- a/app/src/main/res/drawable/bg_webview_progress_bar.xml +++ b/app/src/main/res/drawable/bg_progress_gradient.xml @@ -2,23 +2,19 @@ - + android:centerColor="@color/transparent" + android:endColor="@color/transparent" + android:startColor="@color/transparent" /> - - + android:endColor="@color/colorButtonPressed" + android:startColor="@color/transparent" /> diff --git a/app/src/main/res/drawable/bg_setting_update.xml b/app/src/main/res/drawable/bg_setting_update.xml new file mode 100644 index 00000000..6b7ea67b --- /dev/null +++ b/app/src/main/res/drawable/bg_setting_update.xml @@ -0,0 +1,11 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_checkbox_checked.xml b/app/src/main/res/drawable/ic_checkbox_checked.xml index 6072bcc4..d8ce4489 100644 --- a/app/src/main/res/drawable/ic_checkbox_checked.xml +++ b/app/src/main/res/drawable/ic_checkbox_checked.xml @@ -1,9 +1,11 @@ + + android:viewportWidth="1024" + android:viewportHeight="1024"> + + android:fillColor="@color/colorAccent" + android:pathData="M512 64C262.4 64 64 262.4 64 512s198.4 448 448 448 448-198.4 448-448S761.6 64 512 64z m236.8 326.4L454.4 684.8c-12.8 12.8-32 12.8-44.8 0L275.2 544c-12.8-12.8-12.8-32 0-44.8 12.8-12.8 32-12.8 44.8 0l115.2 115.2L704 339.2c12.8-12.8 32-12.8 44.8 0 12.8 12.8 12.8 38.4 0 51.2z" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_checkbox_disable.xml b/app/src/main/res/drawable/ic_checkbox_disable.xml index 0b95607e..a73bee9d 100644 --- a/app/src/main/res/drawable/ic_checkbox_disable.xml +++ b/app/src/main/res/drawable/ic_checkbox_disable.xml @@ -1,10 +1,12 @@ + + android:viewportWidth="1024" + android:viewportHeight="1024"> + + android:fillColor="@color/colorAccent" + android:pathData="M512 64C262.4 64 64 262.4 64 512s198.4 448 448 448 448-198.4 448-448S761.6 64 512 64z m236.8 326.4L454.4 684.8c-12.8 12.8-32 12.8-44.8 0L275.2 544c-12.8-12.8-12.8-32 0-44.8 12.8-12.8 32-12.8 44.8 0l115.2 115.2L704 339.2c12.8-12.8 32-12.8 44.8 0 12.8 12.8 12.8 38.4 0 51.2z" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_checkbox_normal.xml b/app/src/main/res/drawable/ic_checkbox_normal.xml deleted file mode 100644 index 0f7192ad..00000000 --- a/app/src/main/res/drawable/ic_checkbox_normal.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_compound_normal.xml b/app/src/main/res/drawable/ic_compound_normal.xml new file mode 100644 index 00000000..f9789e33 --- /dev/null +++ b/app/src/main/res/drawable/ic_compound_normal.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_photo_camera.xml b/app/src/main/res/drawable/ic_photo_camera.xml index e2d7c079..9718625a 100644 --- a/app/src/main/res/drawable/ic_photo_camera.xml +++ b/app/src/main/res/drawable/ic_photo_camera.xml @@ -6,7 +6,7 @@ android:viewportHeight="24.00"> diff --git a/app/src/main/res/drawable/ic_photo_succeed.xml b/app/src/main/res/drawable/ic_photo_succeed.xml index 12e90df5..42d88c7a 100644 --- a/app/src/main/res/drawable/ic_photo_succeed.xml +++ b/app/src/main/res/drawable/ic_photo_succeed.xml @@ -6,8 +6,8 @@ android:viewportHeight="24.00"> + android:strokeLineJoin="round" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_radiobutton_checked.xml b/app/src/main/res/drawable/ic_radiobutton_checked.xml new file mode 100644 index 00000000..fd199098 --- /dev/null +++ b/app/src/main/res/drawable/ic_radiobutton_checked.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_radiobutton_disable.xml b/app/src/main/res/drawable/ic_radiobutton_disable.xml new file mode 100644 index 00000000..834d84ec --- /dev/null +++ b/app/src/main/res/drawable/ic_radiobutton_disable.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/image/src/main/res/drawable-xxhdpi/image_load_err.png b/app/src/main/res/drawable/image_load_err.png similarity index 100% rename from image/src/main/res/drawable-xxhdpi/image_load_err.png rename to app/src/main/res/drawable/image_load_err.png diff --git a/image/src/main/res/drawable-xxhdpi/image_loading.png b/app/src/main/res/drawable/image_loading.png similarity index 100% rename from image/src/main/res/drawable-xxhdpi/image_loading.png rename to app/src/main/res/drawable/image_loading.png diff --git a/app/src/main/res/drawable/selector_checkbox.xml b/app/src/main/res/drawable/selector_checkbox.xml index 2cdcf993..96abba62 100644 --- a/app/src/main/res/drawable/selector_checkbox.xml +++ b/app/src/main/res/drawable/selector_checkbox.xml @@ -8,6 +8,6 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_radiobutton.xml b/app/src/main/res/drawable/selector_radiobutton.xml new file mode 100644 index 00000000..98c66d0c --- /dev/null +++ b/app/src/main/res/drawable/selector_radiobutton.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_update_bottom.xml b/app/src/main/res/drawable/selector_update_bottom.xml deleted file mode 100644 index 4abd0b3a..00000000 --- a/app/src/main/res/drawable/selector_update_bottom.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_update_button.xml b/app/src/main/res/drawable/selector_update_button.xml deleted file mode 100644 index ac6690fe..00000000 --- a/app/src/main/res/drawable/selector_update_button.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml index dc30af74..c5277bf9 100644 --- a/app/src/main/res/layout/activity_about.xml +++ b/app/src/main/res/layout/activity_about.xml @@ -25,6 +25,9 @@ android:layout_width="match_parent" android:layout_height="0px" android:layout_weight="1" + android:clickable="true" + android:focusable="true" + android:focusableInTouchMode="true" android:gravity="center_horizontal" android:orientation="vertical"> diff --git a/app/src/main/res/layout/activity_browser.xml b/app/src/main/res/layout/activity_browser.xml new file mode 100644 index 00000000..320e6270 --- /dev/null +++ b/app/src/main/res/layout/activity_browser.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_crash.xml b/app/src/main/res/layout/activity_crash.xml index 91062e98..6bebf0f9 100644 --- a/app/src/main/res/layout/activity_crash.xml +++ b/app/src/main/res/layout/activity_crash.xml @@ -19,6 +19,7 @@ android:layout_height="wrap_content" android:layout_marginTop="15dp" android:gravity="center" + android:lineSpacingExtra="5dp" android:text="@string/crash_error_hint" android:textSize="18sp" android:textStyle="bold" /> @@ -34,8 +35,8 @@ style="@style/ButtonStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginLeft="10dp" - android:layout_marginRight="10dp" + android:layout_marginStart="10dp" + android:layout_marginEnd="10dp" android:text="@string/crash_error_restart" /> diff --git a/app/src/main/res/layout/activity_dialog.xml b/app/src/main/res/layout/activity_dialog.xml index 4fcb8e62..6785801b 100644 --- a/app/src/main/res/layout/activity_dialog.xml +++ b/app/src/main/res/layout/activity_dialog.xml @@ -11,6 +11,7 @@ + + + + + + + + + + + + + + + + + \ 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 index 5f638e13..0e2c8964 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -6,7 +6,7 @@ android:layout_height="match_parent" android:background="@color/white" android:gravity="center_horizontal" - android:layoutAnimation="@anim/layout_animation_from_bottom" + android:layoutAnimation="@anim/layout_from_bottom" android:orientation="vertical" tools:context=".ui.activity.LoginActivity" tools:layoutAnimation="@null"> @@ -39,9 +39,9 @@ style="@style/EditTextStyle" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="40dp" + android:layout_marginStart="40dp" android:layout_marginTop="10dp" - android:layout_marginRight="40dp" + android:layout_marginEnd="40dp" android:hint="@string/common_phone_input_hint" android:inputType="textVisiblePassword" android:singleLine="true" @@ -49,34 +49,34 @@ + android:layout_marginStart="40dp" + android:layout_marginEnd="40dp" /> + android:layout_marginStart="40dp" + android:layout_marginEnd="40dp" /> @@ -85,9 +85,9 @@ style="@style/ButtonStyle" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="40dp" + android:layout_marginStart="40dp" android:layout_marginTop="30dp" - android:layout_marginRight="40dp" + android:layout_marginEnd="40dp" android:text="@string/login_text" /> @@ -114,8 +114,8 @@ diff --git a/app/src/main/res/layout/activity_password_forget.xml b/app/src/main/res/layout/activity_password_forget.xml index fb07d356..d9ab77f7 100644 --- a/app/src/main/res/layout/activity_password_forget.xml +++ b/app/src/main/res/layout/activity_password_forget.xml @@ -19,21 +19,21 @@ android:layout_height="wrap_content" android:layout_marginTop="15dp" android:background="@color/white" - android:drawableLeft="@drawable/ic_small_phone" + android:drawableStart="@drawable/ic_small_phone" android:drawablePadding="15dp" android:hint="@string/common_phone_input_hint" android:inputType="textVisiblePassword" - android:paddingLeft="20dp" + android:paddingStart="20dp" android:paddingTop="18dp" - android:paddingRight="20dp" + android:paddingEnd="20dp" android:paddingBottom="18dp" android:singleLine="true" app:regexType="mobile" /> + android:layout_marginStart="20dp" + android:layout_marginEnd="20dp" /> @@ -68,9 +69,9 @@ style="@style/ButtonStyle" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="40dp" + android:layout_marginStart="40dp" android:layout_marginTop="50dp" - android:layout_marginRight="40dp" + android:layout_marginEnd="40dp" android:text="@string/common_step_next" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_password_reset.xml b/app/src/main/res/layout/activity_password_reset.xml index 3f7d0f9b..9f94388e 100644 --- a/app/src/main/res/layout/activity_password_reset.xml +++ b/app/src/main/res/layout/activity_password_reset.xml @@ -19,21 +19,21 @@ android:layout_height="wrap_content" android:layout_marginTop="15dp" android:background="@color/white" - android:drawableLeft="@drawable/ic_small_safe" + android:drawableStart="@drawable/ic_small_safe" android:drawablePadding="15dp" android:hint="@string/password_reset_phone_hint1" android:inputType="textPassword" android:maxLength="20" android:maxLines="1" - android:paddingLeft="20dp" + android:paddingStart="20dp" android:paddingTop="18dp" - android:paddingRight="20dp" + android:paddingEnd="20dp" android:paddingBottom="18dp" /> + android:layout_marginStart="20dp" + android:layout_marginEnd="20dp" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_personal_data.xml b/app/src/main/res/layout/activity_personal_data.xml index 0cc2b5b5..1a32646a 100644 --- a/app/src/main/res/layout/activity_personal_data.xml +++ b/app/src/main/res/layout/activity_personal_data.xml @@ -12,40 +12,23 @@ android:layout_height="wrap_content" app:title="@string/personal_data_title" /> - - - + app:bar_leftText="@string/personal_data_head"> - - - + android:layout_gravity="end|center_vertical" + android:layout_margin="20dp" + android:scaleType="centerCrop" + tools:src="@drawable/ic_head_placeholder" /> - - - + + app:bar_rightIcon="@drawable/ico_go_black" /> @@ -52,13 +52,13 @@ android:layout_height="wrap_content" android:layout_weight="1" android:background="@color/white" - android:drawableLeft="@drawable/ic_small_safe" + android:drawableStart="@drawable/ic_small_safe" android:drawablePadding="15dp" android:hint="@string/common_code_input_hint" - android:maxLength="4" - android:paddingLeft="20dp" + android:maxLength="@integer/sms_code_length" + android:paddingStart="20dp" android:paddingTop="18dp" - android:paddingRight="20dp" + android:paddingEnd="20dp" android:paddingBottom="18dp" android:singleLine="true" /> diff --git a/app/src/main/res/layout/activity_phone_verify.xml b/app/src/main/res/layout/activity_phone_verify.xml index ba22e8a2..5fa6168f 100644 --- a/app/src/main/res/layout/activity_phone_verify.xml +++ b/app/src/main/res/layout/activity_phone_verify.xml @@ -46,13 +46,13 @@ android:layout_height="wrap_content" android:layout_weight="1" android:background="@color/white" - android:drawableLeft="@drawable/ic_small_safe" + android:drawableStart="@drawable/ic_small_safe" android:drawablePadding="15dp" android:hint="@string/common_code_input_hint" - android:maxLength="4" - android:paddingLeft="20dp" + android:maxLength="@integer/sms_code_length" + android:paddingStart="20dp" android:paddingTop="18dp" - android:paddingRight="20dp" + android:paddingEnd="20dp" android:paddingBottom="18dp" android:singleLine="true" /> diff --git a/app/src/main/res/layout/activity_photo.xml b/app/src/main/res/layout/activity_photo.xml index 19353ef2..466b4bc1 100644 --- a/app/src/main/res/layout/activity_photo.xml +++ b/app/src/main/res/layout/activity_photo.xml @@ -16,30 +16,36 @@ - + android:layout_height="match_parent"> + + + + + app:fabSize="normal" + app:srcCompat="@drawable/ic_photo_camera" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_register.xml b/app/src/main/res/layout/activity_register.xml index c1c500b8..0085825d 100644 --- a/app/src/main/res/layout/activity_register.xml +++ b/app/src/main/res/layout/activity_register.xml @@ -1,5 +1,5 @@ - - - - + android:layout_height="wrap_content" + android:layout_marginStart="40dp" + android:layout_marginEnd="40dp" + android:gravity="center_horizontal" + android:layoutAnimation="@anim/layout_fall_down" + android:orientation="vertical" + tools:layoutAnimation="@null"> + + + + - - + android:layout_height="wrap_content" + android:layout_marginTop="40dp" + android:gravity="center_vertical" + android:orientation="horizontal"> - - - + android:layout_weight="1"> - - - - - - - - - - - - - - - + android:hint="@string/common_phone_input_hint" + android:inputType="textVisiblePassword" + android:maxLines="1" + android:singleLine="true" + app:regexType="mobile" /> + + + + + + - + + + + + - + - + - + - + - + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_setting.xml b/app/src/main/res/layout/activity_setting.xml index d8afd61e..12d54b67 100644 --- a/app/src/main/res/layout/activity_setting.xml +++ b/app/src/main/res/layout/activity_setting.xml @@ -25,8 +25,23 @@ android:id="@+id/sb_setting_update" android:layout_width="match_parent" android:layout_height="wrap_content" - app:bar_leftText="@string/setting_update" - app:bar_rightIcon="@drawable/ico_go_black" /> + app:bar_leftText="@string/setting_update"> + + + + + android:layout_marginStart="15dp" + android:layout_marginEnd="15dp" /> diff --git a/app/src/main/res/layout/activity_splash.xml b/app/src/main/res/layout/activity_splash.xml index cd51ba66..fa04e5f2 100644 --- a/app/src/main/res/layout/activity_splash.xml +++ b/app/src/main/res/layout/activity_splash.xml @@ -1,51 +1,40 @@ - - - + android:layout_marginBottom="40dp" + app:lottie_autoPlay="true" + app:lottie_loop="false" + app:lottie_rawRes="@raw/welcome" /> - - - - + + android:text="@string/app_name" + android:textColor="@color/black60" + android:textSize="18sp" + android:textStyle="bold" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_status.xml b/app/src/main/res/layout/activity_status.xml index efeeed7a..00a41e0c 100644 --- a/app/src/main/res/layout/activity_status.xml +++ b/app/src/main/res/layout/activity_status.xml @@ -12,16 +12,25 @@ android:layout_height="wrap_content" app:title="界面状态案例" /> - - + android:layout_height="match_parent"> - + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_web.xml b/app/src/main/res/layout/activity_web.xml deleted file mode 100644 index ea48dc69..00000000 --- a/app/src/main/res/layout/activity_web.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_address.xml b/app/src/main/res/layout/dialog_address.xml index 83632cd8..af2c5a69 100644 --- a/app/src/main/res/layout/dialog_address.xml +++ b/app/src/main/res/layout/dialog_address.xml @@ -7,7 +7,7 @@ android:layout_gravity="bottom" android:background="@drawable/bg_dialog_rounded_corner" android:orientation="vertical" - tools:context="com.hjq.demo.ui.dialog.AddressDialog"> + tools:context=".ui.dialog.AddressDialog"> - - - - - - - - - - - + android:layout_height="match_parent" /> \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_album.xml b/app/src/main/res/layout/dialog_album.xml index 3ad58301..3d9258f4 100644 --- a/app/src/main/res/layout/dialog_album.xml +++ b/app/src/main/res/layout/dialog_album.xml @@ -7,9 +7,8 @@ android:layout_height="wrap_content" android:layout_gravity="bottom" android:background="@drawable/bg_dialog_rounded_corner" - android:layoutAnimation="@anim/layout_animation_from_bottom" + android:layoutAnimation="@anim/layout_from_bottom" android:orientation="horizontal" - android:overScrollMode="never" tools:context=".ui.dialog.AlbumDialog" tools:itemCount="4" tools:layoutAnimation="@null" diff --git a/app/src/main/res/layout/dialog_custom.xml b/app/src/main/res/layout/dialog_custom.xml index ee5e765b..380da607 100644 --- a/app/src/main/res/layout/dialog_custom.xml +++ b/app/src/main/res/layout/dialog_custom.xml @@ -5,16 +5,18 @@ android:layout_gravity="center"> \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_date.xml b/app/src/main/res/layout/dialog_date.xml index 4c4414a0..bad590ba 100644 --- a/app/src/main/res/layout/dialog_date.xml +++ b/app/src/main/res/layout/dialog_date.xml @@ -4,96 +4,35 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="bottom" - android:background="@drawable/bg_dialog_rounded_corner" - android:orientation="vertical" - tools:context="com.hjq.demo.ui.dialog.DateDialog"> - - + + - - - - - - - - + android:layout_weight="1.2" + tools:itemCount="3" + tools:listitem="@layout/item_picker" /> - - - - - - - + android:layout_weight="1" + tools:itemCount="3" + tools:listitem="@layout/item_picker" /> - - - + - + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_input.xml b/app/src/main/res/layout/dialog_input.xml index 54a0fb7a..6ffbe4b9 100644 --- a/app/src/main/res/layout/dialog_input.xml +++ b/app/src/main/res/layout/dialog_input.xml @@ -1,91 +1,21 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + android:layout_gravity="center_horizontal" + android:layout_marginStart="15dp" + android:layout_marginTop="15dp" + android:layout_marginEnd="15dp" + android:layout_marginBottom="10dp" + android:background="@drawable/bg_dialog_input" + android:paddingStart="10dp" + android:paddingTop="5dp" + android:paddingEnd="10dp" + android:paddingBottom="5dp" + android:singleLine="true" + android:textColor="#333333" + android:textSize="14sp" + tools:context=".ui.dialog.InputDialog" /> \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_menu.xml b/app/src/main/res/layout/dialog_menu.xml index 75b07392..2a4ec651 100644 --- a/app/src/main/res/layout/dialog_menu.xml +++ b/app/src/main/res/layout/dialog_menu.xml @@ -6,30 +6,31 @@ android:layout_height="wrap_content" android:layout_gravity="center_horizontal|bottom" android:orientation="vertical" - tools:context="com.hjq.demo.ui.dialog.MenuDialog"> + tools:context=".ui.dialog.MenuDialog"> - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + android:layout_marginStart="15dp" + android:layout_marginTop="10dp" + android:layout_marginEnd="15dp" + android:layout_marginBottom="10dp" + android:gravity="center" + android:lineSpacingExtra="4dp" + android:textColor="#333333" + android:textSize="14sp" + tools:context=".ui.dialog.MessageDialog" + tools:text="内容" /> \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_pay_password.xml b/app/src/main/res/layout/dialog_pay_password.xml index 8d701ff9..e58efd8a 100644 --- a/app/src/main/res/layout/dialog_pay_password.xml +++ b/app/src/main/res/layout/dialog_pay_password.xml @@ -18,8 +18,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical" - android:layout_marginLeft="40dp" - android:layout_marginRight="40dp" + android:layout_marginStart="40dp" + android:layout_marginEnd="40dp" android:gravity="center" android:text="@string/pay_title" android:textColor="#333333" @@ -75,9 +75,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorLine" - android:overScrollMode="never" tools:itemCount="9" - tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager" + tools:layoutManager="GridLayoutManager" tools:listitem="@layout/item_pay_password_normal" tools:spanCount="3" /> diff --git a/app/src/main/res/layout/dialog_select.xml b/app/src/main/res/layout/dialog_select.xml new file mode 100644 index 00000000..e5bb00d1 --- /dev/null +++ b/app/src/main/res/layout/dialog_select.xml @@ -0,0 +1,11 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_share.xml b/app/src/main/res/layout/dialog_share.xml index 098b1a37..5d2a6274 100644 --- a/app/src/main/res/layout/dialog_share.xml +++ b/app/src/main/res/layout/dialog_share.xml @@ -12,9 +12,9 @@ diff --git a/app/src/main/res/layout/dialog_time.xml b/app/src/main/res/layout/dialog_time.xml index 0372b8eb..4397ba04 100644 --- a/app/src/main/res/layout/dialog_time.xml +++ b/app/src/main/res/layout/dialog_time.xml @@ -1,104 +1,38 @@ - - - + + - - - - - - - - - - - - + android:layout_weight="1" + tools:itemCount="3" + tools:listitem="@layout/item_picker" /> - - - - - - - - - - + - + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_toast.xml b/app/src/main/res/layout/dialog_toast.xml index 772d967a..e3611cd4 100644 --- a/app/src/main/res/layout/dialog_toast.xml +++ b/app/src/main/res/layout/dialog_toast.xml @@ -7,10 +7,10 @@ android:layout_gravity="center" android:gravity="center" android:orientation="vertical" - app:cardBackgroundColor="#dc000000" + app:cardBackgroundColor="@color/black85" app:cardCornerRadius="15dp" app:cardElevation="0px" - tools:context="com.hjq.demo.ui.dialog.ToastDialog"> + tools:context=".ui.dialog.ToastDialog"> + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_update.xml b/app/src/main/res/layout/dialog_update.xml index c19120e5..d26194b1 100644 --- a/app/src/main/res/layout/dialog_update.xml +++ b/app/src/main/res/layout/dialog_update.xml @@ -1,116 +1,137 @@ - - - + + + + + + + + + + + + + + + android:orientation="vertical"> + android:layout_marginStart="20dp" + android:layout_marginEnd="20dp" + android:text="@string/update_content" + android:textColor="@color/black" + android:textSize="16sp" + android:textStyle="bold" /> - - + android:layout_marginStart="20dp" + android:layout_marginTop="10dp" + android:layout_marginEnd="20dp" + android:layout_marginBottom="10dp" + android:lineSpacingExtra="5dp" + android:minLines="3" + android:textColor="@color/black60" + android:textSize="15sp" + tools:text="6666" /> - - - - - + - + - + - + + - + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_wait.xml b/app/src/main/res/layout/dialog_wait.xml index edf36b1d..e038402a 100644 --- a/app/src/main/res/layout/dialog_wait.xml +++ b/app/src/main/res/layout/dialog_wait.xml @@ -7,7 +7,7 @@ android:layout_gravity="center" android:gravity="center" android:orientation="vertical" - app:cardBackgroundColor="#dc000000" + app:cardBackgroundColor="@color/black85" app:cardCornerRadius="15dp" app:cardElevation="0px" tools:context=".ui.dialog.WaitDialog"> @@ -22,7 +22,6 @@ android:padding="10dp"> @@ -35,7 +34,7 @@ android:id="@+id/t_test_title" android:layout_width="match_parent" android:layout_height="?android:attr/actionBarSize" - android:layout_marginRight="20dp" + android:layout_marginEnd="20dp" app:layout_collapseMode="pin" /> + + @@ -81,6 +88,7 @@ + diff --git a/app/src/main/res/layout/fragment_test_b.xml b/app/src/main/res/layout/fragment_test_b.xml index 42f5ac0a..b68e873d 100644 --- a/app/src/main/res/layout/fragment_test_b.xml +++ b/app/src/main/res/layout/fragment_test_b.xml @@ -25,43 +25,35 @@ - - - - - + android:layout_height="90dp" + android:scaleType="centerCrop" + android:src="@drawable/bg_launcher" /> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="20dp" + app:cardCornerRadius="10dp" + app:cardElevation="0px"> - @@ -104,7 +96,7 @@ style="@style/RectButtonStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginLeft="10dp" + android:layout_marginStart="10dp" android:text="不带圆角的" /> @@ -126,8 +118,8 @@ @@ -140,6 +132,37 @@ + + + + + + + + + + @@ -171,9 +194,9 @@ @@ -191,18 +214,18 @@ style="@style/EditTextStyle" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="40dp" + android:layout_marginStart="40dp" android:layout_marginTop="20dp" - android:layout_marginRight="40dp" + android:layout_marginEnd="40dp" android:text="这是一个带清除的按钮的EditText" /> @@ -211,9 +234,9 @@ style="@style/EditTextStyle" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="40dp" + android:layout_marginStart="40dp" android:layout_marginTop="20dp" - android:layout_marginRight="40dp" + android:layout_marginEnd="40dp" android:hint="这里只能输入手机号(只能以 1 开头)" app:regexType="mobile" /> @@ -221,8 +244,8 @@ style="@style/EditTextStyle" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="40dp" - android:layout_marginRight="40dp" + android:layout_marginStart="40dp" + android:layout_marginEnd="40dp" android:hint="这里只能输入中文(普通的中文字符)" app:regexType="chinese" /> @@ -230,8 +253,8 @@ style="@style/EditTextStyle" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="40dp" - android:layout_marginRight="40dp" + android:layout_marginStart="40dp" + android:layout_marginEnd="40dp" android:hint="这里只能输入英文(大写和小写的英文)" app:regexType="english" /> @@ -239,8 +262,8 @@ style="@style/EditTextStyle" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="40dp" - android:layout_marginRight="40dp" + android:layout_marginStart="40dp" + android:layout_marginEnd="40dp" android:hint="这里只能输入数量(非 0 开头的数字)" app:regexType="count" /> @@ -248,8 +271,8 @@ style="@style/EditTextStyle" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="40dp" - android:layout_marginRight="40dp" + android:layout_marginStart="40dp" + android:layout_marginEnd="40dp" android:hint="这里只能输入用户名(中文、英文、数字)" app:regexType="name" /> @@ -257,8 +280,8 @@ style="@style/EditTextStyle" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="40dp" - android:layout_marginRight="40dp" + android:layout_marginStart="40dp" + android:layout_marginEnd="40dp" android:hint="这里只能输入非空格的字符" app:regexType="nonnull" /> @@ -267,7 +290,7 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="20dp" - android:layout_marginRight="10dp" + android:layout_marginEnd="10dp" android:layout_marginBottom="10dp" android:text="这是一条华丽的分割线" /> diff --git a/app/src/main/res/layout/fragment_test_c.xml b/app/src/main/res/layout/fragment_test_c.xml index b716c276..e7547a24 100644 --- a/app/src/main/res/layout/fragment_test_c.xml +++ b/app/src/main/res/layout/fragment_test_c.xml @@ -20,8 +20,8 @@ + + + + @@ -52,10 +52,10 @@ android:id="@+id/rb_album_check" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginLeft="10dp" - android:layout_marginRight="10dp" + android:layout_marginStart="10dp" + android:layout_marginEnd="10dp" android:background="@null" - android:button="@drawable/selector_checkbox" + android:button="@drawable/selector_radiobutton" android:clickable="false" android:focusable="false" android:focusableInTouchMode="false" /> diff --git a/app/src/main/res/layout/item_copy.xml b/app/src/main/res/layout/item_copy.xml index 78365c5c..d2c8c58c 100644 --- a/app/src/main/res/layout/item_copy.xml +++ b/app/src/main/res/layout/item_copy.xml @@ -3,7 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="match_parent" android:orientation="horizontal" tools:context=".ui.adapter.CopyAdapter"> diff --git a/app/src/main/res/layout/item_menu.xml b/app/src/main/res/layout/item_menu.xml index aa36756f..68355585 100644 --- a/app/src/main/res/layout/item_menu.xml +++ b/app/src/main/res/layout/item_menu.xml @@ -7,14 +7,14 @@ tools:context=".ui.dialog.MenuDialog"> \ No newline at end of file diff --git a/app/src/main/res/layout/item_photo.xml b/app/src/main/res/layout/item_photo.xml index 233cb2cb..517dfb9a 100644 --- a/app/src/main/res/layout/item_photo.xml +++ b/app/src/main/res/layout/item_photo.xml @@ -19,10 +19,10 @@ android:id="@+id/fl_photo_check" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="right" - android:paddingLeft="20dp" + android:layout_gravity="end" + android:paddingStart="20dp" android:paddingTop="3dp" - android:paddingRight="3dp" + android:paddingEnd="3dp" android:paddingBottom="20dp"> + \ No newline at end of file diff --git a/app/src/main/res/layout/item_select.xml b/app/src/main/res/layout/item_select.xml new file mode 100644 index 00000000..534e0206 --- /dev/null +++ b/app/src/main/res/layout/item_select.xml @@ -0,0 +1,37 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_status.xml b/app/src/main/res/layout/item_status.xml new file mode 100644 index 00000000..1565f461 --- /dev/null +++ b/app/src/main/res/layout/item_status.xml @@ -0,0 +1,15 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/popup_copy.xml b/app/src/main/res/layout/popup_copy.xml new file mode 100644 index 00000000..6f1e88f0 --- /dev/null +++ b/app/src/main/res/layout/popup_copy.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/popup_menu.xml b/app/src/main/res/layout/popup_menu.xml new file mode 100644 index 00000000..079d7931 --- /dev/null +++ b/app/src/main/res/layout/popup_menu.xml @@ -0,0 +1,13 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/widget_hint_layout.xml b/app/src/main/res/layout/widget_hint_layout.xml new file mode 100644 index 00000000..bdf41b34 --- /dev/null +++ b/app/src/main/res/layout/widget_hint_layout.xml @@ -0,0 +1,33 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-xhdpi/icon_finish.png b/app/src/main/res/mipmap-xhdpi/icon_finish.png new file mode 100644 index 00000000..7c233766 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/icon_finish.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/icon_finish.png b/app/src/main/res/mipmap-xxhdpi/icon_finish.png new file mode 100644 index 00000000..d5452d0a Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/icon_finish.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/icon_finish.png b/app/src/main/res/mipmap-xxxhdpi/icon_finish.png new file mode 100644 index 00000000..92d2d384 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/icon_finish.png differ diff --git a/app/src/main/res/raw/loading.json b/app/src/main/res/raw/loading.json new file mode 100644 index 00000000..aa677872 --- /dev/null +++ b/app/src/main/res/raw/loading.json @@ -0,0 +1 @@ +{"v":"5.5.2","fr":30,"ip":0,"op":30,"w":500,"h":500,"nm":"合成 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"形状图层 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[407,215,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":0,"s":[48,48]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":20,"s":[48,48]},{"t":30,"s":[72,72]}],"ix":2,"x":"var $bm_rt;\nvar amp, freq, decay, n, n, t, t, v;\namp = 0.1;\nfreq = 2;\ndecay = 2;\n$bm_rt = n = 0;\nif (numKeys > 0) {\n $bm_rt = n = nearestKey(time).index;\n if (key(n).time > time) {\n n--;\n }\n}\nif (n == 0) {\n $bm_rt = t = 0;\n} else {\n $bm_rt = t = $bm_sub(time, key(n).time);\n}\nif (n > 0) {\n v = velocityAtTime($bm_sub(key(n).time, $bm_div(thisComp.frameDuration, 10)));\n $bm_rt = $bm_sum(value, $bm_div($bm_mul($bm_mul(v, amp), Math.sin($bm_mul($bm_mul($bm_mul(freq, t), 2), Math.PI))), Math.exp($bm_mul(decay, t))));\n} else {\n $bm_rt = value;\n}"},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0.768627464771,0.784313738346,0.800000011921,1]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":20,"s":[0.768627464771,0.784313738346,0.800000011921,1]},{"t":30,"s":[0.388235300779,0.403921574354,0.419607847929,1]}],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-28,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":150,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"形状图层 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[279,215,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":0,"s":[48,48]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":10,"s":[48,48]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":20,"s":[72,72]},{"t":30,"s":[48,48]}],"ix":2,"x":"var $bm_rt;\nvar amp, freq, decay, n, n, t, t, v;\namp = 0.1;\nfreq = 2;\ndecay = 2;\n$bm_rt = n = 0;\nif (numKeys > 0) {\n $bm_rt = n = nearestKey(time).index;\n if (key(n).time > time) {\n n--;\n }\n}\nif (n == 0) {\n $bm_rt = t = 0;\n} else {\n $bm_rt = t = $bm_sub(time, key(n).time);\n}\nif (n > 0) {\n v = velocityAtTime($bm_sub(key(n).time, $bm_div(thisComp.frameDuration, 10)));\n $bm_rt = $bm_sum(value, $bm_div($bm_mul($bm_mul(v, amp), Math.sin($bm_mul($bm_mul($bm_mul(freq, t), 2), Math.PI))), Math.exp($bm_mul(decay, t))));\n} else {\n $bm_rt = value;\n}"},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0.768627464771,0.784313738346,0.800000011921,1]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":20,"s":[0.388235300779,0.403921574354,0.419607847929,1]},{"t":30,"s":[0.768627464771,0.784313738346,0.800000011921,1]}],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-28,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":150,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"形状图层 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[151,215,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":0,"s":[48,48]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":10,"s":[72,72]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":20,"s":[48,48]},{"t":30,"s":[48,48]}],"ix":2,"x":"var $bm_rt;\nvar amp, freq, decay, n, n, t, t, v;\namp = 0.1;\nfreq = 2;\ndecay = 2;\n$bm_rt = n = 0;\nif (numKeys > 0) {\n $bm_rt = n = nearestKey(time).index;\n if (key(n).time > time) {\n n--;\n }\n}\nif (n == 0) {\n $bm_rt = t = 0;\n} else {\n $bm_rt = t = $bm_sub(time, key(n).time);\n}\nif (n > 0) {\n v = velocityAtTime($bm_sub(key(n).time, $bm_div(thisComp.frameDuration, 10)));\n $bm_rt = $bm_sum(value, $bm_div($bm_mul($bm_mul(v, amp), Math.sin($bm_mul($bm_mul($bm_mul(freq, t), 2), Math.PI))), Math.exp($bm_mul(decay, t))));\n} else {\n $bm_rt = value;\n}"},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0.768627464771,0.784313738346,0.800000011921,1]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[0.388235300779,0.403921574354,0.419607847929,1]},{"t":20,"s":[0.768627464771,0.784313738346,0.800000011921,1]}],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-28,36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":150,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/app/src/main/assets/province.json b/app/src/main/res/raw/province.json similarity index 100% rename from app/src/main/assets/province.json rename to app/src/main/res/raw/province.json diff --git a/app/src/main/res/raw/welcome.json b/app/src/main/res/raw/welcome.json new file mode 100644 index 00000000..0347258f --- /dev/null +++ b/app/src/main/res/raw/welcome.json @@ -0,0 +1 @@ +{"v":"5.5.2","fr":30,"ip":0,"op":136,"w":976,"h":671,"nm":"Hi Animation","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"h path 2 Outlines 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[987.04,469.648,0],"ix":2},"a":{"a":0,"k":[154.5,299.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,54.658]],"v":[[-99.143,-243.983],[-99.574,119.373],[-98.794,145.015]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.207843137255,0.517647058824,0.964705882353,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[154.325,299.75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.24],"y":[1]},"o":{"x":[0.76],"y":[0]},"t":0,"s":[96]},{"i":{"x":[0.36],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":41,"s":[0]},{"i":{"x":[0.627],"y":[-2.176]},"o":{"x":[0.289],"y":[0]},"t":60,"s":[32]},{"i":{"x":[0.693],"y":[0.768]},"o":{"x":[0.281],"y":[-0.012]},"t":97,"s":[32.748]},{"t":105,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.24],"y":[1]},"o":{"x":[0.76],"y":[0]},"t":0,"s":[98]},{"i":{"x":[0.36],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":41,"s":[55.4]},{"i":{"x":[0.627],"y":[-2.176]},"o":{"x":[0.289],"y":[0]},"t":60,"s":[94.4]},{"i":{"x":[0.693],"y":[0.768]},"o":{"x":[0.281],"y":[-0.012]},"t":97,"s":[96.608]},{"t":105,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":1950,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"h path 2 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[749.5,449.5,0],"ix":2},"a":{"a":0,"k":[154.5,299.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-54.658,0],[0,54.658]],"o":[[0,0],[0,0],[0,54.658],[54.66,0],[0,0]],"v":[[-99.143,-243.983],[-99.143,142.703],[-98.794,145.015],[0.174,243.983],[99.143,145.015]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.996078431373,0.733333333333,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[154.325,299.75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.24],"y":[1]},"o":{"x":[0.76],"y":[0]},"t":0,"s":[96]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.64],"y":[0]},"t":60,"s":[0]},{"t":97,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.24],"y":[1]},"o":{"x":[0.76],"y":[0]},"t":0,"s":[98]},{"i":{"x":[0.675],"y":[-2.679]},"o":{"x":[0.342],"y":[0]},"t":60,"s":[55.4]},{"i":{"x":[0.675],"y":[0.696]},"o":{"x":[0.37],"y":[-0.022]},"t":97,"s":[56.806]},{"t":105,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":1950,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"h path 1 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[749.5,447.5,0],"ix":2},"a":{"a":0,"k":[154.5,202.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[54.659,0],[0,-54.658],[0,0]],"o":[[0,0],[0,0],[0,-54.658],[-54.658,0],[0,0],[0,0]],"v":[[98.969,147.132],[98.969,51.627],[98.969,51.868],[-0.001,-47.101],[-98.969,51.868],[-98.969,-147.132]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.996078431373,0.733333333333,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[154.5,202.632],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.74],"y":[1]},"o":{"x":[0.4],"y":[0.8]},"t":0,"s":[68]},{"i":{"x":[0.48],"y":[1]},"o":{"x":[0.114],"y":[0.292]},"t":15,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.26],"y":[0]},"t":64,"s":[0]},{"t":97,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.74],"y":[1]},"o":{"x":[0.4],"y":[0.8]},"t":0,"s":[65]},{"i":{"x":[0.48],"y":[1]},"o":{"x":[0.114],"y":[0.399]},"t":15,"s":[100]},{"i":{"x":[0.657],"y":[-9.391]},"o":{"x":[0.323],"y":[0]},"t":64,"s":[67.4]},{"i":{"x":[0.683],"y":[0.716]},"o":{"x":[0.281],"y":[-0.009]},"t":97,"s":[67.703]},{"t":105,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":1950,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Hi files","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[473.436,364,0],"ix":2},"a":{"a":0,"k":[750,500,0],"ix":1},"s":{"a":0,"k":[108,108,100],"ix":6}},"ao":0,"w":1500,"h":1000,"ip":29,"op":136,"st":29,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.46,"y":1},"o":{"x":0.001,"y":0.504},"t":3,"s":[498.378,416.11,0],"to":[0,7.167,0],"ti":[0,0,0]},{"i":{"x":0.999,"y":0.609},"o":{"x":0.54,"y":0},"t":12,"s":[498.378,459.11,0],"to":[0,0,0],"ti":[1.844,44.19,0]},{"i":{"x":0.16,"y":1},"o":{"x":0.54,"y":0},"t":19,"s":[498.378,416.11,0],"to":[19.917,-49.917,0],"ti":[-20.594,42.719,0]},{"t":35,"s":[621.94,159.798,0]}],"ix":2},"a":{"a":0,"k":[-144.56,-34.931,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[1.335,1.335,0]},"t":21,"s":[55.407,55.407,100]},{"t":35,"s":[10.1,10.1,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[355.037,355.037],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.845751594095,0.012161032359,0.012161032359,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-144.56,-34.931],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[62.211,62.211],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":130,"st":3,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.46,"y":1},"o":{"x":0.001,"y":0.504},"t":4,"s":[521.864,548.108,0],"to":[0,9,0],"ti":[0,0,0]},{"i":{"x":0.999,"y":0.608},"o":{"x":0.54,"y":0},"t":13,"s":[521.864,602.108,0],"to":[0,0,0],"ti":[2.053,34.822,0]},{"i":{"x":0.16,"y":1},"o":{"x":0.301,"y":1},"t":20,"s":[521.864,548.108,0],"to":[9.4,-19.933,0],"ti":[-9.4,10.933,0]},{"t":35,"s":[578.264,482.508,0]}],"ix":2},"a":{"a":0,"k":[-144.56,-34.931,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.16,0.16,0.16],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[1.4,1.4,0]},"t":21,"s":[53,53,100]},{"t":35,"s":[11,11,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[355.037,355.037],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.996078431373,0.733333333333,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-144.56,-34.931],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[62.211,62.211],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":36,"st":4,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.999,"y":0.496},"o":{"x":0.001,"y":0.504},"t":6,"s":[575.274,318.062,0],"to":[0,6.833,0],"ti":[0,0,0]},{"i":{"x":0.999,"y":0.619},"o":{"x":0.54,"y":0},"t":15,"s":[575.274,359.062,0],"to":[0,0,0],"ti":[-9.167,15.167,0]},{"i":{"x":0.16,"y":1},"o":{"x":0.27,"y":1},"t":22,"s":[575.274,318.062,0],"to":[9.167,-15.167,0],"ti":[-9.167,8.333,0]},{"t":35,"s":[630.274,268.062,0]}],"ix":2},"a":{"a":0,"k":[-144.56,-34.931,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.16,0.16,0.16],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[1.4,1.4,0]},"t":21,"s":[35,35,100]},{"t":35,"s":[12.2,12.2,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[355.037,355.037],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.182193397073,0.579084927428,0.000757008265,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-144.56,-34.931],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[62.211,62.211],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":36,"st":6,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.46,"y":1},"o":{"x":0.001,"y":0.56},"t":0,"s":[324.44,338.069,0],"to":[0,5.167,0],"ti":[-0.191,0.573,0]},{"i":{"x":0.999,"y":0.496},"o":{"x":0.54,"y":0},"t":10,"s":[324.44,369.069,0],"to":[0.191,-0.573,0],"ti":[-1.576,28.726,0]},{"i":{"x":0.999,"y":0.953},"o":{"x":0.54,"y":0},"t":20,"s":[325.585,334.633,0],"to":[49.678,18.231,0],"ti":[-49.487,-23.97,0]},{"t":29,"s":[622.507,478.452,0]}],"ix":2},"a":{"a":0,"k":[-144.56,-34.931,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.16,0.16,0.16],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[1.4,1.4,0]},"t":21,"s":[100,100,100]},{"t":35,"s":[10.5,10.5,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[355.037,355.037],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.517647058824,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-144.56,-34.931],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[62.211,62.211],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":36,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 9111032d..fdba1e60 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -1,35 +1,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 125a5df7..4cf90abf 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -3,11 +3,13 @@ 1px + + + 999dp + 5dp - - 15dp - - 12dp + + 15dp \ No newline at end of file diff --git a/app/src/main/res/values/integers.xml b/app/src/main/res/values/integers.xml index 50f1d2d3..5baad8d5 100644 --- a/app/src/main/res/values/integers.xml +++ b/app/src/main/res/values/integers.xml @@ -2,4 +2,7 @@ 200 + + + 4 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 45fd2861..30473e83 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -31,6 +31,24 @@ + 当前没有网络连接,请检查网络设置 + + + 加载中… + + 请求出错,未知错误 + 账号异常,请重新登录 + 数据解析异常,请稍后 + 服务器请求超时,请稍后再试 + 请求失败,请检查网络设置 + 服务器连接异常,请稍后再试 + 请求被中断,请重试 + + + 空空如也 + 请求出错,点击重试 + 网络错误,点击重试 + 请选择地区 请选择 @@ -42,16 +60,19 @@ 请选择时间 - 升级 + 发现新版本 + + 更新内容 + + 下次再说 + 立即更新 + 后台更新 - 启动下载失败 必须先要授予权限才能正常下载更新哦 - 下载中 + 下载中 %d%% 立即安装 下载失败 - 暂停中 - 等待下载 当前已是最新版本 @@ -69,10 +90,9 @@ 请输入支付密码 - - 亲,暂无数据 - 亲,请求出错了 - 亲,没有网络了 + + 至少要选择 %d 项 + 最多只能选择 %d 项 再按一次退出 @@ -91,7 +111,6 @@ 注册 手机号仅用于登录和保护账号安全 - 登录 设置6–18位登录密码 请再次输入一次密码 两次密码输入不一致,请重新输入 @@ -114,7 +133,7 @@ 关于我们 Android 轮子哥 - Copyright © 2018 – 2019 + Copyright © 2018 – 2020 忘记密码 @@ -158,7 +177,7 @@ 发生意外错误\n抱歉,给您带来不便 重新启动 - 错误日志 + 查看日志 错误详情 错误信息 diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index a3c4bc99..3bf48d3b 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -3,37 +3,33 @@ - - - @@ -79,8 +75,8 @@ wrap_content wrap_content @drawable/selector_countdown - 15dp - 15dp + 15dp + 15dp 10dp 10dp 90dp @@ -90,11 +86,11 @@ 12sp - - \ No newline at end of file diff --git a/base/build.gradle b/base/build.gradle index 7d3721a2..a397a722 100644 --- a/base/build.gradle +++ b/base/build.gradle @@ -1,17 +1,10 @@ apply plugin: 'com.android.library' +apply from: "../config.gradle" android { - compileSdkVersion rootProject.ext.compileVersion defaultConfig { - minSdkVersion 14 - targetSdkVersion rootProject.ext.targetVersion // 混淆配置 consumerProguardFiles 'proguard-base.pro' } -} - -dependencies { - implementation "androidx.appcompat:appcompat:$rootProject.ext.appcompatVersion" - implementation "com.google.android.material:material:$rootProject.ext.materialVersion" } \ No newline at end of file diff --git a/base/src/main/java/com/hjq/base/BaseActivity.java b/base/src/main/java/com/hjq/base/BaseActivity.java index 6ad22e99..94887af2 100644 --- a/base/src/main/java/com/hjq/base/BaseActivity.java +++ b/base/src/main/java/com/hjq/base/BaseActivity.java @@ -4,9 +4,6 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.SystemClock; import android.view.View; import android.view.ViewGroup; import android.view.Window; @@ -15,6 +12,10 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; +import com.hjq.base.action.BundleAction; +import com.hjq.base.action.ClickAction; +import com.hjq.base.action.HandlerAction; + import java.util.Random; /** @@ -23,10 +24,8 @@ * time : 2018/10/18 * desc : Activity 基类 */ -public abstract class BaseActivity extends AppCompatActivity { - - private static final Handler HANDLER = new Handler(Looper.getMainLooper()); - public final Object mHandlerToken = hashCode(); +public abstract class BaseActivity extends AppCompatActivity + implements HandlerAction, ClickAction, BundleAction { @Override protected void onCreate(Bundle savedInstanceState) { @@ -40,29 +39,6 @@ protected void initActivity() { initData(); } - /** - * 初始化布局 - */ - protected void initLayout() { - if (getLayoutId() > 0) { - setContentView(getLayoutId()); - initSoftKeyboard(); - } - } - - /** - * 初始化软键盘 - */ - protected void initSoftKeyboard() { - // 点击外部隐藏软键盘,提升用户体验 - getContentView().setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - hideSoftKeyboard(); - } - }); - } - /** * 获取布局 ID */ @@ -78,44 +54,36 @@ public void onClick(View v) { */ protected abstract void initData(); - @Override - public void finish() { - hideSoftKeyboard(); - super.finish(); - } - /** - * 延迟执行 - */ - public final boolean post(Runnable r) { - return postDelayed(r, 0); - } - - /** - * 延迟一段时间执行 + * 初始化布局 */ - public final boolean postDelayed(Runnable r, long delayMillis) { - if (delayMillis < 0) { - delayMillis = 0; + protected void initLayout() { + if (getLayoutId() > 0) { + setContentView(getLayoutId()); + initSoftKeyboard(); } - return postAtTime(r, SystemClock.uptimeMillis() + delayMillis); } /** - * 在指定的时间执行 + * 初始化软键盘 */ - public final boolean postAtTime(Runnable r, long uptimeMillis) { - // 发送和这个 Activity 相关的消息回调 - return HANDLER.postAtTime(r, mHandlerToken, uptimeMillis); + protected void initSoftKeyboard() { + // 点击外部隐藏软键盘,提升用户体验 + getContentView().setOnClickListener(v -> hideSoftKeyboard()); } @Override protected void onDestroy() { - // 移除和这个 Activity 相关的消息回调 - HANDLER.removeCallbacksAndMessages(mHandlerToken); + removeCallbacks(); super.onDestroy(); } + @Override + public void finish() { + hideSoftKeyboard(); + super.finish(); + } + /** * 如果当前的 Activity(singleTop 启动模式) 被复用时会回调 */ @@ -126,10 +94,15 @@ protected void onNewIntent(Intent intent) { setIntent(intent); } + @Override + public Bundle getBundle() { + return getIntent().getExtras(); + } + /** * 获取当前 Activity 对象 */ - public BaseActivity getActivity() { + protected BaseActivity getActivity() { return this; } @@ -141,43 +114,33 @@ public ViewGroup getContentView() { } /** - * startActivity 方法优化 + * startActivity 方法简化 */ - - public void startActivity(Class cls) { - startActivity(new Intent(this, cls)); - } - - public void startActivityFinish(Class cls) { - startActivityFinish(new Intent(this, cls)); - } - - public void startActivityFinish(Intent intent) { - startActivity(intent); - finish(); + public void startActivity(Class clazz) { + startActivity(new Intent(this, clazz)); } /** * startActivityForResult 方法优化 */ - private ActivityCallback mActivityCallback; + private OnActivityCallback mActivityCallback; private int mActivityRequestCode; - public void startActivityForResult(Class cls, ActivityCallback callback) { - startActivityForResult(new Intent(this, cls), null, callback); + public void startActivityForResult(Class clazz, OnActivityCallback callback) { + startActivityForResult(new Intent(this, clazz), null, callback); } - public void startActivityForResult(Intent intent, ActivityCallback callback) { + public void startActivityForResult(Intent intent, OnActivityCallback callback) { startActivityForResult(intent, null, callback); } - public void startActivityForResult(Intent intent, @Nullable Bundle options, ActivityCallback callback) { + public void startActivityForResult(Intent intent, @Nullable Bundle options, OnActivityCallback callback) { // 回调还没有结束,所以不能再次调用此方法,这个方法只适合一对一回调,其他需求请使用原生的方法实现 if (mActivityCallback == null) { mActivityCallback = callback; - // 随机生成请求码,这个请求码在 0 - 255 之间 - mActivityRequestCode = new Random().nextInt(255); + // 随机生成请求码,这个请求码必须在 2 的 16 次幂以内,也就是 0 - 65535 + mActivityRequestCode = new Random().nextInt((int) Math.pow(2, 16)); startActivityForResult(intent, mActivityRequestCode, options); } } @@ -192,53 +155,11 @@ protected void onActivityResult(int requestCode, int resultCode, @Nullable Inten } } - /** - * 处理 Activity 多重跳转:https://www.jianshu.com/p/579f1f118161 - */ - @Override public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) { - if (startActivitySelfCheck(intent)) { - hideSoftKeyboard(); - // 查看源码得知 startActivity 最终也会调用 startActivityForResult - super.startActivityForResult(intent, requestCode, options); - } - } - - private String mStartActivityTag; - private long mStartActivityTime; - - /** - * 检查当前 Activity 是否重复跳转了,不需要检查则重写此方法并返回 true 即可 - * - * @param intent 用于跳转的 Intent 对象 - * @return 检查通过返回true, 检查不通过返回false - */ - protected boolean startActivitySelfCheck(Intent intent) { - // 默认检查通过 - boolean result = true; - // 标记对象 - String tag; - if (intent.getComponent() != null) { - // 显式跳转 - tag = intent.getComponent().getClassName(); - } else if (intent.getAction() != null) { - // 隐式跳转 - tag = intent.getAction(); - } else { - // 其他方式 - return true; - } - - if (tag.equals(mStartActivityTag) && mStartActivityTime >= SystemClock.uptimeMillis() - 500) { - // 检查不通过 - result = false; - } - - // 记录启动标记和时间 - mStartActivityTag = tag; - mStartActivityTime = SystemClock.uptimeMillis(); - return result; + hideSoftKeyboard(); + // 查看源码得知 startActivity 最终也会调用 startActivityForResult + super.startActivityForResult(intent, requestCode, options); } /** @@ -255,10 +176,7 @@ private void hideSoftKeyboard() { } } - /** - * Activity 回调接口 - */ - public interface ActivityCallback { + public interface OnActivityCallback { /** * 结果回调 diff --git a/base/src/main/java/com/hjq/base/BaseRecyclerViewAdapter.java b/base/src/main/java/com/hjq/base/BaseAdapter.java similarity index 77% rename from base/src/main/java/com/hjq/base/BaseRecyclerViewAdapter.java rename to base/src/main/java/com/hjq/base/BaseAdapter.java index 5aa9ef5b..4c4959da 100644 --- a/base/src/main/java/com/hjq/base/BaseRecyclerViewAdapter.java +++ b/base/src/main/java/com/hjq/base/BaseAdapter.java @@ -1,29 +1,26 @@ package com.hjq.base; import android.content.Context; -import android.content.res.Resources; -import android.graphics.drawable.Drawable; import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; -import androidx.annotation.ColorRes; -import androidx.annotation.DrawableRes; import androidx.annotation.IdRes; import androidx.annotation.LayoutRes; import androidx.annotation.NonNull; -import androidx.annotation.StringRes; -import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.hjq.base.action.ContextAction; + /** * author : Android 轮子哥 * github : https://github.com/getActivity/AndroidProject * time : 2018/10/18 * desc : RecyclerView 适配器基类 */ -public abstract class BaseRecyclerViewAdapter extends RecyclerView.Adapter { +public abstract class BaseAdapter + extends RecyclerView.Adapter implements ContextAction { /** 上下文对象 */ private final Context mContext; @@ -43,8 +40,11 @@ public abstract class BaseRecyclerViewAdapter mChildLongClickListeners; - public BaseRecyclerViewAdapter(Context context) { + public BaseAdapter(Context context) { mContext = context; + if (mContext == null) { + throw new IllegalArgumentException("are you ok?"); + } } @Override @@ -53,58 +53,32 @@ public long getItemId(int position) { } @Override - public void onBindViewHolder(@NonNull VH holder, int position) { + public final void onBindViewHolder(@NonNull VH holder, int position) { + holder.markViewHolderPosition(position); holder.onBindView(position); } /** - * 获取RecyclerView对象,需要在setAdapter之后绑定 + * 获取RecyclerView 对象,需要在setAdapter之后绑定 */ public RecyclerView getRecyclerView() { return mRecyclerView; } - /** - * 获取上下文对象 - */ + @Override public Context getContext() { return mContext; } - /** - * 获取资源对象 - */ - protected Resources getResources() { - return mContext.getResources(); - } - - /** - * 获取资源文本 - */ - public String getString(@StringRes int id) { - return mContext.getString(id); - } - - /** - * 获取资源颜色 - */ - protected int getColor(@ColorRes int id) { - return ContextCompat.getColor(mContext, id); - } - - /** - * 获取资源图像 - */ - protected Drawable getDrawable(@DrawableRes int id) { - return ContextCompat.getDrawable(mContext, id); - } - /** * 条目 ViewHolder,需要子类 ViewHolder 继承 */ public abstract class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { + /** 当前 ViewHolder 位置 */ + private int mViewHolderPosition; + public ViewHolder(@LayoutRes int id) { this(LayoutInflater.from(getContext()).inflate(id, getRecyclerView(), false)); } @@ -142,21 +116,31 @@ public ViewHolder(View itemView) { public abstract void onBindView(int position); /** - * {@link View.OnClickListener} + * 记录 ViewHolder 位置 + */ + final void markViewHolderPosition(int position) { + mViewHolderPosition = position; + } + + /** + * 获取 ViewHolder 位置 */ + protected final int getViewHolderPosition() { + return mViewHolderPosition; + } @Override public void onClick(View v) { if (v == getItemView()) { if(mItemClickListener != null) { - mItemClickListener.onItemClick(mRecyclerView, v, getLayoutPosition()); + mItemClickListener.onItemClick(mRecyclerView, v, getViewHolderPosition()); return; } } if (mChildClickListeners != null) { - OnChildClickListener childClickListener = mChildClickListeners.get(v.getId()); - if (childClickListener != null) { - childClickListener.onChildClick(mRecyclerView, v, getLayoutPosition()); + OnChildClickListener listener = mChildClickListeners.get(v.getId()); + if (listener != null) { + listener.onChildClick(mRecyclerView, v, getViewHolderPosition()); } } } @@ -169,19 +153,19 @@ public void onClick(View v) { public boolean onLongClick(View v) { if (v == getItemView()) { if (mItemLongClickListener != null) { - return mItemLongClickListener.onItemLongClick(mRecyclerView, v, getLayoutPosition()); + return mItemLongClickListener.onItemLongClick(mRecyclerView, v, getViewHolderPosition()); } } if (mChildLongClickListeners != null) { - OnChildLongClickListener childClickLongListener = mChildLongClickListeners.get(v.getId()); - if (childClickLongListener != null) { - childClickLongListener.onChildLongClick(mRecyclerView, v, getLayoutPosition()); + OnChildLongClickListener listener = mChildLongClickListeners.get(v.getId()); + if (listener != null) { + listener.onChildLongClick(mRecyclerView, v, getViewHolderPosition()); } } return false; } - public final View getItemView() { + public final View getItemView() { return itemView; } @@ -200,9 +184,9 @@ public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { } //判断当前的布局管理器是否为空,如果为空则设置默认的布局管理器 if (mRecyclerView.getLayoutManager() == null) { - RecyclerView.LayoutManager manager = getDefaultLayoutManager(mContext); - if (manager != null) { - mRecyclerView.setLayoutManager(manager); + RecyclerView.LayoutManager layoutManager = generateDefaultLayoutManager(mContext); + if (layoutManager != null) { + mRecyclerView.setLayoutManager(layoutManager); } } } @@ -217,14 +201,14 @@ public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) { } /** - * 获取默认的布局摆放器 + * 生成默认的布局摆放器 */ - protected RecyclerView.LayoutManager getDefaultLayoutManager(Context context) { + protected RecyclerView.LayoutManager generateDefaultLayoutManager(Context context) { return new LinearLayoutManager(context); } /** - * 设置RecyclerView条目点击监听 + * 设置 RecyclerView 条目点击监听 */ public void setOnItemClickListener(OnItemClickListener listener) { checkRecyclerViewState(); @@ -264,12 +248,12 @@ public void setOnChildLongClickListener(@IdRes int id, OnChildLongClickListener private void checkRecyclerViewState() { if (mRecyclerView != null) { // 必须在 RecyclerView.setAdapter() 之前设置监听 - throw new IllegalStateException("Binding adapters is not allowed before setting listeners"); + throw new IllegalStateException("are you ok?"); } } /** - * 设置RecyclerView条目滚动监听 + * 设置 RecyclerView 条目滚动监听 */ public void setOnScrollingListener(OnScrollingListener listener) { mScrollingListener = listener; @@ -302,10 +286,10 @@ public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newStat if (newState == RecyclerView.SCROLL_STATE_IDLE) { if (!recyclerView.canScrollVertically(1)) { - //是否能向下滚动,false表示已经滚动到底部 + //是否能向下滚动,false 表示已经滚动到底部 mScrollingListener.onScrollDown(recyclerView); } else if (!recyclerView.canScrollVertically(-1)) { - //是否能向上滚动,false表示已经滚动到顶部 + //是否能向上滚动,false 表示已经滚动到顶部 mScrollingListener.onScrollTop(recyclerView); } @@ -324,21 +308,21 @@ public interface OnScrollingListener { /** * 列表滚动到最顶部 * - * @param recyclerView RecyclerView对象 + * @param recyclerView RecyclerView 对象 */ void onScrollTop(RecyclerView recyclerView); /** * 列表滚动中 * - * @param recyclerView RecyclerView对象 + * @param recyclerView RecyclerView 对象 */ void onScrolling(RecyclerView recyclerView); /** * 列表滚动到最底部 * - * @param recyclerView RecyclerView对象 + * @param recyclerView RecyclerView 对象 */ void onScrollDown(RecyclerView recyclerView); } @@ -351,7 +335,7 @@ public interface OnItemClickListener{ /** * 当 RecyclerView 某个条目被点击时回调 * - * @param recyclerView RecyclerView对象 + * @param recyclerView RecyclerView 对象 * @param itemView 被点击的条目对象 * @param position 被点击的条目位置 */ @@ -366,7 +350,7 @@ public interface OnItemLongClickListener { /** * 当 RecyclerView 某个条目被长按时回调 * - * @param recyclerView RecyclerView对象 + * @param recyclerView RecyclerView 对象 * @param itemView 被点击的条目对象 * @param position 被点击的条目位置 * @return 是否拦截事件 @@ -382,7 +366,7 @@ public interface OnChildClickListener { /** * 当 RecyclerView 某个条目 子 View 被点击时回调 * - * @param recyclerView RecyclerView对象 + * @param recyclerView RecyclerView 对象 * @param childView 被点击的条目子 View Id * @param position 被点击的条目位置 */ @@ -397,7 +381,7 @@ public interface OnChildLongClickListener { /** * 当 RecyclerView 某个条目子 View 被长按时回调 * - * @param recyclerView RecyclerView对象 + * @param recyclerView RecyclerView 对象 * @param childView 被点击的条目子 View Id * @param position 被点击的条目位置 */ diff --git a/base/src/main/java/com/hjq/base/BaseDialog.java b/base/src/main/java/com/hjq/base/BaseDialog.java index 40fcef39..e4755952 100644 --- a/base/src/main/java/com/hjq/base/BaseDialog.java +++ b/base/src/main/java/com/hjq/base/BaseDialog.java @@ -1,16 +1,14 @@ package com.hjq.base; import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.Application; import android.content.Context; +import android.content.ContextWrapper; import android.content.DialogInterface; -import android.content.res.Resources; import android.graphics.drawable.Drawable; -import android.os.Build; -import android.os.Handler; -import android.os.Looper; -import android.os.SystemClock; +import android.os.Bundle; import android.util.SparseArray; -import android.util.SparseIntArray; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -23,8 +21,9 @@ import android.widget.LinearLayout; import android.widget.TextView; -import androidx.annotation.ColorRes; +import androidx.annotation.ColorInt; import androidx.annotation.DrawableRes; +import androidx.annotation.FloatRange; import androidx.annotation.IdRes; import androidx.annotation.LayoutRes; import androidx.annotation.NonNull; @@ -34,7 +33,12 @@ import androidx.appcompat.app.AppCompatDialog; import androidx.core.content.ContextCompat; -import java.lang.ref.WeakReference; +import com.hjq.base.action.AnimAction; +import com.hjq.base.action.ClickAction; +import com.hjq.base.action.ContextAction; +import com.hjq.base.action.HandlerAction; + +import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.List; @@ -44,16 +48,8 @@ * time : 2018/11/24 * desc : Dialog 基类 */ -public class BaseDialog extends AppCompatDialog implements - DialogInterface.OnShowListener, - DialogInterface.OnCancelListener, - DialogInterface.OnDismissListener { - - private static final Handler HANDLER = new Handler(Looper.getMainLooper()); - private final Object mHandlerToken = hashCode(); - - /** Dialog 是否可以取消 */ - private boolean mCancelable = true; +public class BaseDialog extends AppCompatDialog implements ContextAction, HandlerAction, ClickAction, + DialogInterface.OnShowListener, DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { private final ListenersWrapper mListeners = new ListenersWrapper<>(this); @@ -69,11 +65,6 @@ public BaseDialog(Context context, int themeResId) { super(context, themeResId); } - @Override - public void setCancelable(boolean flag) { - super.setCancelable(mCancelable = flag); - } - /** * 获取 Dialog 的根布局 */ @@ -81,13 +72,6 @@ public View getContentView() { return findViewById(Window.ID_ANDROID_CONTENT); } - /** - * 是否设置了取消(仅供子类调用) - */ - protected boolean isCancelable() { - return mCancelable; - } - /** * 获取当前设置重心 */ @@ -97,6 +81,7 @@ public int getGravity() { WindowManager.LayoutParams params = window.getAttributes(); return params.gravity; } + return Gravity.NO_GRAVITY; } @@ -144,6 +129,17 @@ public void setWindowAnimations(@StyleRes int id) { } } + /** + * 获取 Dialog 的动画 + */ + public int getWindowAnimations() { + Window window = getWindow(); + if (window != null) { + return window.getAttributes().windowAnimations; + } + return AnimAction.NO_ANIM; + } + /** * 设置背景遮盖层开关 */ @@ -161,49 +157,13 @@ public void setBackgroundDimEnabled(boolean enabled) { /** * 设置背景遮盖层的透明度(前提条件是背景遮盖层开关必须是为开启状态) */ - public void setBackgroundDimAmount(float dimAmount) { + public void setBackgroundDimAmount(@FloatRange(from = 0, to = 1) float dimAmount) { Window window = getWindow(); if (window != null) { window.setDimAmount(dimAmount); } } - /** - * 延迟执行 - */ - public final boolean post(Runnable r) { - return postDelayed(r, 0); - } - - /** - * 延迟一段时间执行 - */ - public final boolean postDelayed(Runnable r, long delayMillis) { - if (delayMillis < 0) { - delayMillis = 0; - } - return postAtTime(r, SystemClock.uptimeMillis() + delayMillis); - } - - /** - * 在指定的时间执行 - */ - public final boolean postAtTime(Runnable r, long uptimeMillis) { - return HANDLER.postAtTime(r, mHandlerToken, uptimeMillis); - } - - @Override - public void hide() { - // 如果当前有 View 获得焦点,就必须将这个对话框 dismiss 掉,否则 Dialog 无法正常显示 - // 复现步骤:创建一个带有 EditText 的对话框,并弹出输入法,点击返回到桌面,然后再回来程序界面 - View view = getCurrentFocus(); - if (view != null) { - dismiss(); - } else { - super.hide(); - } - } - /** * 设置一个显示监听器 * @@ -367,8 +327,8 @@ private void setOnDismissListeners(@Nullable List @Override public void onShow(DialogInterface dialog) { if (mShowListeners != null) { - for (BaseDialog.OnShowListener listener : mShowListeners) { - listener.onShow(this); + for (int i = 0; i < mShowListeners.size(); i++) { + mShowListeners.get(i).onShow(this); } } } @@ -379,8 +339,8 @@ public void onShow(DialogInterface dialog) { @Override public void onCancel(DialogInterface dialog) { if (mCancelListeners != null) { - for (BaseDialog.OnCancelListener listener : mCancelListeners) { - listener.onCancel(this); + for (int i = 0; i < mCancelListeners.size(); i++) { + mCancelListeners.get(i).onCancel(this); } } } @@ -391,50 +351,20 @@ public void onCancel(DialogInterface dialog) { @Override public void onDismiss(DialogInterface dialog) { if (mDismissListeners != null) { - for (BaseDialog.OnDismissListener listener : mDismissListeners) { - listener.onDismiss(this); + for (int i = 0; i < mDismissListeners.size(); i++) { + mDismissListeners.get(i).onDismiss(this); } } - - // 移除和这个 Dialog 相关的消息回调 - HANDLER.removeCallbacksAndMessages(mHandlerToken); } - /** - * Dialog 动画样式 - */ - public static final class AnimStyle { - - /** 没有动画效果 */ - public static final int NO_ANIM = 0; - - /** 默认动画效果 */ - static final int DEFAULT = R.style.ScaleAnimStyle; - - /** 缩放动画 */ - public static final int SCALE = R.style.ScaleAnimStyle; - - /** IOS 动画 */ - public static final int IOS = R.style.IOSAnimStyle; - - /** 吐司动画 */ - public static final int TOAST = android.R.style.Animation_Toast; - - /** 顶部弹出动画 */ - public static final int TOP = R.style.TopAnimStyle; - - /** 底部弹出动画 */ - public static final int BOTTOM = R.style.BottomAnimStyle; - - /** 左边弹出动画 */ - public static final int LEFT = R.style.LeftAnimStyle; - - /** 右边弹出动画 */ - public static final int RIGHT = R.style.RightAnimStyle; + @Override + public void dismiss() { + removeCallbacks(); + super.dismiss(); } @SuppressWarnings("unchecked") - public static class Builder { + public static class Builder implements ContextAction, ClickAction { /** Context 对象 */ private final Context mContext; @@ -446,16 +376,19 @@ public static class Builder { /** 主题 */ private int mThemeId = R.style.BaseDialogStyle; /** 动画 */ - private int mAnimations = BaseDialog.AnimStyle.NO_ANIM; + private int mAnimations = AnimAction.NO_ANIM; /** 位置 */ private int mGravity = Gravity.NO_GRAVITY; + /** 宽度和高度 */ private int mWidth = ViewGroup.LayoutParams.WRAP_CONTENT; private int mHeight = ViewGroup.LayoutParams.WRAP_CONTENT; + /** 背景遮盖层开关 */ private boolean mBackgroundDimEnabled = true; /** 背景遮盖层透明度 */ private float mBackgroundDimAmount = 0.5f; + /** 是否能够被取消 */ private boolean mCancelable = true; /** 点击空白是否能够取消 前提是这个对话框可以被取消 */ @@ -470,13 +403,13 @@ public static class Builder { /** Dialog Key 监听 */ private BaseDialog.OnKeyListener mOnKeyListener; - /** 一些 View 属性设置存放集合 */ - private SparseArray mTextArray; - private SparseIntArray mVisibilityArray; - private SparseArray mBackgroundArray; - private SparseArray mImageArray; + /** 点击事件集合 */ private SparseArray mClickArray; + public Builder(Activity activity) { + this((Context) activity); + } + public Builder(Context context) { mContext = context; } @@ -497,7 +430,7 @@ public B setThemeStyle(@StyleRes int id) { * 设置布局 */ public B setContentView(@LayoutRes int id) { - // 这里解释一下,为什么要传 new FrameLayout,因为如果不传的话,XML 的根布局获取到的 LayoutParams 对象会为空,也就会导致宽高解析不出来 + // 这里解释一下,为什么要传 new FrameLayout,因为如果不传的话,XML 的根布局获取到的 LayoutParams 对象会为空,也就会导致宽高参数解析不出来 return setContentView(LayoutInflater.from(mContext).inflate(id, new FrameLayout(mContext), false)); } public B setContentView(View view) { @@ -507,19 +440,19 @@ public B setContentView(View view) { mDialog.setContentView(view); } else { if (mContentView != null) { - ViewGroup.LayoutParams params = mContentView.getLayoutParams(); - if (params != null && mWidth == ViewGroup.LayoutParams.WRAP_CONTENT && mHeight == ViewGroup.LayoutParams.WRAP_CONTENT) { + ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams(); + if (layoutParams != null && mWidth == ViewGroup.LayoutParams.WRAP_CONTENT && mHeight == ViewGroup.LayoutParams.WRAP_CONTENT) { // 如果当前 Dialog 的宽高设置了自适应,就以布局中设置的宽高为主 - setWidth(params.width); - setHeight(params.height); + setWidth(layoutParams.width); + setHeight(layoutParams.height); } // 如果当前没有设置重心,就自动获取布局重心 if (mGravity == Gravity.NO_GRAVITY) { - if (params instanceof FrameLayout.LayoutParams) { - setGravity(((FrameLayout.LayoutParams) params).gravity); - } else if (params instanceof LinearLayout.LayoutParams) { - setGravity(((LinearLayout.LayoutParams) params).gravity); + if (layoutParams instanceof FrameLayout.LayoutParams) { + setGravity(((FrameLayout.LayoutParams) layoutParams).gravity); + } else if (layoutParams instanceof LinearLayout.LayoutParams) { + setGravity(((LinearLayout.LayoutParams) layoutParams).gravity); } else { // 默认重心是居中 setGravity(Gravity.CENTER); @@ -535,9 +468,6 @@ public B setContentView(View view) { */ public B setGravity(int gravity) { // 适配 Android 4.2 新特性,布局反方向(开发者选项 - 强制使用从右到左的布局方向) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - gravity = Gravity.getAbsoluteGravity(gravity, getResources().getConfiguration().getLayoutDirection()); - } mGravity = gravity; if (isCreated()) { mDialog.setGravity(gravity); @@ -606,7 +536,7 @@ public B setCanceledOnTouchOutside(boolean cancel) { } /** - * 设置动画,已经封装好几种样式,具体可见{@link AnimStyle}类 + * 设置动画,已经封装好几种样式,具体可见{@link AnimAction}类 */ public B setAnimStyle(@StyleRes int id) { mAnimations = id; @@ -619,21 +549,23 @@ public B setAnimStyle(@StyleRes int id) { /** * 设置背景遮盖层开关 */ - public void setBackgroundDimEnabled(boolean enabled) { + public B setBackgroundDimEnabled(boolean enabled) { mBackgroundDimEnabled = enabled; if (isCreated()) { mDialog.setBackgroundDimEnabled(enabled); } + return (B) this; } /** * 设置背景遮盖层的透明度(前提条件是背景遮盖层开关必须是为开启状态) */ - public void setBackgroundDimAmount(float dimAmount) { + public B setBackgroundDimAmount(@FloatRange(from = 0, to = 1) float dimAmount) { mBackgroundDimAmount = dimAmount; if (isCreated()) { mDialog.setBackgroundDimAmount(dimAmount); } + return (B) this; } /** @@ -700,17 +632,26 @@ public B setText(@IdRes int viewId, @StringRes int stringId) { return setText(viewId, getString(stringId)); } public B setText(@IdRes int id, CharSequence text) { - if (isCreated()) { - TextView textView = mDialog.findViewById(id); - if (textView != null) { - textView.setText(text); - } - } else { - if (mTextArray == null) { - mTextArray = new SparseArray<>(); - } - mTextArray.put(id, text); - } + ((TextView) findViewById(id)).setText(text); + return (B) this; + } + + /** + * 设置文本颜色 + */ + public B setTextColor(@IdRes int id, @ColorInt int color) { + ((TextView) findViewById(id)).setTextColor(color); + return (B) this; + } + + /** + * 设置提示 + */ + public B setHint(@IdRes int viewId, @StringRes int stringId) { + return setHint(viewId, getString(stringId)); + } + public B setHint(@IdRes int id, CharSequence text) { + ((TextView) findViewById(id)).setHint(text); return (B) this; } @@ -718,17 +659,7 @@ public B setText(@IdRes int id, CharSequence text) { * 设置可见状态 */ public B setVisibility(@IdRes int id, int visibility) { - if (isCreated()) { - View view = mDialog.findViewById(id); - if (view != null) { - view.setVisibility(visibility); - } - } else { - if (mVisibilityArray == null) { - mVisibilityArray = new SparseIntArray(); - } - mVisibilityArray.put(id, visibility); - } + findViewById(id).setVisibility(visibility); return (B) this; } @@ -739,21 +670,7 @@ public B setBackground(@IdRes int viewId, @DrawableRes int drawableId) { return setBackground(viewId, ContextCompat.getDrawable(mContext, drawableId)); } public B setBackground(@IdRes int id, Drawable drawable) { - if (isCreated()) { - View view = mDialog.findViewById(id); - if (view != null) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - view.setBackground(drawable); - } else { - view.setBackgroundDrawable(drawable); - } - } - } else { - if (mBackgroundArray == null) { - mBackgroundArray = new SparseArray<>(); - } - mBackgroundArray.put(id, drawable); - } + findViewById(id).setBackground(drawable); return (B) this; } @@ -764,17 +681,7 @@ public B setImageDrawable(@IdRes int viewId, @DrawableRes int drawableId) { return setBackground(viewId, ContextCompat.getDrawable(mContext, drawableId)); } public B setImageDrawable(@IdRes int id, Drawable drawable) { - if (isCreated()) { - ImageView imageView = mDialog.findViewById(id); - if (imageView != null) { - imageView.setImageDrawable(drawable); - } - } else { - if (mImageArray == null) { - mImageArray = new SparseArray<>(); - } - mImageArray.put(id, drawable); - } + ((ImageView) findViewById(id)).setImageDrawable(drawable); return (B) this; } @@ -804,7 +711,7 @@ public BaseDialog create() { // 判断布局是否为空 if (mContentView == null) { - throw new IllegalArgumentException("Dialog layout cannot be empty"); + throw new IllegalArgumentException("are you ok?"); } // 如果当前没有设置重心,就设置一个默认的重心 @@ -813,22 +720,22 @@ public BaseDialog create() { } // 如果当前没有设置动画效果,就设置一个默认的动画效果 - if (mAnimations == BaseDialog.AnimStyle.NO_ANIM) { + if (mAnimations == AnimAction.NO_ANIM) { switch (mGravity) { case Gravity.TOP: - mAnimations = AnimStyle.TOP; + mAnimations = AnimAction.TOP; break; case Gravity.BOTTOM: - mAnimations = AnimStyle.BOTTOM; + mAnimations = AnimAction.BOTTOM; break; case Gravity.LEFT: - mAnimations = AnimStyle.LEFT; + mAnimations = AnimAction.LEFT; break; case Gravity.RIGHT: - mAnimations = AnimStyle.RIGHT; + mAnimations = AnimAction.RIGHT; break; default: - mAnimations = AnimStyle.DEFAULT; + mAnimations = AnimAction.DEFAULT; break; } } @@ -874,35 +781,15 @@ public BaseDialog create() { mDialog.setOnKeyListener(mOnKeyListener); } - // 设置文本 - for (int i = 0; mTextArray != null && i < mTextArray.size(); i++) { - ((TextView) mContentView.findViewById(mTextArray.keyAt(i))).setText(mTextArray.valueAt(i)); - } - - // 设置可见状态 - for (int i = 0; mVisibilityArray != null && i < mVisibilityArray.size(); i++) { - mContentView.findViewById(mVisibilityArray.keyAt(i)).setVisibility(mVisibilityArray.valueAt(i)); - } - - // 设置背景 - for (int i = 0; mBackgroundArray != null && i < mBackgroundArray.size(); i++) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - mContentView.findViewById(mBackgroundArray.keyAt(i)).setBackground(mBackgroundArray.valueAt(i)); - } else { - mContentView.findViewById(mBackgroundArray.keyAt(i)).setBackgroundDrawable(mBackgroundArray.valueAt(i)); - } - } - - // 设置图片 - for (int i = 0; mImageArray != null && i < mImageArray.size(); i++) { - ((ImageView) mContentView.findViewById(mImageArray.keyAt(i))).setImageDrawable(mImageArray.valueAt(i)); - } - - // 设置点击事件 for (int i = 0; mClickArray != null && i < mClickArray.size(); i++) { mContentView.findViewById(mClickArray.keyAt(i)).setOnClickListener(new ViewClickWrapper(mDialog, mClickArray.valueAt(i))); } + Activity activity = getActivity(); + if (activity != null) { + DialogLifecycle.with(activity, mDialog); + } + return mDialog; } @@ -910,28 +797,52 @@ public BaseDialog create() { * 显示 */ public BaseDialog show() { - final BaseDialog dialog = create(); - dialog.show(); - return dialog; + if (!isCreated()) { + create(); + } + mDialog.show(); + return mDialog; + } + + @Override + public Context getContext() { + return mContext; + } + + /** + * 获取 Activity + */ + public Activity getActivity() { + Context context = mContext; + do { + if (context instanceof Activity) { + return (Activity) context; + } else if (context instanceof ContextWrapper){ + context = ((ContextWrapper) context).getBaseContext(); + } else { + return null; + } + } while (context != null); + return null; } /** - * 当前 Dialog 是否创建了(仅供子类调用) + * 当前 Dialog 是否创建了 */ - protected boolean isCreated() { + public boolean isCreated() { return mDialog != null; } /** - * 当前 Dialog 是否显示了(仅供子类调用) + * 当前 Dialog 是否显示了 */ - protected boolean isShowing() { - return isCreated() && mDialog.isShowing(); + public boolean isShowing() { + return mDialog != null && mDialog.isShowing(); } /** - * 销毁当前 Dialog(仅供子类调用) + * 销毁当前 Dialog */ - protected void dismiss() { + public void dismiss() { if (mDialog != null) { mDialog.dismiss(); } @@ -947,7 +858,7 @@ protected BaseDialog createDialog(Context context, @StyleRes int themeId) { /** * 延迟执行 */ - protected final void post(Runnable r) { + public final void post(Runnable r) { if (isShowing()) { mDialog.post(r); } else { @@ -958,7 +869,7 @@ protected final void post(Runnable r) { /** * 延迟一段时间执行 */ - protected final void postDelayed(Runnable r, long delayMillis) { + public final void postDelayed(Runnable r, long delayMillis) { if (isShowing()) { mDialog.postDelayed(r, delayMillis); } else { @@ -969,7 +880,7 @@ protected final void postDelayed(Runnable r, long delayMillis) { /** * 在指定的时间执行 */ - protected final void postAtTime(Runnable r, long uptimeMillis) { + public final void postAtTime(Runnable r, long uptimeMillis) { if (isShowing()) { mDialog.postAtTime(r, uptimeMillis); } else { @@ -978,78 +889,122 @@ protected final void postAtTime(Runnable r, long uptimeMillis) { } /** - * 获取上下文对象(仅供子类调用) + * 获取 Dialog 的根布局 */ - protected Context getContext() { - return mContext; + public View getContentView() { + return mContentView; } /** - * 获取资源对象(仅供子类调用) + * 根据 id 查找 View */ - protected Resources getResources() { - return mContext.getResources(); + @Override + public V findViewById(@IdRes int id) { + if (mContentView == null) { + // 没有 setContentView 就想 findViewById ? + throw new IllegalStateException("are you ok?"); + } + return mContentView.findViewById(id); } /** - * 根据 id 获取一个文本(仅供子类调用) + * 获取当前 Dialog 对象 */ - protected String getString(@StringRes int id) { - return mContext.getString(id); + @Nullable + public BaseDialog getDialog() { + return mDialog; } + } - /** - * 根据 id 获取一个颜色(仅供子类调用) - */ - protected int getColor(@ColorRes int id) { - return ContextCompat.getColor(getContext(), id); + /** + * Dialog 生命周期管理 + */ + private static final class DialogLifecycle implements + Application.ActivityLifecycleCallbacks, + BaseDialog.OnShowListener, + BaseDialog.OnDismissListener { + + private static void with(Activity activity, BaseDialog dialog) { + new DialogLifecycle(activity, dialog); } - /** - * 根据 id 获取一个 Drawable(仅供子类调用) - */ - protected Drawable getDrawable(@DrawableRes int id) { - return ContextCompat.getDrawable(mContext, id); + private BaseDialog mDialog; + private Activity mActivity; + + /** Dialog 动画样式(避免 Dialog 从后台返回到前台后再次触发动画效果) */ + private int mDialogAnim; + + private DialogLifecycle(Activity activity, BaseDialog dialog) { + mActivity = activity; + dialog.addOnShowListener(this); + dialog.addOnDismissListener(this); } - /** - * 获取 Dialog 的根布局 - */ - protected View getContentView() { - return mContentView; + @Override + public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {} + + @Override + public void onActivityStarted(@NonNull Activity activity) {} + + @Override + public void onActivityResumed(@NonNull Activity activity) { + if (mDialog != null && mDialog.isShowing()) { + // 还原 Dialog 动画样式(这里必须要使用延迟设置,否则有一定几率会出现) + mDialog.post(() -> mDialog.setWindowAnimations(mDialogAnim)); + } } - /** - * 根据 id 查找 View(仅供子类调用) - */ - protected V findViewById(@IdRes int id) { - if (mContentView == null) { - // 没有 setContentView 就想 findViewById ? - throw new IllegalStateException("are you ok?"); + @Override + public void onActivityPaused(@NonNull Activity activity) {} + + @Override + public void onActivityStopped(@NonNull Activity activity) { + if (mDialog != null && mDialog.isShowing()) { + // 获取 Dialog 动画样式 + mDialogAnim = mDialog.getWindowAnimations(); + // 设置 Dialog 无动画效果 + mDialog.setWindowAnimations(AnimAction.NO_ANIM); } - return mContentView.findViewById(id); } - /** - * 获取当前 Dialog 对象(仅供子类调用) - */ - protected BaseDialog getDialog() { - return mDialog; + @Override + public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {} + + @Override + public void onActivityDestroyed(@NonNull Activity activity) { + if (mActivity == activity) { + if (mDialog != null) { + mDialog.removeOnShowListener(null); + mDialog.removeOnDismissListener(null); + if (mDialog.isShowing()) { + mDialog.dismiss(); + } + mDialog = null; + } + mActivity.getApplication().unregisterActivityLifecycleCallbacks(this); + mActivity = null; + } } - /** - * 获取系统服务 - */ - protected T getSystemService(@NonNull Class serviceClass) { - return ContextCompat.getSystemService(mContext, serviceClass); + @Override + public void onShow(BaseDialog dialog) { + mDialog = dialog; + mActivity.getApplication().registerActivityLifecycleCallbacks(this); + } + + @Override + public void onDismiss(BaseDialog dialog) { + mDialog = null; + mActivity.getApplication().unregisterActivityLifecycleCallbacks(this); } } + /** - * Dialog 监听包装类(修复监听器对象导致内存泄漏的问题) + * Dialog 监听包装类(修复原生 Dialog 监听器对象导致的内存泄漏) */ private static final class ListenersWrapper - extends WeakReference implements DialogInterface.OnShowListener, DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { + extends SoftReference implements DialogInterface.OnShowListener, DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { private ListenersWrapper(T referent) { super(referent); @@ -1102,19 +1057,18 @@ public final void onClick(View v) { * 显示监听包装类 */ private static final class ShowListenerWrapper + extends SoftReference implements BaseDialog.OnShowListener { - private final DialogInterface.OnShowListener mListener; - - private ShowListenerWrapper(DialogInterface.OnShowListener listener) { - mListener = listener; + private ShowListenerWrapper(DialogInterface.OnShowListener referent) { + super(referent); } @Override public void onShow(BaseDialog dialog) { // 在横竖屏切换后监听对象会为空 - if (mListener != null) { - mListener.onShow(dialog); + if (get() != null) { + get().onShow(dialog); } } } @@ -1123,19 +1077,18 @@ public void onShow(BaseDialog dialog) { * 取消监听包装类 */ private static final class CancelListenerWrapper + extends SoftReference implements BaseDialog.OnCancelListener { - private final DialogInterface.OnCancelListener mListener; - - private CancelListenerWrapper(DialogInterface.OnCancelListener listener) { - mListener = listener; + private CancelListenerWrapper(DialogInterface.OnCancelListener referent) { + super(referent); } @Override public void onCancel(BaseDialog dialog) { // 在横竖屏切换后监听对象会为空 - if (mListener != null) { - mListener.onCancel(dialog); + if (get() != null) { + get().onCancel(dialog); } } } @@ -1144,19 +1097,18 @@ public void onCancel(BaseDialog dialog) { * 销毁监听包装类 */ private static final class DismissListenerWrapper + extends SoftReference implements BaseDialog.OnDismissListener { - private final DialogInterface.OnDismissListener mListener; - - private DismissListenerWrapper(DialogInterface.OnDismissListener listener) { - mListener = listener; + private DismissListenerWrapper(DialogInterface.OnDismissListener referent) { + super(referent); } @Override public void onDismiss(BaseDialog dialog) { // 在横竖屏切换后监听对象会为空 - if (mListener != null) { - mListener.onDismiss(dialog); + if (get() != null) { + get().onDismiss(dialog); } } } diff --git a/base/src/main/java/com/hjq/base/BaseDialogFragment.java b/base/src/main/java/com/hjq/base/BaseDialogFragment.java deleted file mode 100644 index d6afe902..00000000 --- a/base/src/main/java/com/hjq/base/BaseDialogFragment.java +++ /dev/null @@ -1,174 +0,0 @@ -package com.hjq.base; - -import android.annotation.SuppressLint; -import android.app.Dialog; -import android.os.Bundle; -import android.os.SystemClock; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.DialogFragment; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentTransaction; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/11/24 - * desc : DialogFragment 基类 - */ -public class BaseDialogFragment extends DialogFragment { - - private BaseDialog mDialog; - - private static String sShowTag; - private static long sLastTime; - - public BaseDialogFragment() { - /** - * 必须预留一个空参构造函数,因为 Activity 横竖屏切换的时候会通过反射创建 Fragment - * 如果没有预留的话就会抛出:Unable to instantiate fragment com.hjq.base.BaseDialogFragment: could not find Fragment constructor - */ - } - - @SuppressLint("ValidFragment") - public BaseDialogFragment(BaseDialog dialog) { - mDialog = dialog; - if (mDialog == null) { - throw new IllegalArgumentException("The dialog box cannot be empty"); - } - } - - @Override - public void onActivityCreated(@Nullable Bundle savedInstanceState) { - /** - * super.onActivityCreated 如果往下执行了,里面的 mDialog 对象在横竖屏切换的时会变成一个空对象,所以就会导致空指针异常 - * 从 super.onActivityCreated 源码中得知 mShowsDialog 为 false 不会向下执行,所以我们要在这里进行预设值,避免空指针 - */ - if (mDialog == null) { - setShowsDialog(false); - } - super.onActivityCreated(savedInstanceState); - /** - * 如果这个 Dialog 对象为空,就移除这个 DialogFragment - */ - if (mDialog == null) { - dismissAllowingStateLoss(); - } - } - - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - return mDialog; - } - - @Override - public Dialog getDialog() { - return mDialog; - } - - /** - * 父类同名方法简化 - */ - public void show(@NonNull Fragment fragment) { - FragmentManager manager = fragment.getFragmentManager(); - if (manager != null) { - show(manager, fragment.getClass().getName()); - } - } - - /** - * 父类同名方法简化 - */ - public void show(@NonNull FragmentActivity activity) { - show(activity.getSupportFragmentManager(), activity.getClass().getName()); - } - - @Override - public void show(@NonNull FragmentManager manager, String tag) { - // !FragmentManager.isStateSaved():如果Activity 已经不可见,就不让这个对话框显示,否则会发生报错 - if (!isRepeatedShow(tag) && !manager.isStateSaved()) { - super.show(manager, tag); - } - } - - @Override - public int show(@NonNull FragmentTransaction transaction, String tag) { - if (!isRepeatedShow(tag) && !isStateSaved()) { - return super.show(transaction, tag); - } - return -1; - } - - /** - * 根据 tag 判断这个 Dialog 是否重复显示了 - * - * @param tag Tag标记 - */ - @SuppressWarnings("all") - protected boolean isRepeatedShow(String tag) { - boolean result = tag.equals(sShowTag) && SystemClock.uptimeMillis() - sLastTime < 500; - sShowTag = tag; - sLastTime = SystemClock.uptimeMillis(); - return result; - } - - public static class Builder extends BaseDialog.Builder { - - private final FragmentActivity mActivity; - private BaseDialogFragment mDialogFragment; - - public Builder(FragmentActivity activity) { - super(activity); - mActivity = activity; - } - - /** - * 获取当前 Activity 对象(仅供子类调用) - */ - protected FragmentActivity getActivity() { - return mActivity; - } - - /** - * 获取当前 DialogFragment 对象(仅供子类调用) - */ - protected BaseDialogFragment getDialogFragment() { - return mDialogFragment; - } - - @Override - public BaseDialog show() { - final BaseDialog dialog = create(); - mDialogFragment = createDialogFragment(dialog); - // 解决 Dialog 设置了而 DialogFragment 没有生效的问题 - mDialogFragment.setCancelable(dialog.isCancelable()); - mDialogFragment.show(mActivity.getSupportFragmentManager(), getFragmentTag()); - return dialog; - } - - @Override - protected void dismiss() { - // 这里不能调用 DialogFragment 的 dismiss 方法,因为在前台 show 之后却在后台 dismiss 会导致崩溃 - // java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState - mDialogFragment.dismissAllowingStateLoss(); - } - - /** - * 创建 DialogFragment 对象(子类可以重写此方法来改变 DialogFragment 类型) - */ - protected BaseDialogFragment createDialogFragment(BaseDialog dialog) { - return new BaseDialogFragment(dialog); - } - - /** - * 获取 Fragment 的标记(仅供子类调用) - */ - protected String getFragmentTag() { - return getClass().getName(); - } - } -} \ No newline at end of file diff --git a/base/src/main/java/com/hjq/base/BaseLazyFragment.java b/base/src/main/java/com/hjq/base/BaseFragment.java similarity index 52% rename from base/src/main/java/com/hjq/base/BaseLazyFragment.java rename to base/src/main/java/com/hjq/base/BaseFragment.java index c57c4301..d2f41396 100644 --- a/base/src/main/java/com/hjq/base/BaseLazyFragment.java +++ b/base/src/main/java/com/hjq/base/BaseFragment.java @@ -1,233 +1,175 @@ -package com.hjq.base; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.IdRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; -import androidx.fragment.app.Fragment; - -import java.util.Random; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/10/18 - * desc : Fragment 懒加载基类 - */ -public abstract class BaseLazyFragment extends Fragment { - - /** Activity对象 */ - private A mActivity; - /** 根布局 */ - private View mRootView; - /** 是否进行过懒加载 */ - private boolean isLazyLoad; - /** Fragment 是否可见 */ - private boolean isFragmentVisible; - /** 是否是 replace Fragment 的形式 */ - private boolean isReplaceFragment; - - /** - * 获得全局的 Activity - */ - @SuppressWarnings("unchecked") - @Override - public void onAttach(@NonNull Context context) { - super.onAttach(context); - mActivity = (A) requireActivity(); - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - - if (mRootView == null && getLayoutId() > 0) { - mRootView = inflater.inflate(getLayoutId(), null); - } - - ViewGroup parent = (ViewGroup) mRootView.getParent(); - if (parent != null) { - parent.removeView(mRootView); - } - - return mRootView; - } - - @Override - public View getView() { - return mRootView; - } - - /** - * 获取绑定的 Activity,防止出现 getActivity 为空 - */ - public A getAttachActivity() { - return mActivity; - } - - /** - * 是否进行了懒加载 - */ - protected boolean isLazyLoad() { - return isLazyLoad; - } - - /** - * 当前 Fragment 是否可见 - */ - public boolean isFragmentVisible() { - return isFragmentVisible; - } - - @Override - public void onActivityCreated(@Nullable Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - if (isReplaceFragment) { - if (isFragmentVisible) { - initLazyLoad(); - } - } else { - initLazyLoad(); - } - } - - /** replace Fragment时使用,ViewPager 切换时会回调此方法 */ - @Override - public void setUserVisibleHint(boolean isVisibleToUser) { - super.setUserVisibleHint(isVisibleToUser); - this.isReplaceFragment = true; - this.isFragmentVisible = isVisibleToUser; - if (isVisibleToUser && mRootView != null) { - if (!isLazyLoad) { - initLazyLoad(); - } else { - // 从不可见到可见 - onRestart(); - } - } - } - - /** - * 初始化懒加载 - */ - protected void initLazyLoad() { - if (!isLazyLoad) { - isLazyLoad = true; - initFragment(); - } - } - - /** - * 从可见的状态变成不可见状态,再从不可见状态变成可见状态时回调 - */ - @SuppressWarnings("all") - protected void onRestart() {} - - protected void initFragment() { - initView(); - initData(); - } - - /** 引入布局 */ - protected abstract int getLayoutId(); - - /** 初始化控件 */ - protected abstract void initView(); - - /** 初始化数据 */ - protected abstract void initData(); - - /** - * 根据资源 id 获取一个 View 对象 - */ - protected V findViewById(@IdRes int id) { - return mRootView.findViewById(id); - } - - protected V findActivityViewById(@IdRes int id) { - return mActivity.findViewById(id); - } - - /** - * startActivity 方法优化 - */ - - public void startActivity(Class cls) { - startActivity(new Intent(mActivity, cls)); - } - - public void startActivityFinish(Class cls) { - startActivityFinish(new Intent(mActivity, cls)); - } - - public void startActivityFinish(Intent intent) { - startActivity(intent); - finish(); - } - - /** - * startActivityForResult 方法优化 - */ - - private BaseActivity.ActivityCallback mActivityCallback; - private int mActivityRequestCode; - - public void startActivityForResult(Class cls, BaseActivity.ActivityCallback callback) { - startActivityForResult(new Intent(mActivity, cls), null, callback); - } - - public void startActivityForResult(Intent intent, BaseActivity.ActivityCallback callback) { - startActivityForResult(intent, null, callback); - } - - public void startActivityForResult(Intent intent, Bundle options, BaseActivity.ActivityCallback callback) { - // 回调还没有结束,所以不能再次调用此方法,这个方法只适合一对一回调,其他需求请使用原生的方法实现 - if (mActivityCallback == null) { - mActivityCallback = callback; - // 随机生成请求码,这个请求码在 0 - 255 之间 - mActivityRequestCode = new Random().nextInt(255); - startActivityForResult(intent, mActivityRequestCode, options); - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { - if (mActivityCallback != null && mActivityRequestCode == requestCode) { - mActivityCallback.onActivityResult(resultCode, data); - mActivityCallback = null; - } else { - super.onActivityResult(requestCode, resultCode, data); - } - } - - /** - * 销毁当前 Fragment 所在的 Activity - */ - public void finish() { - mActivity.finish(); - mActivity = null; - } - - /** - * 获取系统服务 - */ - public T getSystemService(@NonNull Class serviceClass) { - return ContextCompat.getSystemService(mActivity, serviceClass); - } - - /** - * Fragment 返回键被按下时回调 - */ - public boolean onKeyDown(int keyCode, KeyEvent event) { - // 默认不拦截按键事件,回传给 Activity - return false; - } +package com.hjq.base; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.IdRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import com.hjq.base.action.ClickAction; +import com.hjq.base.action.ContextAction; +import com.hjq.base.action.BundleAction; +import com.hjq.base.action.HandlerAction; + +import java.util.Random; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2018/10/18 + * desc : Fragment 基类 + */ +public abstract class BaseFragment + extends Fragment implements ContextAction, HandlerAction, ClickAction, BundleAction { + + /** Activity 对象 */ + private A mActivity; + /** 根布局 */ + private View mRootView; + /** 是否初始化过 */ + private boolean mInitialize; + + @SuppressWarnings("unchecked") + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + // 获得全局的 Activity + mActivity = (A) requireActivity(); + } + + @Override + public void onDetach() { + removeCallbacks(); + mActivity = null; + super.onDetach(); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + if (mRootView == null && getLayoutId() > 0) { + mRootView = inflater.inflate(getLayoutId(), null); + } + + ViewGroup parent = (ViewGroup) mRootView.getParent(); + if (parent != null) { + parent.removeView(mRootView); + } + + return mRootView; + } + + @Override + public void onResume() { + super.onResume(); + if (!mInitialize) { + mInitialize = true; + initFragment(); + } + } + + @NonNull + @Override + public View getView() { + return mRootView; + } + + /** + * 获取绑定的 Activity,防止出现 getActivity 为空 + */ + public A getAttachActivity() { + return mActivity; + } + + protected void initFragment() { + initView(); + initData(); + } + + /** + * 获取布局 ID + */ + protected abstract int getLayoutId(); + + /** + * 初始化控件 + */ + protected abstract void initView(); + + /** + * 初始化数据 + */ + protected abstract void initData(); + + /** + * 根据资源 id 获取一个 View 对象 + */ + @Override + public V findViewById(@IdRes int id) { + return mRootView.findViewById(id); + } + + @Override + public Bundle getBundle() { + return getArguments(); + } + + /** + * startActivityForResult 方法优化 + */ + + private BaseActivity.OnActivityCallback mActivityCallback; + private int mActivityRequestCode; + + public void startActivityForResult(Class clazz, BaseActivity.OnActivityCallback callback) { + startActivityForResult(new Intent(mActivity, clazz), null, callback); + } + + public void startActivityForResult(Intent intent, BaseActivity.OnActivityCallback callback) { + startActivityForResult(intent, null, callback); + } + + public void startActivityForResult(Intent intent, Bundle options, BaseActivity.OnActivityCallback callback) { + // 回调还没有结束,所以不能再次调用此方法,这个方法只适合一对一回调,其他需求请使用原生的方法实现 + if (mActivityCallback == null) { + mActivityCallback = callback; + // 随机生成请求码,这个请求码在 0 - 255 之间 + mActivityRequestCode = new Random().nextInt(255); + startActivityForResult(intent, mActivityRequestCode, options); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + if (mActivityCallback != null && mActivityRequestCode == requestCode) { + mActivityCallback.onActivityResult(resultCode, data); + mActivityCallback = null; + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } + + /** + * 销毁当前 Fragment 所在的 Activity + */ + public void finish() { + if (mActivity != null && !mActivity.isFinishing()) { + mActivity.finish(); + } + } + + /** + * Fragment 返回键被按下时回调 + */ + public boolean onKeyDown(int keyCode, KeyEvent event) { + // 默认不拦截按键事件,回传给 Activity + return false; + } } \ No newline at end of file diff --git a/base/src/main/java/com/hjq/base/BaseFragmentAdapter.java b/base/src/main/java/com/hjq/base/BaseFragmentAdapter.java index fe1065aa..d5e14f01 100644 --- a/base/src/main/java/com/hjq/base/BaseFragmentAdapter.java +++ b/base/src/main/java/com/hjq/base/BaseFragmentAdapter.java @@ -7,6 +7,7 @@ import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentPagerAdapter; +import androidx.viewpager.widget.ViewPager; import java.util.ArrayList; import java.util.List; @@ -25,6 +26,9 @@ public class BaseFragmentAdapter extends FragmentPagerAdapte /** 当前显示的Fragment */ private F mCurrentFragment; + /** 当前 ViewPager */ + private ViewPager mViewPager; + public BaseFragmentAdapter(FragmentActivity activity) { this(activity.getSupportFragmentManager()); } @@ -34,7 +38,11 @@ public BaseFragmentAdapter(Fragment fragment) { } public BaseFragmentAdapter(FragmentManager manager) { - super(manager); + this(manager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); + } + + public BaseFragmentAdapter(FragmentManager manager, int behavior) { + super(manager, behavior); } @NonNull @@ -75,4 +83,39 @@ public List getAllFragment() { public F getCurrentFragment() { return mCurrentFragment; } + + @Override + public void startUpdate(@NonNull ViewGroup container) { + super.startUpdate(container); + if (container instanceof ViewPager) { + // 记录绑定 ViewPager + mViewPager = (ViewPager) container; + } + } + + /** + * 设置当前条目 + * + * @param clazz 欲切换的 Fragment + */ + public void setCurrentItem(Class clazz) { + for (int i = 0; i < mFragmentSet.size(); i++) { + if (mFragmentSet.get(i).getClass() == clazz) { + setCurrentItem(i); + break; + } + } + } + + public void setCurrentItem(int position) { + if (mViewPager != null) { + mViewPager.setCurrentItem(position); + } + } + + public void setCurrentItem(int position, boolean smoothScroll) { + if (mViewPager != null) { + mViewPager.setCurrentItem(position, smoothScroll); + } + } } \ No newline at end of file diff --git a/base/src/main/java/com/hjq/base/BaseListViewAdapter.java b/base/src/main/java/com/hjq/base/BaseListViewAdapter.java deleted file mode 100644 index f9faa127..00000000 --- a/base/src/main/java/com/hjq/base/BaseListViewAdapter.java +++ /dev/null @@ -1,124 +0,0 @@ -package com.hjq.base; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.drawable.Drawable; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; - -import androidx.annotation.ColorRes; -import androidx.annotation.DrawableRes; -import androidx.annotation.IdRes; -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.annotation.StringRes; -import androidx.core.content.ContextCompat; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/10/18 - * desc : ListView 适配器基类 - */ -public abstract class BaseListViewAdapter extends BaseAdapter { - - /** 上下文对象 */ - private final Context mContext; - - public BaseListViewAdapter(Context context) { - mContext = context; - } - - @Override - public int getCount() { - return getItemCount(); - } - - public abstract int getItemCount(); - - @Override - public long getItemId(int position) { - return position; - } - - @SuppressWarnings("all") - @Override - public View getView(int position, View convertView, ViewGroup parent) { - final VH holder; - if (convertView == null) { - holder = onCreateViewHolder(parent, getItemViewType(position)); - holder.getItemView().setTag(holder); - } else { - holder = (VH) convertView.getTag(); - } - onBindViewHolder(holder, position); - return holder.getItemView(); - } - - @NonNull - public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType); - - public void onBindViewHolder(@NonNull VH holder, int position) { - holder.onBindView(position); - } - - /** - * 获取上下文对象 - */ - public Context getContext() { - return mContext; - } - - /** - * 获取资源对象 - */ - public Resources getResources() { - return mContext.getResources(); - } - - /** - * 获取资源文本 - */ - public String getString(@StringRes int id) { - return mContext.getString(id); - } - - /** - * 获取资源颜色 - */ - public int getColor(@ColorRes int id) { - return ContextCompat.getColor(getContext(), id); - } - - /** - * 获取资源图像 - */ - public Drawable getDrawable(@DrawableRes int id) { - return ContextCompat.getDrawable(mContext, id); - } - - public abstract class ViewHolder { - - private final View itemView; - - public ViewHolder(ViewGroup parent, @LayoutRes int id) { - this(LayoutInflater.from(parent.getContext()).inflate(id, parent, false)); - } - - public ViewHolder(View itemView) { - this.itemView = itemView; - } - - public final View getItemView() { - return itemView; - } - - public final V findViewById(@IdRes int id) { - return itemView.findViewById(id); - } - - public abstract void onBindView(int position); - } -} \ No newline at end of file diff --git a/base/src/main/java/com/hjq/base/BasePopupWindow.java b/base/src/main/java/com/hjq/base/BasePopupWindow.java new file mode 100644 index 00000000..eee3c89f --- /dev/null +++ b/base/src/main/java/com/hjq/base/BasePopupWindow.java @@ -0,0 +1,902 @@ +package com.hjq.base; + +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.Context; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.util.SparseArray; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.PopupWindow; +import android.widget.TextView; + +import androidx.annotation.ColorInt; +import androidx.annotation.DrawableRes; +import androidx.annotation.FloatRange; +import androidx.annotation.IdRes; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.annotation.StyleRes; +import androidx.core.content.ContextCompat; +import androidx.core.widget.PopupWindowCompat; + +import com.hjq.base.action.AnimAction; +import com.hjq.base.action.ClickAction; +import com.hjq.base.action.ContextAction; +import com.hjq.base.action.HandlerAction; + +import java.lang.ref.SoftReference; +import java.util.ArrayList; +import java.util.List; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/09/16 + * desc : PopupWindow 基类 + */ +public class BasePopupWindow extends PopupWindow + implements ContextAction, HandlerAction, ClickAction, + PopupWindow.OnDismissListener { + + private final Context mContext; + private PopupBackground mPopupBackground; + + private List mShowListeners; + private List mDismissListeners; + + public BasePopupWindow(@NonNull Context context) { + super(context); + mContext = context; + } + + @Override + public Context getContext() { + return mContext; + } + + /** + * 设置一个销毁监听器 + * + * @param listener 销毁监听器对象 + * @deprecated 请使用 {@link #addOnDismissListener(BasePopupWindow.OnDismissListener)} + */ + @Deprecated + @Override + public void setOnDismissListener(PopupWindow.OnDismissListener listener) { + if (listener == null) { + return; + } + addOnDismissListener(new DismissListenerWrapper(listener)); + } + + /** + * 添加一个显示监听器 + * + * @param listener 监听器对象 + */ + public void addOnShowListener(@Nullable BasePopupWindow.OnShowListener listener) { + if (mShowListeners == null) { + mShowListeners = new ArrayList<>(); + } + mShowListeners.add(listener); + } + + /** + * 添加一个销毁监听器 + * + * @param listener 监听器对象 + */ + public void addOnDismissListener(@Nullable BasePopupWindow.OnDismissListener listener) { + if (mDismissListeners == null) { + mDismissListeners = new ArrayList<>(); + super.setOnDismissListener(this); + } + mDismissListeners.add(listener); + } + + /** + * 移除一个显示监听器 + * + * @param listener 监听器对象 + */ + public void removeOnShowListener(@Nullable BasePopupWindow.OnShowListener listener) { + if (mShowListeners != null) { + mShowListeners.remove(listener); + } + } + + /** + * 移除一个销毁监听器 + * + * @param listener 监听器对象 + */ + public void removeOnDismissListener(@Nullable BasePopupWindow.OnDismissListener listener) { + if (mDismissListeners != null) { + mDismissListeners.remove(listener); + } + } + + /** + * 设置显示监听器集合 + */ + private void setOnShowListeners(@Nullable List listeners) { + mShowListeners = listeners; + } + + /** + * 设置销毁监听器集合 + */ + private void setOnDismissListeners(@Nullable List listeners) { + super.setOnDismissListener(this); + mDismissListeners = listeners; + } + + /** + * {@link PopupWindow.OnDismissListener} + */ + @Override + public void onDismiss() { + if (mDismissListeners != null) { + for (BasePopupWindow.OnDismissListener listener : mDismissListeners) { + listener.onDismiss(this); + } + } + } + + @Override + public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) { + if (isShowing() || getContentView() == null) { + return; + } + + if (mShowListeners != null) { + for (BasePopupWindow.OnShowListener listener : mShowListeners) { + listener.onShow(this); + } + } + super.showAsDropDown(anchor, xoff, yoff, gravity); + } + + @Override + public void showAtLocation(View parent, int gravity, int x, int y) { + if (isShowing() || getContentView() == null) { + return; + } + + if (mShowListeners != null) { + for (BasePopupWindow.OnShowListener listener : mShowListeners) { + listener.onShow(this); + } + } + super.showAtLocation(parent, gravity, x, y); + } + + @Override + public void dismiss() { + removeCallbacks(); + super.dismiss(); + } + + @Override + public V findViewById(@IdRes int id) { + return getContentView().findViewById(id); + } + + @Override + public void setWindowLayoutType(int type) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + super.setWindowLayoutType(type); + } else { + PopupWindowCompat.setWindowLayoutType(this, type); + } + } + + @Override + public int getWindowLayoutType() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + return super.getWindowLayoutType(); + } else { + return PopupWindowCompat.getWindowLayoutType(this); + } + } + + @Override + public void setOverlapAnchor(boolean overlapAnchor) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + super.setOverlapAnchor(overlapAnchor); + } else { + PopupWindowCompat.setOverlapAnchor(this, overlapAnchor); + } + } + + /** + * 设置背景遮盖层的透明度 + */ + public void setBackgroundDimAmount(@FloatRange(from = 0, to = 1) float dimAmount) { + float alpha = 1 - dimAmount; + if (isShowing()) { + setActivityAlpha(alpha); + } + if (mPopupBackground == null && alpha != 1) { + mPopupBackground = new PopupBackground(); + addOnShowListener(mPopupBackground); + addOnDismissListener(mPopupBackground); + } + if (mPopupBackground != null) { + mPopupBackground.setAlpha(alpha); + } + } + + /** + * 设置 Activity 窗口透明度 + */ + private void setActivityAlpha(float alpha) { + if (mContext instanceof Activity) { + Activity activity = (Activity) mContext; + WindowManager.LayoutParams params = activity.getWindow().getAttributes(); + + final ValueAnimator animator = ValueAnimator.ofFloat(params.alpha, alpha); + animator.setDuration(300); + animator.addUpdateListener(animation -> { + float value = (float) animation.getAnimatedValue(); + if (value != params.alpha) { + params.alpha = value; + activity.getWindow().setAttributes(params); + } + }); + animator.start(); + } + } + + @SuppressWarnings("unchecked") + public static class Builder implements ContextAction, ClickAction { + + private static final int DEFAULT_ANCHORED_GRAVITY = Gravity.TOP | Gravity.START; + + /** Context 对象 */ + private final Context mContext; + /** PopupWindow 布局 */ + private View mContentView; + /** PopupWindow 对象 */ + private BasePopupWindow mPopupWindow; + + /** PopupWindow Show 监听 */ + private List mOnShowListeners; + /** PopupWindow Dismiss 监听 */ + private List mOnDismissListeners; + + /** 动画 */ + private int mAnimations = AnimAction.NO_ANIM; + /** 位置 */ + private int mGravity = DEFAULT_ANCHORED_GRAVITY; + /** 宽度和高度 */ + private int mWidth = ViewGroup.LayoutParams.WRAP_CONTENT; + private int mHeight = ViewGroup.LayoutParams.WRAP_CONTENT; + + /** 是否可触摸 */ + private boolean mTouchable = true; + /** 是否有焦点 */ + private boolean mFocusable = true; + /** 是否外层可触摸 */ + private boolean mOutsideTouchable = false; + + /** 背景遮盖层透明度 */ + private float mBackgroundDimAmount; + + /** X 轴偏移 */ + private int mXOffset; + /** Y 轴偏移 */ + private int mYOffset; + + /** 点击事件集合 */ + private SparseArray mClickArray; + + public Builder(Context context) { + mContext = context; + } + + /** + * 设置布局 + */ + public B setContentView(@LayoutRes int id) { + // 这里解释一下,为什么要传 new FrameLayout,因为如果不传的话,XML 的根布局获取到的 LayoutParams 对象会为空,也就会导致宽高解析不出来 + return setContentView(LayoutInflater.from(mContext).inflate(id, new FrameLayout(mContext), false)); + } + public B setContentView(View view) { + mContentView = view; + + if (isCreated()) { + mPopupWindow.setContentView(view); + } else { + if (mContentView != null) { + ViewGroup.LayoutParams params = mContentView.getLayoutParams(); + if (params != null && mWidth == ViewGroup.LayoutParams.WRAP_CONTENT && mHeight == ViewGroup.LayoutParams.WRAP_CONTENT) { + // 如果当前 PopupWindow 的宽高设置了自适应,就以布局中设置的宽高为主 + setWidth(params.width); + setHeight(params.height); + } + + // 如果当前没有设置重心,就自动获取布局重心 + if (mGravity == DEFAULT_ANCHORED_GRAVITY) { + if (params instanceof FrameLayout.LayoutParams) { + setGravity(((FrameLayout.LayoutParams) params).gravity); + } else if (params instanceof LinearLayout.LayoutParams) { + setGravity(((LinearLayout.LayoutParams) params).gravity); + } else { + // 默认重心是居中 + setGravity(Gravity.CENTER); + } + } + } + } + return (B) this; + } + + /** + * 设置重心位置 + */ + public B setGravity(int gravity) { + // 适配 Android 4.2 新特性,布局反方向(开发者选项 - 强制使用从右到左的布局方向) + mGravity = Gravity.getAbsoluteGravity(gravity, getResources().getConfiguration().getLayoutDirection()); + return (B) this; + } + + /** + * 设置宽度 + */ + public B setWidth(int width) { + mWidth = width; + if (isCreated()) { + mPopupWindow.setWidth(width); + } else { + ViewGroup.LayoutParams params = mContentView != null ? mContentView.getLayoutParams() : null; + if (params != null) { + params.width = width; + mContentView.setLayoutParams(params); + } + } + return (B) this; + } + + /** + * 设置高度 + */ + public B setHeight(int height) { + mHeight = height; + if (isCreated()) { + mPopupWindow.setHeight(height); + } else { + // 这里解释一下为什么要重新设置 LayoutParams + // 因为如果不这样设置的话,第一次显示的时候会按照 PopupWindow 宽高显示 + // 但是 Layout 内容变更之后就不会按照之前的设置宽高来显示 + // 所以这里我们需要对 View 的 LayoutParams 也进行设置 + ViewGroup.LayoutParams params = mContentView != null ? mContentView.getLayoutParams() : null; + if (params != null) { + params.height = height; + mContentView.setLayoutParams(params); + } + } + return (B) this; + } + + /** + * 是否可触摸 + */ + public B setTouchable(boolean touchable) { + mTouchable = touchable; + return (B) this; + } + + /** + * 是否有焦点 + */ + public B setFocusable(boolean focusable) { + mFocusable = focusable; + return (B) this; + } + + /** + * 是否外层可触摸 + */ + public B setOutsideTouchable(boolean touchable) { + mOutsideTouchable = touchable; + return (B) this; + } + + /** + * 设置水平偏移量 + */ + public B setXOffset(int offset) { + mXOffset = offset; + return (B) this; + } + + /** + * 设置垂直偏移量 + */ + public B setYOffset(int offset) { + mYOffset = offset; + return (B) this; + } + + /** + * 设置动画,已经封装好几种样式,具体可见{@link AnimAction}类 + */ + public B setAnimStyle(@StyleRes int id) { + mAnimations = id; + if (isCreated()) { + mPopupWindow.setAnimationStyle(id); + } + return (B) this; + } + + /** + * 设置背景遮盖层的透明度 + */ + public B setBackgroundDimAmount(@FloatRange(from = 0, to = 1) float dimAmount) { + mBackgroundDimAmount = dimAmount; + if (isShowing()) { + mPopupWindow.setBackgroundDimAmount(dimAmount); + } + return (B) this; + } + + /** + * 添加显示监听 + */ + public B addOnShowListener(@NonNull BasePopupWindow.OnShowListener listener) { + if (isCreated()) { + mPopupWindow.addOnShowListener(listener); + } else { + if (mOnShowListeners == null) { + mOnShowListeners = new ArrayList<>(); + } + mOnShowListeners.add(listener); + } + return (B) this; + } + + /** + * 添加销毁监听 + */ + public B addOnDismissListener(@NonNull BasePopupWindow.OnDismissListener listener) { + if (isCreated()) { + mPopupWindow.addOnDismissListener(listener); + } else { + if (mOnDismissListeners == null) { + mOnDismissListeners = new ArrayList<>(); + } + mOnDismissListeners.add(listener); + } + return (B) this; + } + + /** + * 设置文本 + */ + public B setText(@IdRes int viewId, @StringRes int stringId) { + return setText(viewId, getString(stringId)); + } + public B setText(@IdRes int id, CharSequence text) { + ((TextView) findViewById(id)).setText(text); + return (B) this; + } + + /** + * 设置文本颜色 + */ + public B setTextColor(@IdRes int id, @ColorInt int color) { + ((TextView) findViewById(id)).setTextColor(color); + return (B) this; + } + + /** + * 设置提示 + */ + public B setHint(@IdRes int viewId, @StringRes int stringId) { + return setHint(viewId, getString(stringId)); + } + public B setHint(@IdRes int id, CharSequence text) { + ((TextView) findViewById(id)).setHint(text); + return (B) this; + } + + /** + * 设置可见状态 + */ + public B setVisibility(@IdRes int id, int visibility) { + findViewById(id).setVisibility(visibility); + return (B) this; + } + + /** + * 设置背景 + */ + public B setBackground(@IdRes int viewId, @DrawableRes int drawableId) { + return setBackground(viewId, ContextCompat.getDrawable(mContext, drawableId)); + } + public B setBackground(@IdRes int id, Drawable drawable) { + findViewById(id).setBackground(drawable); + return (B) this; + } + + /** + * 设置图片 + */ + public B setImageDrawable(@IdRes int viewId, @DrawableRes int drawableId) { + return setBackground(viewId, ContextCompat.getDrawable(mContext, drawableId)); + } + public B setImageDrawable(@IdRes int id, Drawable drawable) { + ((ImageView) findViewById(id)).setImageDrawable(drawable); + return (B) this; + } + + /** + * 设置点击事件 + */ + public B setOnClickListener(@IdRes int id, @NonNull BasePopupWindow.OnClickListener listener) { + if (isCreated()) { + View view = mPopupWindow.findViewById(id); + if (view != null) { + view.setOnClickListener(new ViewClickWrapper(mPopupWindow, listener)); + } + } else { + if (mClickArray == null) { + mClickArray = new SparseArray<>(); + } + mClickArray.put(id, listener); + } + return (B) this; + } + + /** + * 创建 + */ + @SuppressLint("RtlHardcoded") + public BasePopupWindow create() { + + // 判断布局是否为空 + if (mContentView == null) { + throw new IllegalArgumentException("are you ok?"); + } + + // 如果当前没有设置重心,就设置一个默认的重心 + if (mGravity == DEFAULT_ANCHORED_GRAVITY) { + mGravity = Gravity.CENTER; + } + + // 如果当前没有设置动画效果,就设置一个默认的动画效果 + if (mAnimations == AnimAction.NO_ANIM) { + switch (mGravity) { + case Gravity.TOP: + mAnimations = AnimAction.TOP; + break; + case Gravity.BOTTOM: + mAnimations = AnimAction.BOTTOM; + break; + case Gravity.LEFT: + mAnimations = AnimAction.LEFT; + break; + case Gravity.RIGHT: + mAnimations = AnimAction.RIGHT; + break; + default: + mAnimations = AnimAction.DEFAULT; + break; + } + } + + mPopupWindow = createPopupWindow(mContext); + mPopupWindow.setContentView(mContentView); + mPopupWindow.setWidth(mWidth); + mPopupWindow.setHeight(mHeight); + mPopupWindow.setAnimationStyle(mAnimations); + mPopupWindow.setTouchable(mTouchable); + mPopupWindow.setFocusable(mFocusable); + mPopupWindow.setOutsideTouchable(mOutsideTouchable); + mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + mPopupWindow.setBackgroundDimAmount(mBackgroundDimAmount); + + if (mOnShowListeners != null) { + mPopupWindow.setOnShowListeners(mOnShowListeners); + } + + if (mOnDismissListeners != null) { + mPopupWindow.setOnDismissListeners(mOnDismissListeners); + } + + for (int i = 0; mClickArray != null && i < mClickArray.size(); i++) { + mContentView.findViewById(mClickArray.keyAt(i)).setOnClickListener(new BasePopupWindow.ViewClickWrapper(mPopupWindow, mClickArray.valueAt(i))); + } + return mPopupWindow; + } + + /** + * 显示为下拉 + */ + public BasePopupWindow showAsDropDown(View anchor) { + if (!isCreated()) { + create(); + } + mPopupWindow.showAsDropDown(anchor, mXOffset, mYOffset, mGravity); + return mPopupWindow; + } + + /** + * 显示在指定位置 + */ + public BasePopupWindow showAtLocation(View parent) { + if (!isCreated()) { + create(); + } + mPopupWindow.showAtLocation(parent, mGravity, mXOffset, mYOffset); + return mPopupWindow; + } + + @Override + public Context getContext() { + return mContext; + } + + /** + * 当前 PopupWindow 是否创建了 + */ + public boolean isCreated() { + return mPopupWindow != null; + } + + /** + * 当前 PopupWindow 是否显示了 + */ + public boolean isShowing() { + return mPopupWindow != null && mPopupWindow.isShowing(); + } + + /** + * 销毁当前 PopupWindow + */ + public void dismiss() { + if (mPopupWindow != null) { + mPopupWindow.dismiss(); + } + } + + /** + * 创建 PopupWindow 对象(子类可以重写此方法来改变 PopupWindow 类型) + */ + protected BasePopupWindow createPopupWindow(Context context) { + return new BasePopupWindow(context); + } + + /** + * 获取 PopupWindow 的根布局 + */ + public View getContentView() { + return mContentView; + } + + /** + * 根据 id 查找 View + */ + @Override + public V findViewById(@IdRes int id) { + if (mContentView == null) { + // 没有 setContentView 就想 findViewById ? + throw new IllegalStateException("are you ok?"); + } + return mContentView.findViewById(id); + } + + /** + * 获取当前 PopupWindow 对象 + */ + @Nullable + public BasePopupWindow getPopupWindow() { + return mPopupWindow; + } + + /** + * 延迟执行 + */ + public final void post(Runnable r) { + if (isShowing()) { + mPopupWindow.post(r); + } else { + addOnShowListener(new ShowPostWrapper(r)); + } + } + + /** + * 延迟一段时间执行 + */ + public final void postDelayed(Runnable r, long delayMillis) { + if (isShowing()) { + mPopupWindow.postDelayed(r, delayMillis); + } else { + addOnShowListener(new ShowPostDelayedWrapper(r, delayMillis)); + } + } + + /** + * 在指定的时间执行 + */ + public final void postAtTime(Runnable r, long uptimeMillis) { + if (isShowing()) { + mPopupWindow.postAtTime(r, uptimeMillis); + } else { + addOnShowListener(new ShowPostAtTimeWrapper(r, uptimeMillis)); + } + } + } + + /** + * PopupWindow 背景遮盖层实现类 + */ + private static class PopupBackground implements + BasePopupWindow.OnShowListener, + BasePopupWindow.OnDismissListener { + + private float mAlpha; + + private void setAlpha(float alpha) { + mAlpha = alpha; + } + + @Override + public void onShow(BasePopupWindow popupWindow) { + popupWindow.setActivityAlpha(mAlpha); + } + + @Override + public void onDismiss(BasePopupWindow popupWindow) { + popupWindow.setActivityAlpha(1); + } + } + + /** + * 销毁监听包装类 + */ + private static final class DismissListenerWrapper + extends SoftReference + implements BasePopupWindow.OnDismissListener { + + private DismissListenerWrapper(PopupWindow.OnDismissListener referent) { + super(referent); + } + + @Override + public void onDismiss(BasePopupWindow popupWindow) { + // 在横竖屏切换后监听对象会为空 + if (get() != null) { + get().onDismiss(); + } + } + } + + /** + * 点击事件包装类 + */ + private static final class ViewClickWrapper + implements View.OnClickListener { + + private final BasePopupWindow mBasePopupWindow; + private final BasePopupWindow.OnClickListener mListener; + + private ViewClickWrapper(BasePopupWindow popupWindow, BasePopupWindow.OnClickListener listener) { + mBasePopupWindow = popupWindow; + mListener = listener; + } + + @SuppressWarnings("unchecked") + @Override + public final void onClick(View v) { + mListener.onClick(mBasePopupWindow, v); + } + } + + /** + * post 任务包装类 + */ + private static final class ShowPostWrapper implements OnShowListener { + + private final Runnable mRunnable; + + private ShowPostWrapper(Runnable r) { + mRunnable = r; + } + + @Override + public void onShow(BasePopupWindow dialog) { + if (mRunnable != null) { + dialog.removeOnShowListener(this); + dialog.post(mRunnable); + } + } + } + + /** + * postDelayed 任务包装类 + */ + private static final class ShowPostDelayedWrapper implements OnShowListener { + + private final Runnable mRunnable; + private final long mDelayMillis; + + private ShowPostDelayedWrapper(Runnable r, long delayMillis) { + mRunnable = r; + mDelayMillis = delayMillis; + } + + @Override + public void onShow(BasePopupWindow dialog) { + if (mRunnable != null) { + dialog.removeOnShowListener(this); + dialog.postDelayed(mRunnable, mDelayMillis); + } + } + } + + /** + * postAtTime 任务包装类 + */ + private static final class ShowPostAtTimeWrapper implements OnShowListener { + + private final Runnable mRunnable; + private final long mUptimeMillis; + + private ShowPostAtTimeWrapper(Runnable r, long uptimeMillis) { + mRunnable = r; + mUptimeMillis = uptimeMillis; + } + + @Override + public void onShow(BasePopupWindow dialog) { + if (mRunnable != null) { + dialog.removeOnShowListener(this); + dialog.postAtTime(mRunnable, mUptimeMillis); + } + } + } + + /** + * 点击监听器 + */ + public interface OnClickListener { + void onClick(BasePopupWindow popupWindow, V view); + } + + /** + * 显示监听器 + */ + public interface OnShowListener { + + /** + * PopupWindow 显示了 + */ + void onShow(BasePopupWindow popupWindow); + } + + /** + * 销毁监听器 + */ + public interface OnDismissListener { + + /** + * PopupWindow 销毁了 + */ + void onDismiss(BasePopupWindow popupWindow); + } +} \ No newline at end of file diff --git a/base/src/main/java/com/hjq/base/action/AnimAction.java b/base/src/main/java/com/hjq/base/action/AnimAction.java new file mode 100644 index 00000000..adb35083 --- /dev/null +++ b/base/src/main/java/com/hjq/base/action/AnimAction.java @@ -0,0 +1,39 @@ +package com.hjq.base.action; + +import com.hjq.base.R; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/09/21 + * desc : 动画样式 + */ +public interface AnimAction { + + /** 没有动画效果 */ + int NO_ANIM = 0; + + /** 默认动画效果 */ + int DEFAULT = R.style.ScaleAnimStyle; + + /** 缩放动画 */ + int SCALE = R.style.ScaleAnimStyle; + + /** IOS 动画 */ + int IOS = R.style.IOSAnimStyle; + + /** 吐司动画 */ + int TOAST = android.R.style.Animation_Toast; + + /** 顶部弹出动画 */ + int TOP = R.style.TopAnimStyle; + + /** 底部弹出动画 */ + int BOTTOM = R.style.BottomAnimStyle; + + /** 左边弹出动画 */ + int LEFT = R.style.LeftAnimStyle; + + /** 右边弹出动画 */ + int RIGHT = R.style.RightAnimStyle; +} \ No newline at end of file diff --git a/base/src/main/java/com/hjq/base/action/BundleAction.java b/base/src/main/java/com/hjq/base/action/BundleAction.java new file mode 100644 index 00000000..932d8a7e --- /dev/null +++ b/base/src/main/java/com/hjq/base/action/BundleAction.java @@ -0,0 +1,74 @@ +package com.hjq.base.action; + +import android.os.Bundle; +import android.os.Parcelable; + +import androidx.annotation.Nullable; + +import java.io.Serializable; +import java.util.ArrayList; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/10/23 + * desc : 参数意图 + */ +public interface BundleAction { + + @Nullable + Bundle getBundle(); + + default int getInt(String name) { + return getInt(name, 0); + } + + default int getInt(String name, int defaultValue) { + return getBundle() == null ? defaultValue : getBundle().getInt(name, defaultValue); + } + + default long getLong(String name) { + return getLong(name, 0); + } + + default long getLong(String name, int defaultValue) { + return getBundle() == null ? defaultValue : getBundle().getLong(name, defaultValue); + } + + default float getFloat(String name) { + return getFloat(name, 0); + } + + default float getFloat(String name, int defaultValue) { + return getBundle() == null ? defaultValue : getBundle().getFloat(name, defaultValue); + } + + default double getDouble(String name) { + return getDouble(name, 0); + } + + default double getDouble(String name, int defaultValue) { + return getBundle() == null ? defaultValue : getBundle().getDouble(name, defaultValue); + } + + default String getString(String name) { + return getBundle() == null ? null : getBundle().getString(name); + } + + default

P getParcelable(String name) { + return getBundle() == null ? null : getBundle().getParcelable(name); + } + + @SuppressWarnings("unchecked") + default S getSerializable(String name) { + return (S) (getBundle() == null ? null : getBundle().getSerializable(name)); + } + + default ArrayList getStringArrayList(String name) { + return getBundle() == null ? null : getBundle().getStringArrayList(name); + } + + default ArrayList getIntegerArrayList(String name) { + return getBundle() == null ? null : getBundle().getIntegerArrayList(name); + } +} \ No newline at end of file diff --git a/base/src/main/java/com/hjq/base/action/ClickAction.java b/base/src/main/java/com/hjq/base/action/ClickAction.java new file mode 100644 index 00000000..2b30f0e7 --- /dev/null +++ b/base/src/main/java/com/hjq/base/action/ClickAction.java @@ -0,0 +1,27 @@ +package com.hjq.base.action; + +import android.view.View; + +import androidx.annotation.IdRes; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/09/15 + * desc : 点击事件意图 + */ +public interface ClickAction extends View.OnClickListener { + + V findViewById(@IdRes int id); + + @Override + default void onClick(View v) { + // 默认不实现,让子类实现 + } + + default void setOnClickListener(@IdRes int... ids) { + for (int id : ids) { + findViewById(id).setOnClickListener(this); + } + } +} \ No newline at end of file diff --git a/base/src/main/java/com/hjq/base/action/ContextAction.java b/base/src/main/java/com/hjq/base/action/ContextAction.java new file mode 100644 index 00000000..895ac579 --- /dev/null +++ b/base/src/main/java/com/hjq/base/action/ContextAction.java @@ -0,0 +1,86 @@ +package com.hjq.base.action; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; +import androidx.core.content.ContextCompat; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/09/15 + * desc : Context 意图处理(扩展非 Context 类的方法,禁止 Context 类实现此接口) + */ +public interface ContextAction { + + /** + * 获取 Context + */ + Context getContext(); + + /** + * 获取资源对象(仅供子类调用) + */ + default Resources getResources() { + return getContext().getResources(); + } + + /** + * 根据 id 获取一个文本 + */ + default String getString(@StringRes int id) { + return getContext().getString(id); + } + + default String getString(@StringRes int id, Object... formatArgs) { + return getResources().getString(id, formatArgs); + } + + /** + * 根据 id 获取一个 Drawable + */ + default Drawable getDrawable(@DrawableRes int id) { + return ContextCompat.getDrawable(getContext(), id); + } + + /** + * 根据 id 获取一个颜色 + */ + @ColorInt + default int getColor(@ColorRes int id) { + return ContextCompat.getColor(getContext(), id); + } + + /** + * 启动一个 Activity(简化版) + */ + default void startActivity(Class clazz) { + startActivity(new Intent(getContext(), clazz)); + } + + /** + * 启动一个 Activity + */ + default void startActivity(Intent intent) { + if (!(getContext() instanceof Activity)) { + // 如果当前的上下文不是 Activity,调用 startActivity 必须加入新任务栈的标记 + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + } + getContext().startActivity(intent); + } + + /** + * 获取系统服务 + */ + default S getSystemService(@NonNull Class serviceClass) { + return ContextCompat.getSystemService(getContext(), serviceClass); + } +} \ No newline at end of file diff --git a/base/src/main/java/com/hjq/base/action/HandlerAction.java b/base/src/main/java/com/hjq/base/action/HandlerAction.java new file mode 100644 index 00000000..83210f22 --- /dev/null +++ b/base/src/main/java/com/hjq/base/action/HandlerAction.java @@ -0,0 +1,55 @@ +package com.hjq.base.action; + +import android.os.Handler; +import android.os.Looper; +import android.os.SystemClock; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/09/15 + * desc : Handler 意图处理 + */ +public interface HandlerAction { + + Handler HANDLER = new Handler(Looper.getMainLooper()); + + /** + * 获取 Handler + */ + default Handler getHandler() { + return HANDLER; + } + + /** + * 延迟执行 + */ + default boolean post(Runnable r) { + return postDelayed(r, 0); + } + + /** + * 延迟一段时间执行 + */ + default boolean postDelayed(Runnable r, long delayMillis) { + if (delayMillis < 0) { + delayMillis = 0; + } + return postAtTime(r, SystemClock.uptimeMillis() + delayMillis); + } + + /** + * 在指定的时间执行 + */ + default boolean postAtTime(Runnable r, long uptimeMillis) { + // 发送和这个 Activity 相关的消息回调 + return HANDLER.postAtTime(r, this, uptimeMillis); + } + + /** + * 移除消息回调 + */ + default void removeCallbacks() { + HANDLER.removeCallbacksAndMessages(this); + } +} \ No newline at end of file diff --git a/base/src/main/res/anim/item_animation_fall_down.xml b/base/src/main/res/anim/item_fall_down.xml similarity index 100% rename from base/src/main/res/anim/item_animation_fall_down.xml rename to base/src/main/res/anim/item_fall_down.xml diff --git a/base/src/main/res/anim/item_animation_from_bottom.xml b/base/src/main/res/anim/item_from_bottom.xml similarity index 100% rename from base/src/main/res/anim/item_animation_from_bottom.xml rename to base/src/main/res/anim/item_from_bottom.xml diff --git a/base/src/main/res/anim/item_animation_from_right.xml b/base/src/main/res/anim/item_from_right.xml similarity index 100% rename from base/src/main/res/anim/item_animation_from_right.xml rename to base/src/main/res/anim/item_from_right.xml diff --git a/base/src/main/res/anim/layout_animation_fall_down.xml b/base/src/main/res/anim/layout_fall_down.xml similarity index 76% rename from base/src/main/res/anim/layout_animation_fall_down.xml rename to base/src/main/res/anim/layout_fall_down.xml index 6736b9f7..679d1128 100644 --- a/base/src/main/res/anim/layout_animation_fall_down.xml +++ b/base/src/main/res/anim/layout_fall_down.xml @@ -1,5 +1,5 @@ \ No newline at end of file diff --git a/base/src/main/res/anim/layout_animation_from_bottom.xml b/base/src/main/res/anim/layout_from_bottom.xml similarity index 75% rename from base/src/main/res/anim/layout_animation_from_bottom.xml rename to base/src/main/res/anim/layout_from_bottom.xml index f04b269b..eb797d29 100644 --- a/base/src/main/res/anim/layout_animation_from_bottom.xml +++ b/base/src/main/res/anim/layout_from_bottom.xml @@ -1,5 +1,5 @@ \ No newline at end of file diff --git a/base/src/main/res/anim/layout_animation_from_right.xml b/base/src/main/res/anim/layout_from_right.xml similarity index 75% rename from base/src/main/res/anim/layout_animation_from_right.xml rename to base/src/main/res/anim/layout_from_right.xml index 9ea2fa97..1f7e973d 100644 --- a/base/src/main/res/anim/layout_animation_from_right.xml +++ b/base/src/main/res/anim/layout_from_right.xml @@ -1,5 +1,5 @@ \ No newline at end of file diff --git a/base/src/main/res/anim/dialog_bottom_in.xml b/base/src/main/res/anim/window_bottom_in.xml similarity index 100% rename from base/src/main/res/anim/dialog_bottom_in.xml rename to base/src/main/res/anim/window_bottom_in.xml diff --git a/base/src/main/res/anim/dialog_bottom_out.xml b/base/src/main/res/anim/window_bottom_out.xml similarity index 100% rename from base/src/main/res/anim/dialog_bottom_out.xml rename to base/src/main/res/anim/window_bottom_out.xml diff --git a/base/src/main/res/anim/dialog_ios_in.xml b/base/src/main/res/anim/window_ios_in.xml similarity index 100% rename from base/src/main/res/anim/dialog_ios_in.xml rename to base/src/main/res/anim/window_ios_in.xml diff --git a/base/src/main/res/anim/dialog_ios_out.xml b/base/src/main/res/anim/window_ios_out.xml similarity index 100% rename from base/src/main/res/anim/dialog_ios_out.xml rename to base/src/main/res/anim/window_ios_out.xml diff --git a/base/src/main/res/anim/dialog_left_in.xml b/base/src/main/res/anim/window_left_in.xml similarity index 100% rename from base/src/main/res/anim/dialog_left_in.xml rename to base/src/main/res/anim/window_left_in.xml diff --git a/base/src/main/res/anim/dialog_left_out.xml b/base/src/main/res/anim/window_left_out.xml similarity index 100% rename from base/src/main/res/anim/dialog_left_out.xml rename to base/src/main/res/anim/window_left_out.xml diff --git a/base/src/main/res/anim/dialog_right_in.xml b/base/src/main/res/anim/window_right_in.xml similarity index 100% rename from base/src/main/res/anim/dialog_right_in.xml rename to base/src/main/res/anim/window_right_in.xml diff --git a/base/src/main/res/anim/dialog_right_out.xml b/base/src/main/res/anim/window_right_out.xml similarity index 100% rename from base/src/main/res/anim/dialog_right_out.xml rename to base/src/main/res/anim/window_right_out.xml diff --git a/base/src/main/res/anim/dialog_scale_in.xml b/base/src/main/res/anim/window_scale_in.xml similarity index 100% rename from base/src/main/res/anim/dialog_scale_in.xml rename to base/src/main/res/anim/window_scale_in.xml diff --git a/base/src/main/res/anim/dialog_scale_out.xml b/base/src/main/res/anim/window_scale_out.xml similarity index 100% rename from base/src/main/res/anim/dialog_scale_out.xml rename to base/src/main/res/anim/window_scale_out.xml diff --git a/base/src/main/res/anim/dialog_top_in.xml b/base/src/main/res/anim/window_top_in.xml similarity index 100% rename from base/src/main/res/anim/dialog_top_in.xml rename to base/src/main/res/anim/window_top_in.xml diff --git a/base/src/main/res/anim/dialog_top_out.xml b/base/src/main/res/anim/window_top_out.xml similarity index 100% rename from base/src/main/res/anim/dialog_top_out.xml rename to base/src/main/res/anim/window_top_out.xml diff --git a/base/src/main/res/values/styles.xml b/base/src/main/res/values/styles.xml index ce9d805e..6a00cd07 100644 --- a/base/src/main/res/values/styles.xml +++ b/base/src/main/res/values/styles.xml @@ -15,8 +15,8 @@ true - @anim/dialog_scale_in - @anim/dialog_scale_out + @anim/window_scale_in + @anim/window_scale_out 0dp 0dp @@ -36,38 +36,38 @@ \ No newline at end of file diff --git a/build.gradle b/build.gradle index 3fe130ec..251cee3c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,16 @@ -apply from: 'versions.gradle' - buildscript { + repositories { jcenter() google() + maven { url "https://jitpack.io" } } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.4.2' + // AOP 开发插件:https://mvnrepository.com/artifact/org.aspectj/aspectjtools + classpath 'org.aspectj:aspectjtools:1.9.5' + // AOP 配置插件:https://github.com/xuexiangjys/XAOP + classpath 'com.github.xuexiangjys.XAOP:xaop-plugin:1.0.5' } } @@ -14,8 +18,14 @@ allprojects { repositories { jcenter() google() - maven {url "https://jitpack.io"} - maven {url 'https://maven.google.com'} + maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } + maven { url 'http://maven.aliyun.com/nexus/content/repositories/google' } + maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter' } + maven { url 'http://maven.aliyun.com/nexus/content/repositories/gradle-plugin' } + maven { url 'https://maven.aliyun.com/nexus/content/repositories/central/' } + maven { url 'https://dl.bintray.com/getactivity/maven/' } + maven { url 'https://maven.google.com' } + maven { url "https://jitpack.io" } } } diff --git a/config.gradle b/config.gradle new file mode 100644 index 00000000..3ef6ee3b --- /dev/null +++ b/config.gradle @@ -0,0 +1,35 @@ +// 通用配置 +android { + + compileSdkVersion 29 + defaultConfig { + minSdkVersion 19 + targetSdkVersion 29 + versionCode 10 + versionName "1.0" + } + + // 支持 Java JDK 8 + compileOptions { + targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_1_8 + } + + // 设置 SO 库存放目录 + sourceSets { + main { + jniLibs.srcDirs = ['libs'] + } + } +} + +dependencies { + // 依赖 libs 目录下所有 jar 包 + implementation fileTree(include: ['*.jar'], dir: 'libs') + // 依赖 libs 目录下所有 aar 包 + implementation fileTree(include: ['*.aar'], dir: 'libs') + + // 谷歌兼容库:https://developer.android.google.cn/jetpack/androidx/releases/appcompat?hl=zh-cn + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'com.google.android.material:material:1.2.0-alpha04' +} \ No newline at end of file diff --git a/copy/build.gradle b/copy/build.gradle index a3b507b4..f2eed7a1 100644 --- a/copy/build.gradle +++ b/copy/build.gradle @@ -1,15 +1,2 @@ apply plugin: 'com.android.library' - -android { - compileSdkVersion rootProject.ext.compileVersion - - defaultConfig { - minSdkVersion 8 - targetSdkVersion rootProject.ext.targetVersion - } -} - -dependencies { - implementation "androidx.appcompat:appcompat:$rootProject.ext.appcompatVersion" - implementation "com.google.android.material:material:$rootProject.ext.materialVersion" -} \ No newline at end of file +apply from: "../config.gradle" \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cffce4d0..10408a05 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ zipStoreBase = GRADLE_USER_HOME zipStorePath = wrapper/dists distributionBase = GRADLE_USER_HOME distributionPath = wrapper/dists -distributionUrl = https\://services.gradle.org/distributions/gradle-4.6-all.zip \ No newline at end of file +distributionUrl = https\://services.gradle.org/distributions/gradle-5.1.1-all.zip \ No newline at end of file diff --git a/image/build.gradle b/image/build.gradle deleted file mode 100644 index 29ce3de3..00000000 --- a/image/build.gradle +++ /dev/null @@ -1,23 +0,0 @@ -apply plugin: 'com.android.library' - -android { - compileSdkVersion rootProject.ext.compileVersion - - resourcePrefix "image_" - - defaultConfig { - minSdkVersion 14 - targetSdkVersion rootProject.ext.targetVersion - // 混淆配置 - consumerProguardFiles 'proguard-glide.pro' - } -} - -dependencies { - implementation "androidx.appcompat:appcompat:$rootProject.ext.appcompatVersion" - //implementation "com.google.android.material:material:$rootProject.ext.materialVersion" - - // 图片加载框架:https://github.com/bumptech/glide - implementation 'com.github.bumptech.glide:glide:4.9.0' - annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0' -} \ No newline at end of file diff --git a/image/proguard-glide.pro b/image/proguard-glide.pro deleted file mode 100644 index b3524642..00000000 --- a/image/proguard-glide.pro +++ /dev/null @@ -1,10 +0,0 @@ -# Glide --keep public class * implements com.bumptech.glide.module.GlideModule --keep public class * extends com.bumptech.glide.module.AppGlideModule --keep public enum com.bumptech.glide.load.ImageHeaderParser$** { - **[] $VALUES; - public *; -} - -# for DexGuard only -# -keepresourcexmlelements manifest/application/meta-data@value=GlideModule \ No newline at end of file diff --git a/image/src/main/AndroidManifest.xml b/image/src/main/AndroidManifest.xml deleted file mode 100644 index 989d6584..00000000 --- a/image/src/main/AndroidManifest.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/image/src/main/java/com/hjq/image/GlideFactory.java b/image/src/main/java/com/hjq/image/GlideFactory.java deleted file mode 100644 index 32371fbd..00000000 --- a/image/src/main/java/com/hjq/image/GlideFactory.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.hjq.image; - -import android.content.Context; -import android.graphics.drawable.Drawable; - -import androidx.core.content.ContextCompat; - -import com.bumptech.glide.Glide; -import com.hjq.copy.R; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/12/27 - * desc : Glide 加工厂 - */ -public final class GlideFactory implements ImageFactory { - - @Override - public GlideStrategy createImageStrategy() { - return new GlideStrategy(); - } - - @Override - public Drawable createPlaceholder(Context context) { - return ContextCompat.getDrawable(context, R.drawable.image_loading); - } - - @Override - public Drawable createError(Context context) { - return ContextCompat.getDrawable(context, R.drawable.image_load_err); - } - - @Override - public void clearMemoryCache(Context context) { - // 清除内存缓存(必须在主线程) - Glide.get(context).clearMemory(); - } - - @Override - public void clearDiskCache(final Context context) { - new Thread(new Runnable() { - @Override - public void run() { - // 清除本地缓存(必须在子线程) - Glide.get(context).clearDiskCache(); - } - }).start(); - } -} \ No newline at end of file diff --git a/image/src/main/java/com/hjq/image/GlideStrategy.java b/image/src/main/java/com/hjq/image/GlideStrategy.java deleted file mode 100644 index d9ab6eed..00000000 --- a/image/src/main/java/com/hjq/image/GlideStrategy.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.hjq.image; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.Fragment; -import android.content.Context; -import android.graphics.drawable.Drawable; - -import androidx.fragment.app.FragmentActivity; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.RequestBuilder; -import com.bumptech.glide.RequestManager; -import com.bumptech.glide.load.resource.bitmap.RoundedCorners; -import com.bumptech.glide.request.RequestOptions; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/12/26 - * desc : Glide 加载策略 - */ -final class GlideStrategy implements ImageStrategy { - - @SuppressLint("CheckResult") - @Override - public void load(ImageLoader loader) { - RequestManager manager = getRequestManager(loader.context); - - if (loader.isGif) { - manager.asGif(); - } - - final RequestBuilder builder; - if (loader.url != null && !"".equals(loader.url)) { - builder = manager.load(loader.url.trim()); - } else if (loader.resourceId != 0) { - builder = manager.load(loader.resourceId); - } else { - builder = manager.load(loader.error); - } - - if (loader.placeholder != null) { - final RequestOptions options = RequestOptions.errorOf(loader.error).placeholder(loader.placeholder); - if (loader.circle != 0) { - if (loader.circle == Integer.MAX_VALUE) { - // 裁剪成圆形 - options.circleCrop(); - } else { - // 圆角裁剪 - options.transform(new RoundedCorners(loader.circle)); - } - } - - builder.apply(options); - } - - if (loader.width != 0 && loader.height != 0) { - builder.override(loader.width, loader.height); - } - - builder.into(loader.view); - } - - /** - * 获取一个 Glide 的请求对象 - */ - private RequestManager getRequestManager(Object object) { - if (object == null) { - throw new IllegalArgumentException("You cannot start a load on a null Context"); - } else if (object instanceof Context) { - if (object instanceof FragmentActivity) { - return Glide.with((FragmentActivity) object); - } else if (object instanceof Activity) { - return Glide.with((Activity) object); - } else { - return Glide.with((Context) object); - } - } else if (object instanceof Fragment) { - return Glide.with((Fragment) object); - } else if (object instanceof androidx.fragment.app.Fragment) { - return Glide.with((androidx.fragment.app.Fragment) object); - } - // 如果不是上面这几种类型就直接抛出异常 - throw new IllegalArgumentException("This object is illegal"); - } -} \ No newline at end of file diff --git a/image/src/main/java/com/hjq/image/ImageFactory.java b/image/src/main/java/com/hjq/image/ImageFactory.java deleted file mode 100644 index f975e065..00000000 --- a/image/src/main/java/com/hjq/image/ImageFactory.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.hjq.image; - -import android.content.Context; -import android.graphics.drawable.Drawable; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/12/27 - * desc : 图片加载器生产机器 - */ -public interface ImageFactory { - - /** - * 创建一个图片加载策略 - */ - T createImageStrategy(); - - /** - * 创建加载占位图 - */ - Drawable createPlaceholder(Context context); - - /** - * 创建加载错误占位图 - */ - Drawable createError(Context context); - - /** - * 清除内存缓存 - */ - void clearMemoryCache(Context context); - - /** - * 清除磁盘缓存 - */ - void clearDiskCache(Context context); -} \ No newline at end of file diff --git a/image/src/main/java/com/hjq/image/ImageLoader.java b/image/src/main/java/com/hjq/image/ImageLoader.java deleted file mode 100644 index 353e8306..00000000 --- a/image/src/main/java/com/hjq/image/ImageLoader.java +++ /dev/null @@ -1,152 +0,0 @@ -package com.hjq.image; - -import android.app.Application; -import android.app.Fragment; -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.widget.ImageView; - -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; - -import java.io.File; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/12/26 - * desc : 图片加载器 - */ -public final class ImageLoader { - - /** 图片生产工厂 */ - private static ImageFactory sImageFactory; - /** 图片加载策略 */ - private static ImageStrategy sImageStrategy; - - /** 加载中占位图 */ - private static Drawable sPlaceholder; - /** 加载出错占位图 */ - private static Drawable sError; - - public static void init(Application application) { - // 使用 Glide 进行初始化图片加载器 - init(application, new GlideFactory()); - } - - /** - * 使用指定的图片加载器进行初始化 - * - * @param application 上下文对象 - * @param factory 图片加载器生成对象 - */ - public static void init(@NonNull Application application,@NonNull ImageFactory factory) { - sImageFactory = factory; - sImageStrategy = factory.createImageStrategy(); - sPlaceholder = factory.createPlaceholder(application); - sError = factory.createError(application); - } - - /** - * 清除图片缓存 - */ - public static void clear(Context context) { - clearMemoryCache(context); - clearDiskCache(context); - } - - /** - * 清除内存缓存 - */ - public static void clearMemoryCache(Context context) { - sImageFactory.clearMemoryCache(context); - } - - /** - * 清除磁盘缓存 - */ - public static void clearDiskCache(final Context context) { - sImageFactory.clearDiskCache(context); - } - - final Object context; - int circle; - String url; - @DrawableRes int resourceId; - boolean isGif; - - Drawable placeholder = sPlaceholder; - Drawable error = sError; - - int width; - int height; - - ImageView view; - - public ImageLoader(Object context) { - this.context = context; - } - - public static ImageLoader with(Context context) { - return new ImageLoader(context); - } - - public static ImageLoader with(Fragment fragment) { - return new ImageLoader(fragment); - } - - public static ImageLoader with(androidx.fragment.app.Fragment fragment) { - return new ImageLoader(fragment); - } - - public ImageLoader gif() { - this.isGif = true; - return this; - } - - public ImageLoader circle() { - return circle(Integer.MAX_VALUE); - } - - public ImageLoader circle(int circle) { - this.circle = circle; - return this; - } - - public ImageLoader load(String url) { - this.url = url; - return this; - } - - public ImageLoader load(File file) { - this.url = Uri.fromFile(file).toString(); - return this; - } - - public ImageLoader load(@DrawableRes int id) { - this.resourceId = id; - return this; - } - - public ImageLoader placeholder(Drawable placeholder) { - this.placeholder = placeholder; - return this; - } - - public ImageLoader error(Drawable error) { - this.error = error; - return this; - } - - public ImageLoader override(int width, int height) { - this.width = width; - this.height = height; - return this; - } - - public void into(ImageView view) { - this.view = view; - sImageStrategy.load(this); - } -} \ No newline at end of file diff --git a/image/src/main/java/com/hjq/image/ImageStrategy.java b/image/src/main/java/com/hjq/image/ImageStrategy.java deleted file mode 100644 index 3195154e..00000000 --- a/image/src/main/java/com/hjq/image/ImageStrategy.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.hjq.image; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/12/26 - * desc : 图片加载策略接口 - */ -public interface ImageStrategy { - - /** - * 加载图片 - */ - void load(ImageLoader loader); -} \ No newline at end of file diff --git a/image/src/main/res/drawable-hdpi/image_load_err.png b/image/src/main/res/drawable-hdpi/image_load_err.png deleted file mode 100644 index b697076c..00000000 Binary files a/image/src/main/res/drawable-hdpi/image_load_err.png and /dev/null differ diff --git a/image/src/main/res/drawable-hdpi/image_loading.png b/image/src/main/res/drawable-hdpi/image_loading.png deleted file mode 100644 index 59b2a9fa..00000000 Binary files a/image/src/main/res/drawable-hdpi/image_loading.png and /dev/null differ diff --git a/image/src/main/res/drawable-xhdpi/image_load_err.png b/image/src/main/res/drawable-xhdpi/image_load_err.png deleted file mode 100644 index d0dc886f..00000000 Binary files a/image/src/main/res/drawable-xhdpi/image_load_err.png and /dev/null differ diff --git a/image/src/main/res/drawable-xhdpi/image_loading.png b/image/src/main/res/drawable-xhdpi/image_loading.png deleted file mode 100644 index 47048a8f..00000000 Binary files a/image/src/main/res/drawable-xhdpi/image_loading.png and /dev/null differ diff --git a/picture/activity/1.jpg b/picture/activity/1.jpg new file mode 100644 index 00000000..679400a0 Binary files /dev/null and b/picture/activity/1.jpg differ diff --git a/picture/activity/1.png b/picture/activity/1.png deleted file mode 100644 index 867f4dfa..00000000 Binary files a/picture/activity/1.png and /dev/null differ diff --git a/picture/activity/10.jpg b/picture/activity/10.jpg new file mode 100644 index 00000000..a7100d63 Binary files /dev/null and b/picture/activity/10.jpg differ diff --git a/picture/activity/10.png b/picture/activity/10.png deleted file mode 100644 index 1b79cd61..00000000 Binary files a/picture/activity/10.png and /dev/null differ diff --git a/picture/activity/11.jpg b/picture/activity/11.jpg new file mode 100644 index 00000000..0ab1661b Binary files /dev/null and b/picture/activity/11.jpg differ diff --git a/picture/activity/11.png b/picture/activity/11.png deleted file mode 100644 index 57c6708f..00000000 Binary files a/picture/activity/11.png and /dev/null differ diff --git a/picture/activity/12.jpg b/picture/activity/12.jpg new file mode 100644 index 00000000..50762eca Binary files /dev/null and b/picture/activity/12.jpg differ diff --git a/picture/activity/12.png b/picture/activity/12.png deleted file mode 100644 index d8117e75..00000000 Binary files a/picture/activity/12.png and /dev/null differ diff --git a/picture/activity/13.jpg b/picture/activity/13.jpg new file mode 100644 index 00000000..0c2568bd Binary files /dev/null and b/picture/activity/13.jpg differ diff --git a/picture/activity/13.png b/picture/activity/13.png deleted file mode 100644 index 8bbd0e2e..00000000 Binary files a/picture/activity/13.png and /dev/null differ diff --git a/picture/activity/14.jpg b/picture/activity/14.jpg new file mode 100644 index 00000000..40056be4 Binary files /dev/null and b/picture/activity/14.jpg differ diff --git a/picture/activity/14.png b/picture/activity/14.png deleted file mode 100644 index 8f3317e1..00000000 Binary files a/picture/activity/14.png and /dev/null differ diff --git a/picture/activity/15.jpg b/picture/activity/15.jpg new file mode 100644 index 00000000..e8a9ec56 Binary files /dev/null and b/picture/activity/15.jpg differ diff --git a/picture/activity/15.png b/picture/activity/15.png deleted file mode 100644 index c656d025..00000000 Binary files a/picture/activity/15.png and /dev/null differ diff --git a/picture/activity/16.jpg b/picture/activity/16.jpg new file mode 100644 index 00000000..b20a57cb Binary files /dev/null and b/picture/activity/16.jpg differ diff --git a/picture/activity/16.png b/picture/activity/16.png deleted file mode 100644 index 859f9494..00000000 Binary files a/picture/activity/16.png and /dev/null differ diff --git a/picture/activity/17.jpg b/picture/activity/17.jpg new file mode 100644 index 00000000..fedd8af2 Binary files /dev/null and b/picture/activity/17.jpg differ diff --git a/picture/activity/17.png b/picture/activity/17.png deleted file mode 100644 index d25ecb8e..00000000 Binary files a/picture/activity/17.png and /dev/null differ diff --git a/picture/activity/18.jpg b/picture/activity/18.jpg new file mode 100644 index 00000000..99b729ae Binary files /dev/null and b/picture/activity/18.jpg differ diff --git a/picture/activity/18.png b/picture/activity/18.png deleted file mode 100644 index e0f3bf87..00000000 Binary files a/picture/activity/18.png and /dev/null differ diff --git a/picture/activity/19.jpg b/picture/activity/19.jpg new file mode 100644 index 00000000..5edb3ed1 Binary files /dev/null and b/picture/activity/19.jpg differ diff --git a/picture/activity/19.png b/picture/activity/19.png deleted file mode 100644 index af533e79..00000000 Binary files a/picture/activity/19.png and /dev/null differ diff --git a/picture/activity/2.jpg b/picture/activity/2.jpg new file mode 100644 index 00000000..535ca3a3 Binary files /dev/null and b/picture/activity/2.jpg differ diff --git a/picture/activity/2.png b/picture/activity/2.png deleted file mode 100644 index a5336379..00000000 Binary files a/picture/activity/2.png and /dev/null differ diff --git a/picture/activity/20.jpg b/picture/activity/20.jpg new file mode 100644 index 00000000..590a7674 Binary files /dev/null and b/picture/activity/20.jpg differ diff --git a/picture/activity/20.png b/picture/activity/20.png deleted file mode 100644 index 60852a67..00000000 Binary files a/picture/activity/20.png and /dev/null differ diff --git a/picture/activity/21.jpg b/picture/activity/21.jpg new file mode 100644 index 00000000..3afd1e9a Binary files /dev/null and b/picture/activity/21.jpg differ diff --git a/picture/activity/21.png b/picture/activity/21.png deleted file mode 100644 index 468472b3..00000000 Binary files a/picture/activity/21.png and /dev/null differ diff --git a/picture/activity/22.jpg b/picture/activity/22.jpg new file mode 100644 index 00000000..24a4f6a4 Binary files /dev/null and b/picture/activity/22.jpg differ diff --git a/picture/activity/22.png b/picture/activity/22.png deleted file mode 100644 index 3f3bb493..00000000 Binary files a/picture/activity/22.png and /dev/null differ diff --git a/picture/activity/23.jpg b/picture/activity/23.jpg new file mode 100644 index 00000000..c2c0a626 Binary files /dev/null and b/picture/activity/23.jpg differ diff --git a/picture/activity/24.jpg b/picture/activity/24.jpg new file mode 100644 index 00000000..fcfa35a0 Binary files /dev/null and b/picture/activity/24.jpg differ diff --git a/picture/activity/25.jpg b/picture/activity/25.jpg new file mode 100644 index 00000000..00f3e98a Binary files /dev/null and b/picture/activity/25.jpg differ diff --git a/picture/activity/26.jpg b/picture/activity/26.jpg new file mode 100644 index 00000000..7c0c6722 Binary files /dev/null and b/picture/activity/26.jpg differ diff --git a/picture/activity/27.jpg b/picture/activity/27.jpg new file mode 100644 index 00000000..343b6ee3 Binary files /dev/null and b/picture/activity/27.jpg differ diff --git a/picture/activity/28.jpg b/picture/activity/28.jpg new file mode 100644 index 00000000..045bfe34 Binary files /dev/null and b/picture/activity/28.jpg differ diff --git a/picture/activity/29.jpg b/picture/activity/29.jpg new file mode 100644 index 00000000..0bd2740c Binary files /dev/null and b/picture/activity/29.jpg differ diff --git a/picture/activity/3.jpg b/picture/activity/3.jpg new file mode 100644 index 00000000..9edd43bc Binary files /dev/null and b/picture/activity/3.jpg differ diff --git a/picture/activity/3.png b/picture/activity/3.png deleted file mode 100644 index 1fb1120e..00000000 Binary files a/picture/activity/3.png and /dev/null differ diff --git a/picture/activity/30.jpg b/picture/activity/30.jpg new file mode 100644 index 00000000..1bad43c3 Binary files /dev/null and b/picture/activity/30.jpg differ diff --git a/picture/activity/31.jpg b/picture/activity/31.jpg new file mode 100644 index 00000000..beac9c31 Binary files /dev/null and b/picture/activity/31.jpg differ diff --git a/picture/activity/4.jpg b/picture/activity/4.jpg new file mode 100644 index 00000000..0b701bc6 Binary files /dev/null and b/picture/activity/4.jpg differ diff --git a/picture/activity/4.png b/picture/activity/4.png deleted file mode 100644 index a50dfe30..00000000 Binary files a/picture/activity/4.png and /dev/null differ diff --git a/picture/activity/5.jpg b/picture/activity/5.jpg new file mode 100644 index 00000000..674a93bc Binary files /dev/null and b/picture/activity/5.jpg differ diff --git a/picture/activity/5.png b/picture/activity/5.png deleted file mode 100644 index ecbe333d..00000000 Binary files a/picture/activity/5.png and /dev/null differ diff --git a/picture/activity/6.jpg b/picture/activity/6.jpg new file mode 100644 index 00000000..ca473db7 Binary files /dev/null and b/picture/activity/6.jpg differ diff --git a/picture/activity/6.png b/picture/activity/6.png deleted file mode 100644 index b390f073..00000000 Binary files a/picture/activity/6.png and /dev/null differ diff --git a/picture/activity/7.jpg b/picture/activity/7.jpg new file mode 100644 index 00000000..32696728 Binary files /dev/null and b/picture/activity/7.jpg differ diff --git a/picture/activity/7.png b/picture/activity/7.png deleted file mode 100644 index db1df34a..00000000 Binary files a/picture/activity/7.png and /dev/null differ diff --git a/picture/activity/8.jpg b/picture/activity/8.jpg new file mode 100644 index 00000000..330fcf01 Binary files /dev/null and b/picture/activity/8.jpg differ diff --git a/picture/activity/8.png b/picture/activity/8.png deleted file mode 100644 index cbfa3917..00000000 Binary files a/picture/activity/8.png and /dev/null differ diff --git a/picture/activity/9.jpg b/picture/activity/9.jpg new file mode 100644 index 00000000..e48a1d57 Binary files /dev/null and b/picture/activity/9.jpg differ diff --git a/picture/activity/9.png b/picture/activity/9.png deleted file mode 100644 index 14cfff08..00000000 Binary files a/picture/activity/9.png and /dev/null differ diff --git a/picture/dialog/1.jpg b/picture/dialog/1.jpg new file mode 100644 index 00000000..dd985aff Binary files /dev/null and b/picture/dialog/1.jpg differ diff --git a/picture/dialog/1.png b/picture/dialog/1.png deleted file mode 100644 index 123d68f3..00000000 Binary files a/picture/dialog/1.png and /dev/null differ diff --git a/picture/dialog/10.jpg b/picture/dialog/10.jpg new file mode 100644 index 00000000..575e86d7 Binary files /dev/null and b/picture/dialog/10.jpg differ diff --git a/picture/dialog/10.png b/picture/dialog/10.png deleted file mode 100644 index c5b1f8ed..00000000 Binary files a/picture/dialog/10.png and /dev/null differ diff --git a/picture/dialog/11.jpg b/picture/dialog/11.jpg new file mode 100644 index 00000000..a68821bf Binary files /dev/null and b/picture/dialog/11.jpg differ diff --git a/picture/dialog/11.png b/picture/dialog/11.png deleted file mode 100644 index e0b72a7c..00000000 Binary files a/picture/dialog/11.png and /dev/null differ diff --git a/picture/dialog/12.jpg b/picture/dialog/12.jpg new file mode 100644 index 00000000..e7643857 Binary files /dev/null and b/picture/dialog/12.jpg differ diff --git a/picture/dialog/12.png b/picture/dialog/12.png deleted file mode 100644 index d6e7d94b..00000000 Binary files a/picture/dialog/12.png and /dev/null differ diff --git a/picture/dialog/13.jpg b/picture/dialog/13.jpg new file mode 100644 index 00000000..35660698 Binary files /dev/null and b/picture/dialog/13.jpg differ diff --git a/picture/dialog/13.png b/picture/dialog/13.png deleted file mode 100644 index 57236759..00000000 Binary files a/picture/dialog/13.png and /dev/null differ diff --git a/picture/dialog/14.jpg b/picture/dialog/14.jpg new file mode 100644 index 00000000..67f7cb7a Binary files /dev/null and b/picture/dialog/14.jpg differ diff --git a/picture/dialog/14.png b/picture/dialog/14.png deleted file mode 100644 index 22ae286b..00000000 Binary files a/picture/dialog/14.png and /dev/null differ diff --git a/picture/dialog/15.jpg b/picture/dialog/15.jpg new file mode 100644 index 00000000..8e364d76 Binary files /dev/null and b/picture/dialog/15.jpg differ diff --git a/picture/dialog/15.png b/picture/dialog/15.png deleted file mode 100644 index 51d53fd5..00000000 Binary files a/picture/dialog/15.png and /dev/null differ diff --git a/picture/dialog/16.jpg b/picture/dialog/16.jpg new file mode 100644 index 00000000..044bfdb9 Binary files /dev/null and b/picture/dialog/16.jpg differ diff --git a/picture/dialog/17.jpg b/picture/dialog/17.jpg new file mode 100644 index 00000000..171dccfa Binary files /dev/null and b/picture/dialog/17.jpg differ diff --git a/picture/dialog/18.jpg b/picture/dialog/18.jpg new file mode 100644 index 00000000..769468e0 Binary files /dev/null and b/picture/dialog/18.jpg differ diff --git a/picture/dialog/2.jpg b/picture/dialog/2.jpg new file mode 100644 index 00000000..641a4037 Binary files /dev/null and b/picture/dialog/2.jpg differ diff --git a/picture/dialog/2.png b/picture/dialog/2.png deleted file mode 100644 index 6c404662..00000000 Binary files a/picture/dialog/2.png and /dev/null differ diff --git a/picture/dialog/3.jpg b/picture/dialog/3.jpg new file mode 100644 index 00000000..b690af9e Binary files /dev/null and b/picture/dialog/3.jpg differ diff --git a/picture/dialog/3.png b/picture/dialog/3.png deleted file mode 100644 index 553f5098..00000000 Binary files a/picture/dialog/3.png and /dev/null differ diff --git a/picture/dialog/4.jpg b/picture/dialog/4.jpg new file mode 100644 index 00000000..288a2d84 Binary files /dev/null and b/picture/dialog/4.jpg differ diff --git a/picture/dialog/4.png b/picture/dialog/4.png deleted file mode 100644 index 071fb43a..00000000 Binary files a/picture/dialog/4.png and /dev/null differ diff --git a/picture/dialog/5.jpg b/picture/dialog/5.jpg new file mode 100644 index 00000000..c9ba51ab Binary files /dev/null and b/picture/dialog/5.jpg differ diff --git a/picture/dialog/5.png b/picture/dialog/5.png deleted file mode 100644 index 4adb1ec2..00000000 Binary files a/picture/dialog/5.png and /dev/null differ diff --git a/picture/dialog/6.jpg b/picture/dialog/6.jpg new file mode 100644 index 00000000..17baae87 Binary files /dev/null and b/picture/dialog/6.jpg differ diff --git a/picture/dialog/6.png b/picture/dialog/6.png deleted file mode 100644 index 4e613115..00000000 Binary files a/picture/dialog/6.png and /dev/null differ diff --git a/picture/dialog/7.jpg b/picture/dialog/7.jpg new file mode 100644 index 00000000..b3cd974e Binary files /dev/null and b/picture/dialog/7.jpg differ diff --git a/picture/dialog/7.png b/picture/dialog/7.png deleted file mode 100644 index b491c90b..00000000 Binary files a/picture/dialog/7.png and /dev/null differ diff --git a/picture/dialog/8.jpg b/picture/dialog/8.jpg new file mode 100644 index 00000000..1b0f3055 Binary files /dev/null and b/picture/dialog/8.jpg differ diff --git a/picture/dialog/8.png b/picture/dialog/8.png deleted file mode 100644 index b4756a17..00000000 Binary files a/picture/dialog/8.png and /dev/null differ diff --git a/picture/dialog/9.jpg b/picture/dialog/9.jpg new file mode 100644 index 00000000..72d427f3 Binary files /dev/null and b/picture/dialog/9.jpg differ diff --git a/picture/dialog/9.png b/picture/dialog/9.png deleted file mode 100644 index 8b99d03d..00000000 Binary files a/picture/dialog/9.png and /dev/null differ diff --git a/picture/gif/1.gif b/picture/gif/1.gif index 4b723cac..9e51f1af 100644 Binary files a/picture/gif/1.gif and b/picture/gif/1.gif differ diff --git a/settings.gradle b/settings.gradle index 325bdca5..57821ab3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,4 @@ include ':app' include ':base' include ':widget' -include ':image' include ':umeng' \ No newline at end of file diff --git a/umeng/build.gradle b/umeng/build.gradle index c4fd8d90..9fc4e944 100644 --- a/umeng/build.gradle +++ b/umeng/build.gradle @@ -1,22 +1,24 @@ apply plugin: 'com.android.library' +apply from: "../config.gradle" android { - compileSdkVersion rootProject.ext.compileVersion - - resourcePrefix "umeng_" defaultConfig { - minSdkVersion 8 - targetSdkVersion rootProject.ext.targetVersion // 混淆配置 consumerProguardFiles 'proguard-umeng.pro' - } -} -dependencies { - implementation "androidx.appcompat:appcompat:$rootProject.ext.appcompatVersion" - // implementation "com.google.android.material:material:$rootProject.ext.materialVersion" - - // 依赖 libs 目录下所有 Jar 包 - implementation fileTree(include: ['*.jar'], dir: 'libs') + // 清单占位符 + manifestPlaceholders = [ + // 友盟 AppKey + "UMENG_APPKEY" : "5cb16d93570df399fd0014e2", + // QQ AppId + "QQ_APPID" : "101828096", + // QQ AppKey + "QQ_APPKEY" : "9dfd3300c3aa3c4596a07796c64914b2", + // 微信 AppId + "WX_APPID" : "wxdc1e388c3822c80b", + // 微信 AppKey + "WX_APPKEY" : "3baf1193c85774b3fd9d18447d76cab0" + ] + } } \ No newline at end of file diff --git a/umeng/src/main/AndroidManifest.xml b/umeng/src/main/AndroidManifest.xml index a12887cc..fddba513 100644 --- a/umeng/src/main/AndroidManifest.xml +++ b/umeng/src/main/AndroidManifest.xml @@ -1,72 +1,69 @@ - + + - - - + - - + + - - - - + + android:value="${UMENG_APPKEY}" /> - + - + android:value="${QQ_APPID}" /> + + android:value="${QQ_APPKEY}" /> - + - + android:value="${WX_APPID}" /> + + android:value="${WX_APPKEY}" /> - + + android:noHistory="true"> + - + + + android:configChanges="orientation|keyboardHidden|screenSize" + android:theme="@android:style/Theme.Translucent.NoTitleBar" /> + diff --git a/umeng/src/main/java/com/hjq/demo/wxapi/WXEntryActivity.java b/umeng/src/main/java/com/hjq/demo/wxapi/WXEntryActivity.java index bfb8bf35..b3088ef7 100644 --- a/umeng/src/main/java/com/hjq/demo/wxapi/WXEntryActivity.java +++ b/umeng/src/main/java/com/hjq/demo/wxapi/WXEntryActivity.java @@ -8,4 +8,4 @@ * time : 2019/05/06 * desc : 微信登录回调(请注意这个 Activity 放置的包名要和当前项目的包名保持一致,否则将不能正常回调) */ -public class WXEntryActivity extends WXCallbackActivity {} \ No newline at end of file +public final class WXEntryActivity extends WXCallbackActivity {} \ No newline at end of file diff --git a/umeng/src/main/java/com/hjq/umeng/UmengClient.java b/umeng/src/main/java/com/hjq/umeng/UmengClient.java index c1e57933..cb02f187 100644 --- a/umeng/src/main/java/com/hjq/umeng/UmengClient.java +++ b/umeng/src/main/java/com/hjq/umeng/UmengClient.java @@ -37,8 +37,8 @@ public static void init(Application application) { // 初始化各个平台的 Key PlatformConfig.setWeixin(String.valueOf(metaData.get("WX_APPID")), String.valueOf(metaData.get("WX_APPKEY"))); PlatformConfig.setQQZone(String.valueOf(metaData.get("QQ_APPID")), String.valueOf(metaData.get("QQ_APPKEY"))); - PlatformConfig.setSinaWeibo(String.valueOf(metaData.get("SN_APPID")), String.valueOf(metaData.get("SN_APPKEY")),"http://sns.whalecloud.com"); + //PlatformConfig.setSinaWeibo(String.valueOf(metaData.get("SN_APPID")), String.valueOf(metaData.get("SN_APPKEY")),"http://sns.whalecloud.com"); // 豆瓣RENREN平台目前只能在服务器端配置 //PlatformConfig.setYixin("yxc0614e80c9304c11b0391514d09f13bf"); //PlatformConfig.setTwitter("3aIN7fuF685MuZ7jtXkQxalyi", "MK6FEYG63eWcpDFgRYw4w9puJhzDl0tyuqWjZ3M7XJuuG7mMbO"); @@ -156,9 +156,9 @@ public static boolean isAppInstalled(Context context, Platform platform) { private static boolean isAppInstalled(Context context, @NonNull final String packageName) { try { - return context.getPackageManager().getApplicationInfo(packageName, 0) != null; - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); + context.getPackageManager().getApplicationInfo(packageName, 0); + return true; + } catch (PackageManager.NameNotFoundException ignored) { return false; } } diff --git a/umeng/src/main/java/com/hjq/umeng/UmengLogin.java b/umeng/src/main/java/com/hjq/umeng/UmengLogin.java index 3e6022b0..f16c6377 100644 --- a/umeng/src/main/java/com/hjq/umeng/UmengLogin.java +++ b/umeng/src/main/java/com/hjq/umeng/UmengLogin.java @@ -3,7 +3,7 @@ import com.umeng.socialize.UMAuthListener; import com.umeng.socialize.bean.SHARE_MEDIA; -import java.lang.ref.WeakReference; +import java.lang.ref.SoftReference; import java.util.Map; /** @@ -23,17 +23,16 @@ public static final class LoginData { /** 性别 */ private final String mSex; /** 头像 */ - private final String mIcon; + private final String mAvatar; /** Token */ private final String mToken; - @SuppressWarnings("all") LoginData(Map data) { // 第三方登录获取用户资料:https://developer.umeng.com/docs/66632/detail/66639#h3-u83B7u53D6u7528u6237u8D44u6599 mId = data.get("uid"); mName = data.get("name"); mSex = data.get("gender"); - mIcon = data.get("iconurl"); + mAvatar = data.get("iconurl"); mToken = data.get("accessToken"); } @@ -45,8 +44,8 @@ public String getSex() { return mSex; } - public String getIcon() { - return mIcon; + public String getAvatar() { + return mAvatar; } public String getId() { @@ -68,7 +67,7 @@ public boolean isMan() { /** * 为什么要用软引用,因为友盟会将监听回调(UMAuthListener)持有成静态的 */ - public static final class LoginListenerWrapper extends WeakReference implements UMAuthListener { + public static final class LoginListenerWrapper extends SoftReference implements UMAuthListener { private final Platform mPlatform; @@ -92,11 +91,7 @@ public static final class LoginListenerWrapper extends WeakReference implements UMShareListener { + public static final class ShareListenerWrapper extends SoftReference implements UMShareListener { private final Platform mPlatform; @@ -104,11 +104,7 @@ public static final class ShareListenerWrapper extends WeakReference= Build.VERSION_CODES.JELLY_BEAN) { - setBackground(drawable); - } else { - setBackgroundDrawable(drawable); - } + StateListDrawable drawable = new StateListDrawable(); + drawable.addState(new int[]{android.R.attr.state_pressed}, new ColorDrawable(ContextCompat.getColor(getContext(), R.color.black5))); + drawable.addState(new int[]{android.R.attr.state_selected}, new ColorDrawable(ContextCompat.getColor(getContext(), R.color.black5))); + drawable.addState(new int[]{android.R.attr.state_focused}, new ColorDrawable(ContextCompat.getColor(getContext(), R.color.black5))); + drawable.addState(new int[]{}, new ColorDrawable(ContextCompat.getColor(getContext(), R.color.white))); + setBackground(drawable); + + // 必须要设置可点击,否则点击屏幕任何角落都会触发按压事件 + setFocusable(true); + setClickable(true); } - // 回收TypedArray array.recycle(); } @@ -265,11 +289,7 @@ public SettingBar setLineColor(@ColorInt int color) { return setLineDrawable(new ColorDrawable(color)); } public SettingBar setLineDrawable(Drawable drawable) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - mLineView.setBackground(drawable); - } else { - mLineView.setBackgroundDrawable(drawable); - } + mLineView.setBackground(drawable); return this; } @@ -295,21 +315,28 @@ public SettingBar setLineMargin(int margin) { } /** - * 获取左标题View对象 + * 获取主布局 + */ + public LinearLayout getMainLayout() { + return mMainLayout; + } + + /** + * 获取左标题 */ public TextView getLeftView() { return mLeftView; } /** - * 获取右标题View对象 + * 获取右标题 */ public TextView getRightView() { return mRightView; } /** - * 获取分割线View对象 + * 获取分割线 */ public View getLineView() { return mLineView; diff --git a/widget/src/main/java/com/hjq/widget/layout/SimpleLayout.java b/widget/src/main/java/com/hjq/widget/layout/SimpleLayout.java index 9e1e2193..86e02abf 100644 --- a/widget/src/main/java/com/hjq/widget/layout/SimpleLayout.java +++ b/widget/src/main/java/com/hjq/widget/layout/SimpleLayout.java @@ -11,7 +11,7 @@ * time : 2018/10/18 * desc : 简单的 Layout(常用于自定义组合控件继承的基类,可以起到性能优化的作用) */ -public class SimpleLayout extends ViewGroup { +public final class SimpleLayout extends ViewGroup { public SimpleLayout(Context context) { super(context); diff --git a/widget/src/main/java/com/hjq/widget/layout/WrapRecyclerView.java b/widget/src/main/java/com/hjq/widget/layout/WrapRecyclerView.java new file mode 100644 index 00000000..1daccff1 --- /dev/null +++ b/widget/src/main/java/com/hjq/widget/layout/WrapRecyclerView.java @@ -0,0 +1,476 @@ +package com.hjq.widget.layout; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; + +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.List; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/09/21 + * desc : 支持添加底部和头部的 RecyclerView + */ +public final class WrapRecyclerView extends RecyclerView { + + /** 原有的适配器 */ + private RecyclerView.Adapter mRealAdapter; + + /** 支持添加头部和底部的适配器 */ + private final WrapRecyclerAdapter mWrapAdapter = new WrapRecyclerAdapter(); + + public WrapRecyclerView(Context context) { + super(context); + } + + public WrapRecyclerView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public WrapRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public void setAdapter(Adapter adapter) { + mRealAdapter = adapter; + // 偷梁换柱 + mWrapAdapter.setRealAdapter(mRealAdapter); + super.setAdapter(mWrapAdapter); + } + + @Override + public Adapter getAdapter() { + return mRealAdapter; + } + + /** + * 添加头部View + */ + public void addHeaderView(View view) { + mWrapAdapter.addHeaderView(view); + } + + @SuppressWarnings("unchecked") + public V addHeaderView(@LayoutRes int id) { + View headerView = LayoutInflater.from(getContext()).inflate(id, this, false); + addHeaderView(headerView); + return (V) headerView; + } + + /** + * 移除头部View + */ + public void removeHeaderView(View view) { + mWrapAdapter.removeHeaderView(view); + } + + /** + * 添加底部View + */ + public void addFooterView(View view) { + mWrapAdapter.addFooterView(view); + } + + @SuppressWarnings("unchecked") + public V addFooterView(@LayoutRes int id) { + View footerView = LayoutInflater.from(getContext()).inflate(id, this, false); + addFooterView(footerView); + return (V) footerView; + } + + /** + * 移除底部View + */ + public void removeFooterView(View view) { + mWrapAdapter.removeFooterView(view); + } + + /** + * 获取头部View总数 + */ + public int getHeaderViewsCount() { + return mWrapAdapter.getHeaderViewsCount(); + } + + /** + * 获取底部View总数 + */ + public int getFooterViewsCount() { + return mWrapAdapter.getFooterViewsCount(); + } + + /** + * 获取头部View集合 + */ + public List getHeaderViews() { + return mWrapAdapter.getHeaderViews(); + } + + /** + * 获取底部View集合 + */ + public List getFooterViews() { + return mWrapAdapter.getFooterViews(); + } + + /** + * 刷新头部和底部布局所有的 View 的状态 + */ + public void refreshHeaderFooterViews() { + mWrapAdapter.notifyDataSetChanged(); + } + + /** + * 采用装饰设计模式,将原有的适配器包装起来 + */ + private static final class WrapRecyclerAdapter extends RecyclerView.Adapter { + + /** 头部条目类型 */ + private static final int HEADER_VIEW_TYPE = Integer.MIN_VALUE >> 1; + /** 底部条目类型 */ + private static final int FOOTER_VIEW_TYPE = Integer.MAX_VALUE >> 1; + + /** 原有的适配器 */ + private RecyclerView.Adapter mRealAdapter; + /** 头部View集合 */ + private final List mHeaderViews = new ArrayList<>(); + /** 底部View集合 */ + private final List mFooterViews = new ArrayList<>(); + /** 当前调用的位置 */ + private int mCurrentPosition; + + /** RecyclerView对象 */ + private RecyclerView mRecyclerView; + + /** 数据观察者对象 */ + private WrapAdapterDataObserver mObserver; + + private void setRealAdapter(RecyclerView.Adapter adapter) { + if (mRealAdapter != adapter) { + + if (mRealAdapter != null) { + if (mObserver != null) { + // 为原有的RecyclerAdapter移除数据监听对象 + mRealAdapter.unregisterAdapterDataObserver(mObserver); + } + } + + mRealAdapter = adapter; + if (mRealAdapter != null) { + if (mObserver == null) { + mObserver = new WrapAdapterDataObserver(this); + } + // 为原有的RecyclerAdapter添加数据监听对象 + mRealAdapter.registerAdapterDataObserver(mObserver); + // 适配器不是第一次被绑定到RecyclerView上需要发送通知,因为第一次绑定会自动通知 + if (mRecyclerView != null) { + notifyDataSetChanged(); + } + } + } + } + + @Override + public int getItemCount() { + if (mRealAdapter != null) { + return getHeaderViewsCount() + mRealAdapter.getItemCount() + getFooterViewsCount(); + }else { + return getHeaderViewsCount() + getFooterViewsCount(); + } + } + + @SuppressWarnings("all") + @Override + public int getItemViewType(int position) { + mCurrentPosition = position; + // 获取头部布局的总数 + int headerCount = getHeaderViewsCount(); + // 获取原有适配器的总数 + int adapterCount = mRealAdapter != null ? mRealAdapter.getItemCount() : 0; + // 获取在原有适配器上的位置 + int adjPosition = position - headerCount; + if (position < headerCount) { + return HEADER_VIEW_TYPE; + }else if (adjPosition < adapterCount) { + return mRealAdapter.getItemViewType(adjPosition); + }else { + return FOOTER_VIEW_TYPE; + } + } + + public int getPosition() { + return mCurrentPosition; + } + + @SuppressWarnings("all") + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + switch (viewType) { + case HEADER_VIEW_TYPE: + return newWrapViewHolder(mHeaderViews.get(getPosition())); + case FOOTER_VIEW_TYPE: + return newWrapViewHolder(mFooterViews.get(getPosition() - getHeaderViewsCount() - (mRealAdapter != null ? mRealAdapter.getItemCount() : 0))); + default: + int itemViewType = mRealAdapter.getItemViewType(getPosition() - getHeaderViewsCount()); + if (itemViewType == HEADER_VIEW_TYPE || itemViewType == FOOTER_VIEW_TYPE) { + throw new IllegalStateException("Please do not use this type as itemType"); + } + if (mRealAdapter != null) { + return mRealAdapter.onCreateViewHolder(parent, itemViewType); + } else { + return null; + } + } + } + + @SuppressWarnings("unchecked") + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + int viewType = getItemViewType(position); + switch (viewType) { + case HEADER_VIEW_TYPE: + case FOOTER_VIEW_TYPE: + break; + default: + if (mRealAdapter != null) { + mRealAdapter.onBindViewHolder(holder, getPosition() - getHeaderViewsCount()); + } + break; + } + } + + private WrapViewHolder newWrapViewHolder(View view) { + ViewParent parent = view.getParent(); + if (parent instanceof ViewGroup) { + // IllegalStateException: ViewHolder views must not be attached when created. + // Ensure that you are not passing 'true' to the attachToRoot parameter of LayoutInflater.inflate(..., boolean attachToRoot) + ((ViewGroup) parent).removeView(view); + } + return new WrapViewHolder(view); + } + + @Override + public long getItemId(int position) { + if (mRealAdapter != null && position > getHeaderViewsCount() - 1 && position < getHeaderViewsCount() + mRealAdapter.getItemCount()) { + return mRealAdapter.getItemId(position - getHeaderViewsCount()); + } else { + return super.getItemId(position); + } + } + + @Override + public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { + mRecyclerView = recyclerView; + if (mRealAdapter != null) { + mRealAdapter.onAttachedToRecyclerView(recyclerView); + } + } + + @Override + public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) { + mRecyclerView = null; + if (mRealAdapter != null) { + mRealAdapter.onDetachedFromRecyclerView(recyclerView); + } + } + + @SuppressWarnings("unchecked") + @Override + public void onViewRecycled(@NonNull ViewHolder holder) { + if (holder instanceof WrapViewHolder) { + // 防止这个 ViewHolder 被 RecyclerView 拿去复用 + holder.setIsRecyclable(false); + return; + } + if (mRealAdapter != null) { + mRealAdapter.onViewRecycled(holder); + } + } + + @SuppressWarnings("unchecked") + @Override + public boolean onFailedToRecycleView(@NonNull ViewHolder holder) { + if (mRealAdapter != null) { + return mRealAdapter.onFailedToRecycleView(holder); + } + return super.onFailedToRecycleView(holder); + } + + @SuppressWarnings("unchecked") + @Override + public void onViewAttachedToWindow(@NonNull ViewHolder holder) { + if (mRealAdapter != null) { + mRealAdapter.onViewAttachedToWindow(holder); + } + } + + @SuppressWarnings("unchecked") + @Override + public void onViewDetachedFromWindow(@NonNull ViewHolder holder) { + if (mRealAdapter != null) { + mRealAdapter.onViewDetachedFromWindow(holder); + } + } + + /** + * 添加头部View + */ + private void addHeaderView(View view) { + // 不能添加同一个View对象,否则会导致RecyclerView复用异常 + if (!mHeaderViews.contains(view) && !mFooterViews.contains(view)) { + adjustSpanSize(mRecyclerView, this, mRealAdapter); + mHeaderViews.add(view); + notifyDataSetChanged(); + } + } + + /** + * 移除头部View + */ + private void removeHeaderView(View view) { + if (mHeaderViews.remove(view)) { + notifyDataSetChanged(); + } + } + + /** + * 添加底部View + */ + private void addFooterView(View view) { + // 不能添加同一个View对象,否则会导致RecyclerView复用异常 + if (!mFooterViews.contains(view) && !mHeaderViews.contains(view)) { + adjustSpanSize(mRecyclerView, this, mRealAdapter); + mFooterViews.add(view); + notifyDataSetChanged(); + } + } + + /** + * 移除底部View + */ + private void removeFooterView(View view) { + if (mFooterViews.remove(view)) { + notifyDataSetChanged(); + } + } + + /** + * 获取头部View总数 + */ + private int getHeaderViewsCount() { + return mHeaderViews.size(); + } + + /** + * 获取底部View总数 + */ + private int getFooterViewsCount() { + return mFooterViews.size(); + } + + /** + * 获取头部View集合 + */ + private List getHeaderViews() { + return mHeaderViews; + } + + /** + * 获取底部View集合 + */ + private List getFooterViews() { + return mFooterViews; + } + + /** + * 添加头部尾部的时候,在 RecyclerView 模式是配置 GridLayoutManager 的时候,发现头部会跑到第一格,也就不是独立一行的效果 + * + * @param recyclerView RecyclerView对象 + * @param wrapAdapter 增强的适配器 + * @param realAdapter 原有的适配器 + */ + private static void adjustSpanSize(RecyclerView recyclerView, final WrapRecyclerAdapter wrapAdapter, final RecyclerView.Adapter realAdapter) { + + if (recyclerView == null || wrapAdapter == null || realAdapter == null) { + return; + } + + final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); + if (layoutManager instanceof GridLayoutManager) { + ((GridLayoutManager) layoutManager).setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { + + @Override + public int getSpanSize(int position) { + return (position < wrapAdapter.getHeaderViewsCount() + || position >= wrapAdapter.getHeaderViewsCount() + realAdapter.getItemCount()) + ? ((GridLayoutManager) layoutManager).getSpanCount() : 1; + } + }); + } + } + } + + /** + * 头部和底部通用的ViewHolder对象 + */ + private static final class WrapViewHolder extends RecyclerView.ViewHolder { + + private WrapViewHolder(View itemView) { + super(itemView); + } + } + + /** + * 数据改变监听器 + */ + private static final class WrapAdapterDataObserver extends RecyclerView.AdapterDataObserver { + + private final WrapRecyclerAdapter mWrapAdapter; + + private WrapAdapterDataObserver(WrapRecyclerAdapter adapter) { + mWrapAdapter = adapter; + } + + @Override + public void onChanged() { + mWrapAdapter.notifyDataSetChanged(); + } + + @Override + public void onItemRangeChanged(int positionStart, int itemCount) { + mWrapAdapter.notifyItemRangeChanged(mWrapAdapter.getHeaderViewsCount() + positionStart, itemCount); + } + + @Override + public void onItemRangeChanged(int positionStart, int itemCount, Object payload) { + onItemRangeChanged(mWrapAdapter.getHeaderViewsCount() + positionStart, itemCount); + } + + @Override + public void onItemRangeInserted(int positionStart, int itemCount) { + mWrapAdapter.notifyItemRangeInserted(mWrapAdapter.getHeaderViewsCount() + positionStart, itemCount); + } + + @Override + public void onItemRangeRemoved(int positionStart, int itemCount) { + mWrapAdapter.notifyItemRangeRemoved(mWrapAdapter.getHeaderViewsCount() + positionStart, itemCount); + } + + @Override + public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { + mWrapAdapter.notifyItemMoved(mWrapAdapter.getHeaderViewsCount() + fromPosition, toPosition); + } + } +} \ No newline at end of file diff --git a/widget/src/main/java/com/hjq/widget/view/AnimImageView.java b/widget/src/main/java/com/hjq/widget/view/AnimImageView.java deleted file mode 100644 index 8945751a..00000000 --- a/widget/src/main/java/com/hjq/widget/view/AnimImageView.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.hjq.widget.view; - -import android.content.Context; -import android.graphics.drawable.AnimationDrawable; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; - -import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatImageView; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2019/07/06 - * desc : 自动播放帧动画的 ImageView - */ -public final class AnimImageView extends AppCompatImageView { - - public AnimImageView(Context context) { - super(context); - } - - public AnimImageView(Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - } - - public AnimImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @Override - public void setImageDrawable(@Nullable Drawable drawable) { - super.setImageDrawable(drawable); - // 默认开启帧动画 - startAnimation(); - } - - /** - * 开启帧动画 - */ - public void startAnimation() { - // 判断当前的 Drawable 是否为帧动画 - if (getDrawable() instanceof AnimationDrawable) { - // 如果是的话自动播放动画 - ((AnimationDrawable) getDrawable()).start(); - } - } - - /** - * 停止帧动画 - */ - public void stopAnimation() { - // 判断当前的 Drawable 是否为帧动画 - if (getDrawable() instanceof AnimationDrawable) { - // 如果是的话自动播放动画 - ((AnimationDrawable) getDrawable()).stop(); - } - } -} \ No newline at end of file diff --git a/widget/src/main/java/com/hjq/widget/view/ClearEditText.java b/widget/src/main/java/com/hjq/widget/view/ClearEditText.java index 81cd9227..1643cd44 100644 --- a/widget/src/main/java/com/hjq/widget/view/ClearEditText.java +++ b/widget/src/main/java/com/hjq/widget/view/ClearEditText.java @@ -14,6 +14,8 @@ import com.hjq.widget.R; +import java.util.Objects; + /** * author : Android 轮子哥 * github : https://github.com/getActivity/AndroidProject @@ -30,25 +32,18 @@ public final class ClearEditText extends RegexEditText private OnFocusChangeListener mOnFocusChangeListener; public ClearEditText(Context context) { - super(context); + this(context, null); } public ClearEditText(Context context, AttributeSet attrs) { - super(context, attrs); + this(context, attrs, android.R.attr.editTextStyle); } + @SuppressLint("ClickableViewAccessibility") public ClearEditText(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - } - - @SuppressWarnings("all") - @SuppressLint("ClickableViewAccessibility") - @Override - protected void initialize(Context context, AttributeSet attrs) { - super.initialize(context, attrs); - // Wrap the drawable so that it can be tinted pre Lollipop - mClearDrawable = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_input_delete)); + mClearDrawable = DrawableCompat.wrap(Objects.requireNonNull(ContextCompat.getDrawable(context, R.drawable.ic_input_delete))); mClearDrawable.setBounds(0, 0, mClearDrawable.getIntrinsicWidth(), mClearDrawable.getIntrinsicHeight()); setDrawableVisible(false); super.setOnTouchListener(this); @@ -81,7 +76,7 @@ public void setOnTouchListener(final OnTouchListener onTouchListener) { } /** - * {@link View.OnFocusChangeListener} + * {@link OnFocusChangeListener} */ @Override @@ -97,7 +92,7 @@ public void onFocusChange(final View view, final boolean hasFocus) { } /** - * {@link View.OnTouchListener} + * {@link OnTouchListener} */ @Override diff --git a/widget/src/main/java/com/hjq/widget/view/CountdownView.java b/widget/src/main/java/com/hjq/widget/view/CountdownView.java index 3433996f..43ae31f7 100644 --- a/widget/src/main/java/com/hjq/widget/view/CountdownView.java +++ b/widget/src/main/java/com/hjq/widget/view/CountdownView.java @@ -24,8 +24,6 @@ public final class CountdownView extends AppCompatTextView implements Runnable { private int mCurrentSecond; /** 记录原有的文本 */ private CharSequence mRecordText; - /** 标记是否重置了倒计控件 */ - private boolean mFlag; public CountdownView(Context context) { super(context); @@ -47,10 +45,21 @@ public void setTotalTime(int totalTime) { } /** - * 重置倒计时控件 + * 开始倒计时 */ - public void resetState() { - mFlag = true; + public void start() { + mRecordText = getText(); + setEnabled(false); + mCurrentSecond = mTotalSecond; + post(this); + } + + /** + * 结束倒计时 + */ + public void stop() { + setText(mRecordText); + setEnabled(true); } @Override @@ -67,26 +76,11 @@ protected void onDetachedFromWindow() { super.onDetachedFromWindow(); } - @Override - public boolean performClick() { - boolean click = super.performClick(); - mRecordText = getText(); - setEnabled(false); - mCurrentSecond = mTotalSecond; - post(this); - return click; - } - - /** - * {@link Runnable} - */ @SuppressLint("SetTextI18n") @Override public void run() { - if (mCurrentSecond == 0 || mFlag) { - setText(mRecordText); - setEnabled(true); - mFlag = false; + if (mCurrentSecond == 0) { + stop(); } else { mCurrentSecond--; setText(mCurrentSecond + " " + TIME_UNIT); diff --git a/widget/src/main/java/com/hjq/widget/view/PasswordEditText.java b/widget/src/main/java/com/hjq/widget/view/PasswordEditText.java index e8c393da..56d951a5 100644 --- a/widget/src/main/java/com/hjq/widget/view/PasswordEditText.java +++ b/widget/src/main/java/com/hjq/widget/view/PasswordEditText.java @@ -29,35 +29,30 @@ public final class PasswordEditText extends RegexEditText private static final int TYPE_INVISIBLE = InputType.TYPE_TEXT_VARIATION_PASSWORD; private Drawable mCurrentDrawable; - private Drawable mVisibleDrawable; - private Drawable mInvisibleDrawable; + private final Drawable mVisibleDrawable; + private final Drawable mInvisibleDrawable; private OnTouchListener mOnTouchListener; private OnFocusChangeListener mOnFocusChangeListener; public PasswordEditText(Context context) { - super(context); + this(context, null); } public PasswordEditText(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public PasswordEditText(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); + this(context, attrs, android.R.attr.editTextStyle); } @SuppressWarnings("all") @SuppressLint("ClickableViewAccessibility") - @Override - protected void initialize(Context context, AttributeSet attrs) { - super.initialize(context, attrs); + public PasswordEditText(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); // Wrap the drawable so that it can be tinted pre Lollipop - mVisibleDrawable = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_input_show)); + mVisibleDrawable = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_input_hide)); mVisibleDrawable.setBounds(0, 0, mVisibleDrawable.getIntrinsicWidth(), mVisibleDrawable.getIntrinsicHeight()); - mInvisibleDrawable = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_input_hide)); + mInvisibleDrawable = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_input_show)); mInvisibleDrawable.setBounds(0, 0, mInvisibleDrawable.getIntrinsicWidth(), mInvisibleDrawable.getIntrinsicHeight()); mCurrentDrawable = mVisibleDrawable; @@ -75,14 +70,14 @@ protected void initialize(Context context, AttributeSet attrs) { super.addTextChangedListener(this); } - private void setDrawableVisible(final boolean visible) { + private void setDrawableVisible(boolean visible) { if (mCurrentDrawable.isVisible() == visible) { return; } mCurrentDrawable.setVisible(visible, false); - final Drawable[] drawables = getCompoundDrawables(); - setCompoundDrawables( + Drawable[] drawables = getCompoundDrawables(); + setCompoundDrawablesRelative( drawables[0], drawables[1], visible ? mCurrentDrawable : null, @@ -90,8 +85,8 @@ private void setDrawableVisible(final boolean visible) { } private void refreshDrawableStatus() { - final Drawable[] drawables = getCompoundDrawables(); - setCompoundDrawables( + Drawable[] drawables = getCompoundDrawables(); + setCompoundDrawablesRelative( drawables[0], drawables[1], mCurrentDrawable, @@ -99,12 +94,12 @@ private void refreshDrawableStatus() { } @Override - public void setOnFocusChangeListener(final OnFocusChangeListener onFocusChangeListener) { + public void setOnFocusChangeListener(OnFocusChangeListener onFocusChangeListener) { mOnFocusChangeListener = onFocusChangeListener; } @Override - public void setOnTouchListener(final OnTouchListener onTouchListener) { + public void setOnTouchListener(OnTouchListener onTouchListener) { mOnTouchListener = onTouchListener; } @@ -113,7 +108,7 @@ public void setOnTouchListener(final OnTouchListener onTouchListener) { */ @Override - public void onFocusChange(final View view, final boolean hasFocus) { + public void onFocusChange(View view, boolean hasFocus) { if (hasFocus && getText() != null) { setDrawableVisible(getText().length() > 0); } else { @@ -129,8 +124,8 @@ public void onFocusChange(final View view, final boolean hasFocus) { */ @Override - public boolean onTouch(final View view, final MotionEvent motionEvent) { - final int x = (int) motionEvent.getX(); + public boolean onTouch(View view, MotionEvent motionEvent) { + int x = (int) motionEvent.getX(); if (mCurrentDrawable.isVisible() && x > getWidth() - getPaddingRight() - mCurrentDrawable.getIntrinsicWidth()) { if (motionEvent.getAction() == MotionEvent.ACTION_UP) { if (mCurrentDrawable == mVisibleDrawable) { @@ -161,7 +156,7 @@ public boolean onTouch(final View view, final MotionEvent motionEvent) { */ @Override - public void onTextChanged(final CharSequence s, final int start, final int before, final int count) { + public void onTextChanged(CharSequence s, int start, int before, int count) { if (isFocused()) { setDrawableVisible(s.length() > 0); } diff --git a/widget/src/main/java/com/hjq/widget/view/RegexEditText.java b/widget/src/main/java/com/hjq/widget/view/RegexEditText.java index 2a6118d9..5bbc0316 100644 --- a/widget/src/main/java/com/hjq/widget/view/RegexEditText.java +++ b/widget/src/main/java/com/hjq/widget/view/RegexEditText.java @@ -37,24 +37,16 @@ public class RegexEditText extends AppCompatEditText implements InputFilter { private Pattern mPattern; public RegexEditText(Context context) { - super(context); - initialize(context, null); + this(context, null); } public RegexEditText(Context context, AttributeSet attrs) { - super(context, attrs); - initialize(context, attrs); + this(context, attrs, android.R.attr.editTextStyle); } public RegexEditText(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - initialize(context, attrs); - } - /** - * 初始化属性 - */ - protected void initialize(Context context, AttributeSet attrs) { final TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.RegexEditText); if (array.hasValue(R.styleable.RegexEditText_inputRegex)) { diff --git a/widget/src/main/java/com/hjq/widget/view/ScaleImageView.java b/widget/src/main/java/com/hjq/widget/view/ScaleImageView.java index d444d824..fb8463df 100644 --- a/widget/src/main/java/com/hjq/widget/view/ScaleImageView.java +++ b/widget/src/main/java/com/hjq/widget/view/ScaleImageView.java @@ -35,8 +35,7 @@ public ScaleImageView(Context context, AttributeSet attrs, int defStyleAttr) { } @Override - public void setPressed(boolean pressed) { - super.setPressed(pressed); + protected void dispatchSetPressed(boolean pressed) { // 判断当前手指是否按下了 if (pressed) { setScaleX(mScaleSize); diff --git a/widget/src/main/java/com/hjq/widget/view/SmartTextView.java b/widget/src/main/java/com/hjq/widget/view/SmartTextView.java index e2b2639f..c877e8bc 100644 --- a/widget/src/main/java/com/hjq/widget/view/SmartTextView.java +++ b/widget/src/main/java/com/hjq/widget/view/SmartTextView.java @@ -15,21 +15,16 @@ public final class SmartTextView extends AppCompatTextView implements TextWatcher { public SmartTextView(Context context) { - super(context); - initialize(); + this(context, null); } public SmartTextView(Context context, AttributeSet attrs) { - super(context, attrs); - initialize(); + this(context, attrs, android.R.attr.textViewStyle); } public SmartTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - initialize(); - } - private void initialize() { addTextChangedListener(this); // 触发一次监听 afterTextChanged(null); diff --git a/widget/src/main/java/com/hjq/widget/view/SwitchButton.java b/widget/src/main/java/com/hjq/widget/view/SwitchButton.java index 8b99ce5e..f00da966 100644 --- a/widget/src/main/java/com/hjq/widget/view/SwitchButton.java +++ b/widget/src/main/java/com/hjq/widget/view/SwitchButton.java @@ -10,7 +10,6 @@ import android.graphics.RadialGradient; import android.graphics.RectF; import android.graphics.Shader; -import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; @@ -20,7 +19,6 @@ import android.view.animation.AccelerateInterpolator; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import com.hjq.widget.R; @@ -28,7 +26,7 @@ * author : Android 轮子哥 * github : https://github.com/getActivity/AndroidProject * time : 2019/02/20 - * desc : 开关按钮 + * desc : 高仿 ios 开关按钮 */ public final class SwitchButton extends View { @@ -90,30 +88,19 @@ public final class SwitchButton extends View { private float mShadowReservedHeight; public SwitchButton(Context context) { - super(context); - initialize(null); + this(context, null); } public SwitchButton(Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - initialize(attrs); + this(context, attrs, 0); } public SwitchButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - initialize(attrs); - } - - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - public SwitchButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - initialize(attrs); - } - private void initialize(AttributeSet attrs) { setLayerType(LAYER_TYPE_SOFTWARE, null); - TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.SwitchButton); + TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SwitchButton); mChecked = array.getBoolean(R.styleable.SwitchButton_android_checked, mChecked); setEnabled(array.getBoolean(R.styleable.SwitchButton_android_enabled, isEnabled())); mLastCheckedState = mCheckedState = mChecked ? STATE_SWITCH_ON : STATE_SWITCH_OFF; @@ -130,7 +117,6 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + getPaddingLeft() + getPaddingRight()), MeasureSpec.EXACTLY); break; case MeasureSpec.EXACTLY: - break; default: break; } @@ -141,7 +127,6 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + getPaddingTop() + getPaddingBottom(), MeasureSpec.EXACTLY); break; case MeasureSpec.EXACTLY: - break; default: break; } @@ -367,8 +352,6 @@ public boolean onTouchEvent(MotionEvent event) { && (mAnim1 * mAnim2 == 0)) { switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - break; case MotionEvent.ACTION_UP: mLastCheckedState = mCheckedState; mAnim2 = 1; @@ -390,6 +373,7 @@ public boolean onTouchEvent(MotionEvent event) { break; } break; + case MotionEvent.ACTION_DOWN: default: break; } diff --git a/widget/src/main/res/drawable/widget_bg_settting_bar_selector.xml b/widget/src/main/res/drawable/widget_bg_settting_bar_selector.xml deleted file mode 100644 index 2f209d99..00000000 --- a/widget/src/main/res/drawable/widget_bg_settting_bar_selector.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/widget/src/main/res/layout/widget_hint_layout.xml b/widget/src/main/res/layout/widget_hint_layout.xml deleted file mode 100644 index 7fb36cd9..00000000 --- a/widget/src/main/res/layout/widget_hint_layout.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/widget/src/main/res/layout/widget_setting_bar.xml b/widget/src/main/res/layout/widget_setting_bar.xml deleted file mode 100644 index 974fb6c1..00000000 --- a/widget/src/main/res/layout/widget_setting_bar.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file