diff --git a/API.md b/API.md index 544319aa..f73cf8c1 100644 --- a/API.md +++ b/API.md @@ -169,29 +169,29 @@ public final View getItemView() - public final V findViewById(@IdRes int viewId) + public final V findViewById(@IdRes int id) - public final ViewHolder setText(@IdRes int viewId, @StringRes int resId) + public final ViewHolder setText(@IdRes int id, @StringRes int id) - public final ViewHolder setText(@IdRes int viewId, String text) + public final ViewHolder setText(@IdRes int id, String text) - public final ViewHolder setVisibility(@IdRes int viewId, int visibility) + public final ViewHolder setVisibility(@IdRes int id, int visibility) - public final ViewHolder setColor(@IdRes int viewId, @ColorInt int color) + public final ViewHolder setColor(@IdRes int id, @ColorInt int color) - public final ViewHolder setImage(@IdRes int viewId, @DrawableRes int resId) + public final ViewHolder setImage(@IdRes int id, @DrawableRes int drawableId) > 监听方法(必须在 RecyclerView.setAdapter 之前调用) - public void setOnItemClickListener(OnItemClickListener l) + public void setOnItemClickListener(OnItemClickListener listener) - public void setOnChildClickListener(@IdRes int childId, OnChildClickListener l) + public void setOnChildClickListener(@IdRes int id, OnChildClickListener listener) - public void setOnItemLongClickListener(OnItemLongClickListener l) + public void setOnItemLongClickListener(OnItemLongClickListener listener) - public void setOnChildLongClickListener(@IdRes int childId, OnChildLongClickListener l) + public void setOnChildLongClickListener(@IdRes int id, OnChildLongClickListener listener) - public void setOnScrollingListener(OnScrollingListener l) + public void setOnScrollingListener(OnScrollingListener listener) > MyListViewAdapter 和 MyRecyclerViewAdapter 差不多,只不过没有上面这些监听方法,因为 ListView 本身已经自带这些了 diff --git a/AndroidProject.apk b/AndroidProject.apk index c98532ad..546a4aea 100644 Binary files a/AndroidProject.apk and b/AndroidProject.apk differ diff --git a/README.md b/README.md index 023ceb4a..a92ede28 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,48 @@ -# 模板工程 +# 安卓架构 > 博客地址:[但愿人长久,搬砖不再有](https://www.jianshu.com/p/77dd326f21dc) > 当我们日复一日年复一年的搬砖的时候,你是否曾想过提升一下开发效率,如果一个模板的项目摆在你的面前,你还会选择自己搭架构么 -> 但是做出一个好的模板项目并非易事,有多少人愿意选择去做,还有多少人选择努力去做好,可能寥寥无几,但是你今天看到的,正是你所想要的,一个真正能解决你开发新项目时最大痛点的模板工程,你不需要再麻木 Copy 原有旧项目的代码,只需改动少量代码就能得到想要的效果,你会发现开发新项目其实是一件很快乐的事 +> 但是搭建出一个好的架构并非易事,有多少人愿意选择去做,还有多少人选择努力去做好,可能寥寥无几,但是你今天看到的,正是你所想要的,一个真正能解决你开发新项目时最大痛点的架构工程,你不需要再麻木 Copy 原有旧项目的代码,只需改动少量代码就能得到想要的效果,你会发现开发新项目其实是一件很快乐的事 -> 已经正式投入公司新项目开发多时,暂时没有发现毛病,[点击此处下载Demo](https://raw.githubusercontent.com/getActivity/AndroidProject/master/AndroidProject.apk),如果有问题随时欢迎你提交 [issues](https://github.com/getActivity/AndroidProject/issues/new) 给我反馈 +> 已经正式投入多个公司项目实践,暂时没有发现任何问题,[点击此处下载Demo](https://raw.githubusercontent.com/getActivity/AndroidProject/master/AndroidProject.apk) -![](picture/0.png) +![](picture/demo_code.png) -![](picture/1.gif) +#### 常用界面 -![](picture/2.png) +![](picture/activity/1.png) ![](picture/activity/2.png) ![](picture/activity/3.png) -![](picture/3.png) +![](picture/activity/4.png) ![](picture/activity/5.png) ![](picture/activity/6.png) -![](picture/4.png) +![](picture/activity/7.png) ![](picture/activity/8.png) ![](picture/activity/9.png) -![](picture/5.png) +![](picture/activity/11.png) ![](picture/activity/12.png) ![](picture/activity/13.png) -![](picture/6.png) +![](picture/activity/14.png) ![](picture/activity/15.png) ![](picture/activity/16.png) -![](picture/7.png) +![](picture/activity/17.png) ![](picture/activity/18.png) ![](picture/activity/19.png) -![](picture/8.png) +![](picture/activity/20.png) ![](picture/activity/21.png) ![](picture/activity/22.png) -![](picture/9.png) +#### 常用对话框 -![](picture/10.png) +![](picture/dialog/1.png) ![](picture/dialog/2.png) ![](picture/dialog/3.png) -![](picture/11.png) +![](picture/dialog/4.png) ![](picture/dialog/5.png) ![](picture/dialog/6.png) -![](picture/12.png) +![](picture/dialog/7.png) ![](picture/dialog/8.png) ![](picture/dialog/9.png) -![](picture/13.png) +![](picture/dialog/10.png) ![](picture/dialog/11.png) ![](picture/dialog/12.png) -![](picture/14.png) +![](picture/dialog/13.png) ![](picture/dialog/14.png) ![](picture/dialog/15.png) -![](picture/15.png) +#### 动图欣赏 -![](picture/16.png) +![](picture/gif/1.gif) ![](picture/gif/2.gif) ![](picture/gif/3.gif) -![](picture/17.png) - -![](picture/18.png) - -![](picture/19.png) - -![](picture/20.png) - -![](picture/21.png) - -![](picture/22.png) - -![](picture/23.png) - -![](picture/24.png) - -![](picture/25.png) - -![](picture/26.png) - -![](picture/27.png) - -![](picture/28.png) - -![](picture/29.png) - -![](picture/30.png) - -![](picture/31.png) +![](picture/gif/4.gif) ![](picture/gif/5.gif) ![](picture/gif/6.gif) #### 集成框架 @@ -82,17 +54,19 @@ * 状态栏沉浸:[https://github.com/gyf-dev/ImmersionBar](https://github.com/gyf-dev/ImmersionBar) -* 界面侧滑功能:[https://github.com/bingoogolapple/BGASwipeBackLayout-Android](https://github.com/bingoogolapple/BGASwipeBackLayout-Android) +* 缩放 ImageView:[https://github.com/chrisbanes/PhotoView](https://github.com/chrisbanes/PhotoView) -* 圆形ImageView:[https://github.com/hdodenhof/CircleImageView](https://github.com/hdodenhof/CircleImageView) +* ViewPager 指示器:[https://github.com/romandanylyk/PageIndicatorView](https://github.com/romandanylyk/PageIndicatorView) -* 缩放ImageView:[https://github.com/chrisbanes/PhotoView](https://github.com/chrisbanes/PhotoView) +* ButterKnife 注解:[https://github.com/JakeWharton/butterknife](https://github.com/JakeWharton/butterknife) -* ButterKnife注解:[https://github.com/JakeWharton/butterknife](https://github.com/JakeWharton/butterknife) +* EventBus 事件:[https://github.com/greenrobot/EventBus](https://github.com/greenrobot/EventBus) -#### 模板项目亮点,[查看详细](ProjectDetails.md) +* 内存泄漏捕捉:[https://github.com/square/leakcanary](https://github.com/square/leakcanary) -* APP用户体验:已经集成界面侧滑以及状态栏沉浸框架 +* 本地异常捕捉:[https://github.com/Ereza/CustomActivityOnCrash](https://github.com/Ereza/CustomActivityOnCrash) + +#### 模板项目亮点,[查看详细](ProjectDetails.md) * 必备优秀框架:危险权限处理,标题栏控件,吐司工具类,圆形ImageView @@ -126,15 +100,19 @@ * 吐司框架:[ToastUtils](https://github.com/getActivity/ToastUtils) -* 悬浮窗框架:[XToast](https://github.com/getActivity/XToast) +* 国际化框架:[MultiLanguages](https://github.com/getActivity/MultiLanguages) * 标题栏框架:[TitleBar](https://github.com/getActivity/TitleBar) +* 悬浮窗框架:[XToast](https://github.com/getActivity/XToast) + #### Android技术讨论Q群:78797078 #### 如果您觉得我的开源库帮你节省了大量的开发时间,请扫描下方的二维码随意打赏,要是能打赏个 10.24 :monkey_face:就太:thumbsup:了。您的支持将鼓励我继续创作:octocat: -![](picture/pay_ali.png) ![](picture/pay_wechat.png) +![](https://raw.githubusercontent.com/getActivity/Donate/master/picture/pay_ali.png) ![](https://raw.githubusercontent.com/getActivity/Donate/master/picture/pay_wechat.png) + +#### [点击查看捐赠列表](https://github.com/getActivity/Donate) ## License diff --git a/app/AppSignature.jks b/app/AppSignature.jks new file mode 100644 index 00000000..82bab2f7 Binary files /dev/null and b/app/AppSignature.jks differ diff --git a/app/AppSignature.jpg b/app/AppSignature.jpg new file mode 100644 index 00000000..8db40777 Binary files /dev/null and b/app/AppSignature.jpg differ diff --git a/app/build.gradle b/app/build.gradle index 886e3415..82253098 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,24 +1,40 @@ apply plugin: 'com.android.application' android { - compileSdkVersion rootProject.ext.compileSdkVersion - buildToolsVersion rootProject.ext.buildToolsVersion + compileSdkVersion rootProject.ext.compileVersion // 使用 JDK 1.8 -// compileOptions { -// targetCompatibility JavaVersion.VERSION_1_8 -// sourceCompatibility JavaVersion.VERSION_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.targetSdkVersion + targetSdkVersion rootProject.ext.targetVersion versionCode 10 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + // 仅保留中文语种的资源 + resConfig 'zh' + + // 仅保留 xxhdpi 图片资源(目前主流分辨率 1920 * 1080) + resConfig 'xxhdpi' + + // 仅保留两种架构的 so 库 + ndk { + // armeabi:已经淘汰(0%) + // armeabi-v7a:曾经主流的架构平台(20%) + // arm64-v8a:目前主流架构平台(80%) + abiFilters "armeabi-v7a", "arm64-v8a" + } + + // 开启 Dex 分包 + //multiDexEnabled true + // 混淆配置 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-app.pro' @@ -30,55 +46,58 @@ android { } } - //APK 签名的那些事:https://www.jianshu.com/p/a1f8e5896aa2 -// signingConfigs { -// release { -// keyAlias '密钥别称' -// keyPassword '密钥密码' -// storeFile file('E:/MySign.jks') -// storePassword '密钥库密码' -// } -// -// debug { -// keyAlias '密钥别称' -// keyPassword '密钥密码' -// storeFile file('E:/MySign.jks') -// storePassword '密钥库密码' -// } -// } + // APK 签名的那些事:https://www.jianshu.com/p/a1f8e5896aa2 + signingConfigs { + debug { + storeFile file(StoreFile) + storePassword StorePassword + keyAlias KeyAlias + keyPassword KeyPassword + } + release { + storeFile file(StoreFile) + storePassword StorePassword + keyAlias KeyAlias + keyPassword KeyPassword + } + } + buildTypes { release { - // 不显示Log - buildConfigField "boolean", "LOG_DEBUG", "false" - // 移除无用的resource文件,前提minifyEnabled必须打开 + // 移除无用的资源文件 shrinkResources true - // ZipAlign优化 + // ZipAlign 优化 zipAlignEnabled true // 设置混淆 minifyEnabled true // 正式环境签名 - //signingConfig signingConfigs.release + signingConfig signingConfigs.release + // 正式环境下的 BuglyId + buildConfigField "String", "BUGLY_ID", "\"请自行替换 Bugly 上面的 AppID\"" } debug { - // ZipAlign优化 + // 移除无用的资源文件 + shrinkResources false + // ZipAlign 优化 zipAlignEnabled false // 设置混淆 minifyEnabled false // 开发环境签名 - //signingConfig signingConfigs.debug + signingConfig signingConfigs.debug + // 开发环境下的 BuglyId + buildConfigField "String", "BUGLY_ID", "\"请自行替换 Bugly 上面的 AppID\"" } } - flavorDimensions "default" // 这个名字貌似随便取,也可以有多个,总之一定要有 + // 默认渠道名 + flavorDimensions "default" // 友盟多渠道打包 productFlavors { - kuan {} // 酷安 tencent {} // 应用宝 baidu {} // 百度 xiaomi {} // 小米 huawei {} // 华为 - google {} // 谷歌 productFlavors.all { flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name] @@ -114,8 +133,6 @@ android { .replaceAll("QQ_APPKEY_VALUE", "c7394704798a158208a74ab60104f0ba") // QQ Key .replaceAll("WX_APPID_VALUE", "wxdc1e388c3822c80b") // 微信 AppId .replaceAll("WX_APPKEY_VALUE", "3baf1193c85774b3fd9d18447d76cab0") // 微信 Key - .replaceAll("SN_APPID_VALUE", "3921700954") // 新浪 AppId - .replaceAll("SN_APPKEY_VALUE", "04b48b094faeb16683c32669824ebdad") // 新浪 Key new File(manifestFile).write(updatedContent, 'UTF-8') } } @@ -132,61 +149,65 @@ dependencies { implementation project(':base') // 自定义 View implementation project(':widget') - // Dialog 封装 - implementation project(':dialog') // Glide 隔离 implementation project(':image') // 友盟隔离 implementation project(':umeng') - implementation 'androidx.appcompat:appcompat:1.1.0-alpha04' - implementation 'com.google.android.material:material:1.1.0-alpha06' - implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation 'androidx.cardview:cardview:1.0.0' - implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha5' + // 谷歌 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' + // Dex 分包,解决 64k 方法问题 + //implementation 'androidx.multidex:multidex:2.0.1' // ButterKnife 注解库:https://github.com/JakeWharton/butterknife - implementation 'com.jakewharton:butterknife:9.0.0-rc1' - annotationProcessor 'com.jakewharton:butterknife-compiler:9.0.0-rc1' + implementation 'com.jakewharton:butterknife:10.1.0' + annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0' - // EventBus 事件总线 + // 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:2.3.3' - // 侧滑功能:https://github.com/bingoogolapple/BGASwipeBackLayout-Android - implementation 'cn.bingoogolapple:bga-swipebacklayout:1.2.0' + implementation 'com.gyf.immersionbar:immersionbar:3.0.0' // 权限请求框架:https://github.com/getActivity/XXPermissions - implementation 'com.hjq:xxpermissions:5.5' + implementation 'com.hjq:xxpermissions:6.0' + // 标题栏:https://github.com/getActivity/TitleBar - implementation 'com.hjq:titlebar:5.0' + implementation 'com.hjq:titlebar:6.0' + // 吐司工具类:https://github.com/getActivity/ToastUtils - implementation 'com.hjq:toast:6.0' + implementation 'com.hjq:toast:8.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:nativecrashreport:3.7.1' + + // 本地异常捕捉框架:https://github.com/Ereza/CustomActivityOnCrash + implementation 'cat.ereza:customactivityoncrash:2.2.0' - // 圆形的ImageView:https://github.com/hdodenhof/CircleImageView - implementation 'de.hdodenhof:circleimageview: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' - // 支持放大缩放的ImageView:https://github.com/chrisbanes/PhotoView - implementation 'com.github.chrisbanes:PhotoView:2.0.0' + // 网络请求(待发布):https://github.com/getActivity/EasyHttp - // 布局优化:https://github.com/getActivity/Layouts - // 分割线:https://github.com/getActivity/RecyclerItemDecoration // 国际化:https://github.com/getActivity/MultiLanguages // 悬浮窗:https://github.com/getActivity/XToast - // 网络请求:https://github.com/zhou-you/RxEasyHttp - // RxJava: https://github.com/ReactiveX/RxAndroid - // RecyclerView:https://github.com/CymChad/BaseRecyclerViewAdapterHelper // 上拉刷新下拉加载:https://github.com/scwang90/SmartRefreshLayout // 工具类:https://github.com/Blankj/AndroidUtilCode - // 图片选择:https://github.com/zhihu/Matisse // 轮播图:https://github.com/bingoogolapple/BGABanner-Android // 二维码:https://github.com/bingoogolapple/BGAQRCode-Android // 第三方支付:https://github.com/getActivity/RxPay - // Log 打印:https://github.com/JakeWharton/timber - // 重要数据存储:https://github.com/Tencent/MMKV + // Log 打印:https://github.com/elvishew/XLog + // 图片压缩:https://github.com/Curzibn/Luban + // 对象存储:https://github.com/leavesC/DoKV + // 数据注入:https://github.com/JumeiRdGroup/Parceler } \ No newline at end of file diff --git a/app/gradle.properties b/app/gradle.properties new file mode 100644 index 00000000..0caf1444 --- /dev/null +++ b/app/gradle.properties @@ -0,0 +1,5 @@ +# ǩļԿϢ +StoreFile = AppSignature.jks +StorePassword = AndroidProject +KeyAlias = AndroidProject +KeyPassword = AndroidProject \ No newline at end of file diff --git a/app/proguard-app.pro b/app/proguard-app.pro index 56bb2bbc..ae479810 100644 --- a/app/proguard-app.pro +++ b/app/proguard-app.pro @@ -1,42 +1,25 @@ # 忽略警告 -#-ignorewarning +-ignorewarning # 混淆保护自己项目的部分代码以及引用的第三方jar包 #-libraryjars libs/umeng-analytics-v5.2.4.jar --keep class com.github.chrisbanes.photoview.** {*;} - # 标题栏框架 --keep class com.hjq.bar.** {*;} +#-keep class com.hjq.bar.** {*;} # 吐司框架 --keep class com.hjq.toast.** {*;} +#-keep class com.hjq.toast.** {*;} # 权限请求框架 --keep class com.hjq.permissions.** {*;} - -#移除log 测试了下没有用还是建议自己定义一个开关控制是否输出日志 -#-assumenosideeffects class android.util.Log { -# public static boolean isLoggable(java.lang.String, int); -# public static int v(...); -# public static int i(...); -# public static int w(...); -# public static int d(...); -# public static int e(...); -#} +#-keep class com.hjq.permissions.** {*;} -# webview + js +# 不混淆 WebView 的 JS 接口 -keepattributes *JavascriptInterface* -# keep 使用 webview 的类 --keepclassmembers class com.veidy.activity.WebViewActivity { - public *; -} -# keep 使用 webview 的类的所有的内部类 +# 不混淆 WebView 的类的所有的内部类 -keepclassmembers class com.veidy.activity.WebViewActivity$*{ *; } - -# 不混淆WebChromeClient中的openFileChooser方法 +# 不混淆 WebChromeClient 中的 openFileChooser 方法 -keepclassmembers class * extends android.webkit.WebChromeClient{ public void openFileChooser(...); } @@ -50,3 +33,7 @@ -keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent { (java.lang.Throwable); } + +# Bugly +-dontwarn com.tencent.bugly.** +-keep public class com.tencent.bugly.**{*;} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2a49306c..b9ed121f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,14 +12,16 @@ - + + + + + + android:name=".ui.activity.SplashActivity" + android:configChanges="orientation|screenSize|keyboardHidden" + android:theme="@style/SplashTheme"> @@ -54,66 +64,106 @@ - + + android:configChanges="orientation|screenSize|keyboardHidden" + android:launchMode="singleTask" + android:screenOrientation="portrait" /> - + - + + android:configChanges="orientation|screenSize|keyboardHidden" + android:label="@string/setting_title" + android:screenOrientation="portrait" /> + android:configChanges="orientation|screenSize|keyboardHidden" + android:label="@string/password_forget_title" + android:screenOrientation="portrait" /> + android:configChanges="orientation|screenSize|keyboardHidden" + android:label="@string/password_reset_title" + android:screenOrientation="portrait" /> + android:configChanges="orientation|screenSize|keyboardHidden" + android:label="@string/phone_verify_title" + android:screenOrientation="portrait" /> + android:configChanges="orientation|screenSize|keyboardHidden" + android:label="@string/phone_reset_title" + android:screenOrientation="portrait" /> - + + android:configChanges="orientation|screenSize|keyboardHidden" + android:label="@string/about_title" + android:screenOrientation="portrait" /> + android:configChanges="orientation|screenSize|keyboardHidden" + android:label="@string/personal_data_title" + android:screenOrientation="portrait" /> - + + android:configChanges="orientation|screenSize|keyboardHidden" + android:label="@string/web_title" + android:screenOrientation="portrait" /> - - + + - - + + + + + - - + + diff --git a/dialog/src/main/assets/province.json b/app/src/main/assets/province.json similarity index 99% rename from dialog/src/main/assets/province.json rename to app/src/main/assets/province.json index 5acdbb20..4e2cbcda 100644 --- a/dialog/src/main/assets/province.json +++ b/app/src/main/assets/province.json @@ -1208,6 +1208,27 @@ "遂平县", "新蔡县" ] + }, + { + "name": "济源市", + "area": [ + "济水街道", + "沁园街道", + "北海街道", + "天坛街道", + "玉泉街道", + "克井镇", + "五龙口镇", + "梨林镇", + "轵城镇", + "承留镇", + "坡头镇", + "大峪镇", + "邵原镇", + "思礼镇", + "王屋镇", + "下冶镇" + ] } ] }, 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 8ea6db09..7a07c316 100644 --- a/app/src/main/java/com/hjq/demo/common/MyActivity.java +++ b/app/src/main/java/com/hjq/demo/common/MyActivity.java @@ -1,17 +1,23 @@ package com.hjq.demo.common; -import android.content.pm.ActivityInfo; +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 android.util.Log; -import android.view.View; +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.demo.R; import com.hjq.demo.helper.ActivityStackManager; -import com.hjq.demo.helper.DebugUtils; import com.hjq.demo.other.EventBusManager; import com.hjq.demo.other.StatusManager; import com.hjq.toast.ToastUtils; @@ -26,50 +32,127 @@ * time : 2018/10/18 * desc : 项目中的 Activity 基类 */ -public abstract class MyActivity extends UIActivity +public abstract class MyActivity extends BaseActivity implements OnTitleBarListener { + /** 标题栏对象 */ + private TitleBar mTitleBar; + /** 状态栏沉浸 */ + private ImmersionBar mImmersionBar; + /** ButterKnife 注解 */ + private Unbinder mButterKnife; + + /** + * 获取标题栏 id + */ + protected int getTitleId() { + return 0; + } + @Override protected void initActivity() { super.initActivity(); - ActivityStackManager.getInstance().onActivityCreated(this); + ActivityStackManager.getInstance().onCreated(this); } - // ButterKnife 注解 - private Unbinder mButterKnife; - @Override protected void initLayout() { super.initLayout(); // 初始化标题栏的监听 if (getTitleId() > 0) { - if (findViewById(getTitleId()) instanceof TitleBar) { - ((TitleBar) findViewById(getTitleId())).setOnTitleBarListener(this); + // 勤快模式 + View view = findViewById(getTitleId()); + if (view instanceof TitleBar) { + mTitleBar = (TitleBar) view; } + } else if (getTitleId() == 0) { + // 懒人模式 + mTitleBar = findTitleBar(getContentView()); + } + if (mTitleBar != null) { + mTitleBar.setOnTitleBarListener(this); } mButterKnife = ButterKnife.bind(this); EventBusManager.register(this); - initOrientation(); + initImmersion(); } /** - * 初始化横竖屏方向,会和 LauncherTheme 主题样式有冲突,注意不要同时使用 + * 递归获取 ViewGroup 中的 TitleBar 对象 */ - protected void initOrientation() { - // 当前 Activity 不能是透明的并且没有指定屏幕方向,默认设置为竖屏 - if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + 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(); + + // 设置标题栏沉浸 + if (getTitleId() > 0) { + ImmersionBar.setTitleBar(this, findViewById(getTitleId())); + } else if (mTitleBar != null) { + ImmersionBar.setTitleBar(this, mTitleBar); + } } } + /** + * 是否使用沉浸式状态栏 + */ + public boolean isStatusBarEnabled() { + return true; + } + + /** + * 获取状态栏沉浸的配置对象 + */ + public ImmersionBar getStatusBarConfig() { + return mImmersionBar; + } + + /** + * 获取状态栏字体颜色 + */ + public boolean statusBarDarkFont() { + // 返回真表示黑色字体 + return true; + } + + /** + * 初始化沉浸式状态栏 + */ + protected ImmersionBar statusBarConfig() { + // 在BaseActivity里初始化 + mImmersionBar = ImmersionBar.with(this) + // 默认状态栏字体颜色为黑色 + .statusBarDarkFont(statusBarDarkFont()); + return mImmersionBar; + } + /** * 设置标题栏的标题 */ @Override - public void setTitle(int titleId) { - setTitle(getText(titleId)); + public void setTitle(@StringRes int id) { + setTitle(getString(id)); } /** @@ -78,41 +161,127 @@ public void setTitle(int titleId) { @Override public void setTitle(CharSequence title) { super.setTitle(title); - TitleBar titleBar = getTitleBar(); - if (titleBar != null) { - titleBar.setTitle(title); + if (mTitleBar != null) { + mTitleBar.setTitle(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 TitleBar getTitleBar() { - if (getTitleId() > 0 && findViewById(getTitleId()) instanceof TitleBar) { - return findViewById(getTitleId()); + public Drawable getLeftIcon() { + if (mTitleBar != null) { + return mTitleBar.getLeftIcon(); } return null; } - @Override - public boolean statusBarDarkFont() { - //返回true表示黑色字体 - return true; + /** + * 设置标题栏的右图标 + */ + 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; + } + + @Nullable + public TitleBar getTitleBar() { + return mTitleBar; } /** * {@link OnTitleBarListener} */ - // TitleBar 左边的View被点击了 + /** + * TitleBar 左边的View被点击了 + */ @Override public void onLeftClick(View v) { onBackPressed(); } - // TitleBar 中间的View被点击了 + /** + * TitleBar 中间的View被点击了 + */ @Override public void onTitleClick(View v) {} - // TitleBar 右边的View被点击了 + /** + * TitleBar 右边的View被点击了 + */ @Override public void onRightClick(View v) {} @@ -131,20 +300,34 @@ protected void onPause() { @Override protected void onDestroy() { super.onDestroy(); - if (mButterKnife != null) mButterKnife.unbind(); + if (mButterKnife != null) { + mButterKnife.unbind(); + } EventBusManager.unregister(this); - ActivityStackManager.getInstance().onActivityDestroyed(this); + ActivityStackManager.getInstance().onDestroyed(this); + } + + @Override + public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) { + super.startActivityForResult(intent, requestCode, options); + overridePendingTransition(R.anim.activity_right_in, R.anim.activity_right_out); + } + + @Override + public void finish() { + super.finish(); + overridePendingTransition(R.anim.activity_left_in, R.anim.activity_left_out); } /** * 显示吐司 */ - public void toast(CharSequence s) { - ToastUtils.show(s); + public void toast(CharSequence text) { + ToastUtils.show(text); } public void toast(@StringRes int id) { - ToastUtils.show(getString(id)); + ToastUtils.show(id); } public void toast(Object object) { @@ -155,18 +338,11 @@ public void toast(Object object) { * 打印日志 */ public void log(Object object) { - if (DebugUtils.isDebug(this)) { + if (BuildConfig.DEBUG) { Log.v(getClass().getSimpleName(), object != null ? object.toString() : "null"); } } - /** - * 获取当前的 Application 对象 - */ - public final MyApplication getMyApplication() { - return (MyApplication) getApplication(); - } - private final StatusManager mStatusManager = new StatusManager(); /** @@ -176,6 +352,14 @@ 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); + } + /** * 显示加载完成 */ @@ -200,8 +384,8 @@ public void showError() { /** * 显示自定义提示 */ - public void showLayout(@DrawableRes int iconId, @StringRes int textId) { - mStatusManager.showLayout(getContentView(), iconId, textId); + public void showLayout(@DrawableRes int drawableId, @StringRes int stringId) { + mStatusManager.showLayout(getContentView(), drawableId, stringId); } public void showLayout(Drawable drawable, CharSequence hint) { 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 5b750a15..b039eb56 100644 --- a/app/src/main/java/com/hjq/demo/common/MyApplication.java +++ b/app/src/main/java/com/hjq/demo/common/MyApplication.java @@ -2,14 +2,21 @@ import android.app.Application; import android.content.Context; -import androidx.multidex.MultiDex; +import android.util.Log; +import android.widget.Toast; +import com.hjq.demo.BuildConfig; import com.hjq.demo.other.EventBusManager; +import com.hjq.demo.ui.activity.CrashActivity; +import com.hjq.demo.ui.activity.HomeActivity; import com.hjq.image.ImageLoader; +import com.hjq.toast.ToastInterceptor; import com.hjq.toast.ToastUtils; import com.hjq.umeng.UmengClient; +import com.squareup.leakcanary.LeakCanary; +import com.tencent.bugly.crashreport.CrashReport; -import cn.bingoogolapple.swipebacklayout.BGASwipeBackHelper; +import cat.ereza.customactivityoncrash.config.CaocConfig; /** * author : Android 轮子哥 @@ -17,7 +24,7 @@ * time : 2018/10/18 * desc : 项目中的 Application 基类 */ -public class MyApplication extends Application { +public final class MyApplication extends Application { @Override public void onCreate() { @@ -29,30 +36,62 @@ public void onCreate() { * 初始化一些第三方框架 */ public static void initSDK(Application application) { - /** - * 必须在 Application 的 onCreate 方法中执行 BGASwipeBackHelper.init 来初始化滑动返回 - * 第一个参数:应用程序上下文 - * 第二个参数:如果发现滑动返回后立即触摸界面时应用崩溃,请把该界面里比较特殊的 View 的 class 添加到该集合中,目前在库中已经添加了 WebView 和 SurfaceView - */ - BGASwipeBackHelper.init(application, null); - - // 初始化吐司工具类 + // 这个过程专门用于堆分析的 leak 金丝雀 + // 你不应该在这个过程中初始化你的应用程序 + if (LeakCanary.isInAnalyzerProcess(application)) { + return; + } + + // 内存泄漏检测 + LeakCanary.install(application); + + // 友盟统计、登录、分享 SDK + UmengClient.init(application); + + // 设置 Toast 拦截器 + ToastUtils.setToastInterceptor(new ToastInterceptor() { + @Override + public boolean intercept(Toast toast, CharSequence text) { + boolean intercept = super.intercept(toast, text); + if (intercept) { + Log.e("Toast", "空 Toast"); + } else { + Log.i("Toast", text.toString()); + } + return intercept; + } + }); + // 吐司工具类 ToastUtils.init(application); - // 初始化图片加载器 + // 图片加载器 ImageLoader.init(application); - // 初始化 EventBus + // EventBus 事件总线 EventBusManager.init(); - // 初始化友盟 SDK - UmengClient.init(application); + // Bugly 异常捕捉 + CrashReport.initCrashReport(application, BuildConfig.BUGLY_ID, false); + + // Crash 捕捉界面 + CaocConfig.Builder.create() + .backgroundMode(CaocConfig.BACKGROUND_MODE_SHOW_CUSTOM) + .enabled(true) + .trackActivities(true) + .minTimeBetweenCrashesMs(2000) + // 重启的 Activity + .restartActivity(HomeActivity.class) + // 错误的 Activity + .errorActivity(CrashActivity.class) + // 设置监听器 + //.eventListener(new YourCustomEventListener()) + .apply(); } @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); // 使用 Dex分包 - MultiDex.install(this); + //MultiDex.install(this); } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/common/MyDialogFragment.java b/app/src/main/java/com/hjq/demo/common/MyDialogFragment.java new file mode 100644 index 00000000..f2298970 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/common/MyDialogFragment.java @@ -0,0 +1,51 @@ +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/MyLazyFragment.java b/app/src/main/java/com/hjq/demo/common/MyLazyFragment.java index ec2256a3..ca2b3a16 100644 --- a/app/src/main/java/com/hjq/demo/common/MyLazyFragment.java +++ b/app/src/main/java/com/hjq/demo/common/MyLazyFragment.java @@ -2,17 +2,21 @@ import android.graphics.drawable.Drawable; import android.os.Bundle; -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; 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.demo.helper.DebugUtils; +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; @@ -27,23 +31,115 @@ * time : 2018/10/18 * desc : 项目中 Fragment 懒加载基类 */ -public abstract class MyLazyFragment extends UILazyFragment { +public abstract class MyLazyFragment + extends BaseLazyFragment implements OnTitleBarListener { - private Unbinder mButterKnife; // View注解 + /** 标题栏对象 */ + 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); - mButterKnife = ButterKnife.bind(this, view); + 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(); @@ -53,6 +149,25 @@ public void onDestroy() { 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) { @@ -64,11 +179,11 @@ public TitleBar getTitleBar() { /** * 显示吐司 */ - public void toast(CharSequence s) { - ToastUtils.show(s); + public void toast(CharSequence text) { + ToastUtils.show(text); } - public void toast(int id) { + public void toast(@StringRes int id) { ToastUtils.show(id); } @@ -80,7 +195,7 @@ public void toast(Object object) { * 打印日志 */ public void log(Object object) { - if (DebugUtils.isDebug(getBindingActivity())) { + if (BuildConfig.DEBUG) { Log.v(getClass().getSimpleName(), object != null ? object.toString() : "null"); } } @@ -97,13 +212,43 @@ public void onPause() { 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(getBindingActivity()); + mStatusManager.showLoading(getAttachActivity()); + } + + public void showLoading(@StringRes int id) { + mStatusManager.showLoading(getAttachActivity(), getString(id)); + } + + public void showLoading(CharSequence text) { + mStatusManager.showLoading(getAttachActivity(), text); } /** @@ -130,8 +275,8 @@ public void showError() { /** * 显示自定义提示 */ - public void showLayout(@DrawableRes int iconId, @StringRes int textId) { - mStatusManager.showLayout(getView(), iconId, textId); + public void showLayout(@DrawableRes int drawableId, @StringRes int stringId) { + mStatusManager.showLayout(getView(), drawableId, stringId); } public void showLayout(Drawable drawable, CharSequence hint) { diff --git a/app/src/main/java/com/hjq/demo/common/MyListViewAdapter.java b/app/src/main/java/com/hjq/demo/common/MyListViewAdapter.java index f7cdffd0..fc882b2c 100644 --- a/app/src/main/java/com/hjq/demo/common/MyListViewAdapter.java +++ b/app/src/main/java/com/hjq/demo/common/MyListViewAdapter.java @@ -1,18 +1,18 @@ package com.hjq.demo.common; import android.content.Context; -import androidx.annotation.ColorInt; -import androidx.annotation.DrawableRes; -import androidx.annotation.IdRes; -import androidx.annotation.StringRes; import android.view.View; import android.view.ViewGroup; -import android.widget.CompoundButton; -import android.widget.ImageView; -import android.widget.TextView; + +import androidx.annotation.LayoutRes; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; import com.hjq.base.BaseListViewAdapter; -import com.hjq.image.ImageLoader; +import com.hjq.toast.ToastUtils; + +import java.util.ArrayList; +import java.util.List; import butterknife.ButterKnife; @@ -22,20 +22,130 @@ * time : 2018/10/18 * desc : 项目中 ListView 适配器基类 */ -public abstract class MyListViewAdapter - extends BaseListViewAdapter { +public abstract class MyListViewAdapter extends BaseListViewAdapter { - //当前列表的页码,默认为第一页,用于分页加载功能 + /** 列表数据 */ + private List mDataSet; + /** 当前列表的页码,默认为第一页,用于分页加载功能 */ private int mPageNumber = 1; - //是否是最后一页,默认为false,用于分页加载功能 + /** 是否是最后一页,默认为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(); + } + /** * 获取当前的页码 */ @@ -78,68 +188,45 @@ public void setTag(Object tag) { mTag = tag; } - public class ViewHolder extends BaseListViewAdapter.ViewHolder { + /** + * 显示吐司 + */ + public void toast(CharSequence text) { + ToastUtils.show(text); + } - public ViewHolder(ViewGroup parent, int layoutId) { - super(parent, layoutId); - } + public void toast(@StringRes int id) { + ToastUtils.show(id); + } - public ViewHolder(View itemView) { - super(itemView); - ButterKnife.bind(itemView); - } + public void toast(Object object) { + ToastUtils.show(object); + } - public final ViewHolder setText(@IdRes int viewId, @StringRes int resId) { - return setText(viewId, getItemView().getResources().getString(resId)); - } + public abstract class ViewHolder extends BaseListViewAdapter.ViewHolder { - public final ViewHolder setText(@IdRes int viewId, String text) { - if (text == null) text = ""; - View view = findViewById(viewId); - if (view instanceof TextView) { - ((TextView) view).setText(text); - } - return this; + public ViewHolder(ViewGroup parent, @LayoutRes int id) { + super(parent, id); + ButterKnife.bind(getItemView()); } - public final ViewHolder setVisibility(@IdRes int viewId, int visibility) { - View view = findViewById(viewId); - if (view != null) { - view.setVisibility(visibility); - } - return this; + public ViewHolder(View itemView) { + super(itemView); + ButterKnife.bind(itemView); } + } - public final ViewHolder setColor(@IdRes int viewId, @ColorInt int color) { - View view = findViewById(viewId); - if (view instanceof TextView) { - ((TextView) view).setTextColor(color); - } - return this; - } + public class SimpleHolder extends ViewHolder { - public final ViewHolder setImage(@IdRes int viewId, @DrawableRes int resId) { - View view = findViewById(viewId); - if (view instanceof ImageView) { - ((ImageView) view).setImageResource(resId); - } - return this; + public SimpleHolder(ViewGroup parent, @LayoutRes int id) { + super(parent, id); } - public final ViewHolder setImage(@IdRes int viewId, String url) { - View view = findViewById(viewId); - if (view instanceof ImageView) { - ImageLoader.loadImage((ImageView) view, url); - } - return this; + public SimpleHolder(View itemView) { + super(itemView); } - public final ViewHolder setChecked(@IdRes int viewId, boolean checked) { - View view = findViewById(viewId); - if (view instanceof CompoundButton) { - ((CompoundButton) view).setChecked(checked); - } - return this; - } + @Override + public void onBindView(int position) {} } } \ 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/MyRecyclerViewAdapter.java index aac4c020..8f1868b5 100644 --- a/app/src/main/java/com/hjq/demo/common/MyRecyclerViewAdapter.java +++ b/app/src/main/java/com/hjq/demo/common/MyRecyclerViewAdapter.java @@ -1,18 +1,17 @@ package com.hjq.demo.common; import android.content.Context; -import androidx.annotation.ColorInt; -import androidx.annotation.DrawableRes; -import androidx.annotation.IdRes; -import androidx.annotation.StringRes; import android.view.View; -import android.view.ViewGroup; -import android.widget.CompoundButton; -import android.widget.ImageView; -import android.widget.TextView; + +import androidx.annotation.LayoutRes; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; import com.hjq.base.BaseRecyclerViewAdapter; -import com.hjq.image.ImageLoader; +import com.hjq.toast.ToastUtils; + +import java.util.ArrayList; +import java.util.List; import butterknife.ButterKnife; @@ -22,20 +21,130 @@ * time : 2018/12/19 * desc : 项目中 RecyclerView 适配器基类 */ -public abstract class MyRecyclerViewAdapter - extends BaseRecyclerViewAdapter { +public abstract class MyRecyclerViewAdapter extends BaseRecyclerViewAdapter { - // 当前列表的页码,默认为第一页,用于分页加载功能 + /** 列表数据 */ + private List mDataSet; + /** 当前列表的页码,默认为第一页,用于分页加载功能 */ private int mPageNumber = 1; - // 是否是最后一页,默认为false,用于分页加载功能 + /** 是否是最后一页,默认为false,用于分页加载功能 */ private boolean mLastPage; - // 标记对象 + /** 标记对象 */ private Object mTag; public MyRecyclerViewAdapter(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 (data == null || data.size() == 0) { + return; + } + + if (mDataSet == null || mDataSet.size() == 0) { + setData(data); + } else { + mDataSet.addAll(data); + notifyItemRangeInserted(mDataSet.size() - data.size(), data.size()); + } + } + + /** + * 清空当前数据 + */ + public void clearData() { + if (mDataSet == null || mDataSet.size() == 0) { + return; + } + + mDataSet.clear(); + notifyDataSetChanged(); + } + + /** + * 获取某个位置上的数据 + */ + 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); + notifyItemChanged(position); + } + + /** + * 添加单条数据 + */ + 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<>(); + } + + if (position < mDataSet.size()) { + mDataSet.add(position, item); + } else { + mDataSet.add(item); + position = mDataSet.size() - 1; + } + notifyItemInserted(position); + } + + /** + * 删除单条数据 + */ + 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); + //告诉适配器删除数据的位置,会有动画效果 + notifyItemRemoved(position); + } + /** * 获取当前的页码 */ @@ -78,10 +187,25 @@ public void setTag(Object tag) { mTag = tag; } - public class ViewHolder extends BaseRecyclerViewAdapter.ViewHolder { + /** + * 显示吐司 + */ + public void toast(CharSequence text) { + ToastUtils.show(text); + } + + public void toast(@StringRes int id) { + ToastUtils.show(id); + } - public ViewHolder(ViewGroup parent, int layoutId) { - super(parent, layoutId); + public void toast(Object object) { + ToastUtils.show(object); + } + + public abstract class ViewHolder extends BaseRecyclerViewAdapter.ViewHolder { + + public ViewHolder(@LayoutRes int id) { + super(id); ButterKnife.bind(this, itemView); } @@ -89,58 +213,19 @@ public ViewHolder(View itemView) { super(itemView); ButterKnife.bind(this, itemView); } + } - public final ViewHolder setText(@IdRes int viewId, @StringRes int resId) { - return setText(viewId, getItemView().getResources().getString(resId)); - } - - public final ViewHolder setText(@IdRes int viewId, String text) { - if (text == null) text = ""; - View view = findViewById(viewId); - if (view instanceof TextView) { - ((TextView) view).setText(text); - } - return this; - } - - public final ViewHolder setVisibility(@IdRes int viewId, int visibility) { - View view = findViewById(viewId); - if (view != null) { - view.setVisibility(visibility); - } - return this; - } - - public final ViewHolder setColor(@IdRes int viewId, @ColorInt int color) { - View view = findViewById(viewId); - if (view instanceof TextView) { - ((TextView) view).setTextColor(color); - } - return this; - } + public class SimpleHolder extends ViewHolder { - public final ViewHolder setImage(@IdRes int viewId, @DrawableRes int resId) { - View view = findViewById(viewId); - if (view instanceof ImageView) { - ((ImageView) view).setImageResource(resId); - } - return this; + public SimpleHolder(@LayoutRes int id) { + super(id); } - public final ViewHolder setImage(@IdRes int viewId, String url) { - View view = findViewById(viewId); - if (view instanceof ImageView) { - ImageLoader.loadImage((ImageView) view, url); - } - return this; + public SimpleHolder(View itemView) { + super(itemView); } - public final ViewHolder setChecked(@IdRes int viewId, boolean checked) { - View view = findViewById(viewId); - if (view instanceof CompoundButton) { - ((CompoundButton) view).setChecked(checked); - } - return this; - } + @Override + public void onBindView(int position) {} } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/common/UIActivity.java b/app/src/main/java/com/hjq/demo/common/UIActivity.java deleted file mode 100644 index b449be3a..00000000 --- a/app/src/main/java/com/hjq/demo/common/UIActivity.java +++ /dev/null @@ -1,181 +0,0 @@ -package com.hjq.demo.common; - -import android.os.Build; -import android.os.Bundle; -import android.view.ViewTreeObserver; -import android.view.WindowManager; - -import com.gyf.barlibrary.ImmersionBar; -import com.hjq.base.BaseActivity; -import com.hjq.demo.R; - -import cn.bingoogolapple.swipebacklayout.BGASwipeBackHelper; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/10/18 - * desc : 支持沉浸式和侧滑的Activity基类(默认开启沉浸式状态栏和侧滑功能) - */ -public abstract class UIActivity extends BaseActivity - implements BGASwipeBackHelper.Delegate, - ViewTreeObserver.OnGlobalLayoutListener { - - private ImmersionBar mImmersionBar;//状态栏沉浸 - private BGASwipeBackHelper mSwipeBackHelper;//侧滑返回 - - @Override - protected void onCreate(Bundle savedInstanceState) { - // 在 super.onCreate(savedInstanceState) 之前调用该方法 - initSwipeBack(); - super.onCreate(savedInstanceState); - } - - @Override - protected void initLayout() { - super.initLayout(); - initImmersion(); - } - - /** - * 初始化沉浸式 - */ - protected void initImmersion() { - //初始化沉浸式状态栏 - if (isStatusBarEnabled()) { - statusBarConfig().init(); - - //设置标题栏 - if (getTitleId() > 0) { - ImmersionBar.setTitleBar(this, findViewById(getTitleId())); - } - } - } - - public BGASwipeBackHelper getSwipeBackHelper() { - return mSwipeBackHelper; - } - - /** - * 初始化滑动返回。在 super.onCreate(savedInstanceState) 之前调用该方法 - */ - private void initSwipeBack() { - mSwipeBackHelper = new BGASwipeBackHelper(this, this); - - // 「必须在 Application 的 onCreate 方法中执行 BGASwipeBackHelper.init 来初始化滑动返回」 - // 下面几项可以不配置,这里只是为了讲述接口用法。 - - // 设置滑动返回是否可用。默认值为 true - mSwipeBackHelper.setSwipeBackEnable(true); - // 设置是否仅仅跟踪左侧边缘的滑动返回。默认值为 true - mSwipeBackHelper.setIsOnlyTrackingLeftEdge(true); - // 设置是否是微信滑动返回样式。默认值为 true - mSwipeBackHelper.setIsWeChatStyle(true); - // 设置阴影资源 id。默认值为 R.drawable.bga_sbl_shadow - mSwipeBackHelper.setShadowResId(R.drawable.bga_sbl_shadow); - // 设置是否显示滑动返回的阴影效果。默认值为 true - mSwipeBackHelper.setIsNeedShowShadow(true); - // 设置阴影区域的透明度是否根据滑动的距离渐变。默认值为 true - mSwipeBackHelper.setIsShadowAlphaGradient(true); - // 设置触发释放后自动滑动返回的阈值,默认值为 0.3f - mSwipeBackHelper.setSwipeBackThreshold(0.3f); - // 设置底部导航条是否悬浮在内容上,默认值为 false - mSwipeBackHelper.setIsNavigationBarOverlap(false); - } - - /** - * {@link BGASwipeBackHelper.Delegate} - */ - - /** - * 是否支持滑动返回。这里在父类中默认返回 true 来支持滑动返回,如果某个界面不想支持滑动返回则重写该方法返回 false 即可 - */ - @Override - public boolean isSupportSwipeBack() { - // android 9.0系统滑动返回上一级时闪屏:https://github.com/bingoogolapple/BGASwipeBackLayout-Android/issues/154 - return Build.VERSION.SDK_INT < Build.VERSION_CODES.P; - } - - /** - * 正在滑动返回 - * - * @param slideOffset 从 0 到 1 - */ - @Override - public void onSwipeBackLayoutSlide(float slideOffset) {} - - /** - * 没达到滑动返回的阈值,取消滑动返回动作,回到默认状态 - */ - @Override - public void onSwipeBackLayoutCancel() {} - - /** - * 滑动返回执行完毕,销毁当前 Activity - */ - @Override - public void onSwipeBackLayoutExecuted() { - mSwipeBackHelper.swipeBackward(); - } - - @Override - public void onBackPressed() { - // 正在滑动返回的时候取消返回按钮事件 - if (mSwipeBackHelper.isSliding()) { - return; - } - mSwipeBackHelper.backward(); - super.onBackPressed(); - } - - /** - * 是否使用沉浸式状态栏 - */ - public boolean isStatusBarEnabled() { - return true; - } - - /** - * 获取状态栏沉浸的配置对象 - */ - public ImmersionBar getStatusBarConfig() { - return mImmersionBar; - } - - /** - * 初始化沉浸式状态栏 - */ - protected ImmersionBar statusBarConfig() { - //在BaseActivity里初始化 - mImmersionBar = ImmersionBar.with(this) - .statusBarDarkFont(statusBarDarkFont()) //默认状态栏字体颜色为黑色 - .keyboardEnable(false, WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN - | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); //解决软键盘与底部输入框冲突问题,默认为false,还有一个重载方法,可以指定软键盘mode - //必须设置View树布局变化监听,否则软键盘无法顶上去,还有模式必须是SOFT_INPUT_ADJUST_PAN - getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(this); - return mImmersionBar; - } - - /** - * {@link ViewTreeObserver.OnGlobalLayoutListener} - */ - @Override - public void onGlobalLayout() {}//不用写任何方法 - - /** - * 获取状态栏字体颜色 - */ - public boolean statusBarDarkFont() { - //返回false表示白色字体 - return true; - } - - @Override - protected void onDestroy() { - super.onDestroy(); - if (mImmersionBar != null) mImmersionBar.destroy(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - getWindow().getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(this); - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/common/UILazyFragment.java b/app/src/main/java/com/hjq/demo/common/UILazyFragment.java deleted file mode 100644 index d71a9929..00000000 --- a/app/src/main/java/com/hjq/demo/common/UILazyFragment.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.hjq.demo.common; - -import com.gyf.barlibrary.ImmersionBar; -import com.hjq.base.BaseLazyFragment; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/10/18 - * desc : 支持沉浸式Fragment懒加载基类(默认不开启沉浸式) - */ -public abstract class UILazyFragment extends BaseLazyFragment { - - private ImmersionBar mImmersionBar; // 状态栏沉浸 - - @Override - protected void initFragment() { - initImmersion(); - super.initFragment(); - } - - /** - * 初始化沉浸式 - */ - protected void initImmersion() { - - // 初始化沉浸式状态栏 - if (isStatusBarEnabled()) { - statusBarConfig().init(); - - // 设置标题栏 - if (getTitleId() > 0) { - ImmersionBar.setTitleBar(mActivity, findViewById(getTitleId())); - } - } - } - - /** - * 是否在Fragment使用沉浸式 - */ - public boolean isStatusBarEnabled() { - return false; - } - - /** - * 获取状态栏沉浸的配置对象 - */ - protected ImmersionBar getStatusBarConfig() { - return mImmersionBar; - } - - /** - * 初始化沉浸式 - */ - private ImmersionBar statusBarConfig() { - //在BaseActivity里初始化 - mImmersionBar = ImmersionBar.with(this) - .statusBarDarkFont(statusBarDarkFont()) //默认状态栏字体颜色为黑色 - .keyboardEnable(true); //解决软键盘与底部输入框冲突问题,默认为false,还有一个重载方法,可以指定软键盘mode - return mImmersionBar; - } - - /** - * 获取状态栏字体颜色 - */ - protected boolean statusBarDarkFont() { - //返回true表示黑色字体 - return true; - } - - @Override - public void onDestroy() { - super.onDestroy(); - if (mImmersionBar != null) mImmersionBar.destroy(); - } - - @Override - public void setUserVisibleHint(boolean isVisibleToUser) { - super.setUserVisibleHint(isVisibleToUser); - if (isVisibleToUser && isStatusBarEnabled() && isLazyLoad()) { - // 重新初始化状态栏 - statusBarConfig().init(); - } - } -} \ 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 b113ecc5..8380f185 100644 --- a/app/src/main/java/com/hjq/demo/helper/ActivityStackManager.java +++ b/app/src/main/java/com/hjq/demo/helper/ActivityStackManager.java @@ -1,6 +1,8 @@ package com.hjq.demo.helper; import android.app.Activity; +import android.app.Application; + import androidx.collection.ArrayMap; /** @@ -13,9 +15,9 @@ public final class ActivityStackManager { private static volatile ActivityStackManager sInstance; - private ArrayMap mActivitySet = new ArrayMap<>(); + private final ArrayMap mActivitySet = new ArrayMap<>(); - // 当前 Activity 对象标记 + /** 当前 Activity 对象标记 */ private String mCurrentTag; private ActivityStackManager() {} @@ -32,6 +34,13 @@ public static ActivityStackManager getInstance() { return sInstance; } + /** + * 获取 Application 对象 + */ + public Application getApplication() { + return getTopActivity().getApplication(); + } + /** * 获取栈顶的 Activity */ @@ -72,12 +81,18 @@ public final void finishAllActivities(Class... classArray) { } } - public void onActivityCreated(Activity activity) { + /** + * Activity 同名方法回调 + */ + public void onCreated(Activity activity) { mCurrentTag = getObjectTag(activity); mActivitySet.put(getObjectTag(activity), activity); } - public void onActivityDestroyed(Activity activity) { + /** + * Activity 同名方法回调 + */ + public void onDestroyed(Activity activity) { mActivitySet.remove(getObjectTag(activity)); // 如果当前的 Activity 是最后一个的话 if (getObjectTag(activity).equals(mCurrentTag)) { 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 c65d04f7..68fee3e9 100644 --- a/app/src/main/java/com/hjq/demo/helper/CacheDataManager.java +++ b/app/src/main/java/com/hjq/demo/helper/CacheDataManager.java @@ -39,16 +39,20 @@ public static void clearAllCache(Context context) { * 删除文件夹 */ private static boolean deleteDir(File dir) { - if (dir != null && dir.isDirectory()) { - String[] children = dir.list(); - for (int i = 0; i < children.length; i++) { - boolean success = deleteDir(new File(dir, children[i])); - if (!success) { - return false; + if (dir != null) { + if (dir.isDirectory()) { + String[] children = dir.list(); + for (String child : children) { + boolean success = deleteDir(new File(dir, child)); + if (!success) { + return false; + } } + } else { + return dir.delete(); } } - return dir.delete(); + return false; } // 获取文件大小 @@ -57,13 +61,13 @@ private static boolean deleteDir(File dir) { private static long getFolderSize(File file) { long size = 0; try { - File[] fileList = file.listFiles(); - for (int i = 0; i < fileList.length; i++) { + File[] list = file.listFiles(); + for (File temp : list) { // 如果下面还有文件 - if (fileList[i].isDirectory()) { - size = size + getFolderSize(fileList[i]); + if (temp.isDirectory()) { + size = size + getFolderSize(temp); } else { - size = size + fileList[i].length(); + size = size + temp.length(); } } } catch (Exception e) { diff --git a/app/src/main/java/com/hjq/demo/helper/DebugUtils.java b/app/src/main/java/com/hjq/demo/helper/DebugUtils.java deleted file mode 100644 index ae57de1f..00000000 --- a/app/src/main/java/com/hjq/demo/helper/DebugUtils.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.hjq.demo.helper; - -import android.content.Context; -import android.content.pm.ApplicationInfo; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/10/18 - * desc : Debug 判断工具类 - */ -public final class DebugUtils { - - /** - * 当前是否为Debug模式 - */ - public static boolean isDebug(Context context) { - return context.getApplicationInfo() != null - && (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; - } -} diff --git a/app/src/main/java/com/hjq/demo/helper/DoubleClickHelper.java b/app/src/main/java/com/hjq/demo/helper/DoubleClickHelper.java index fb14ed51..6c9295eb 100644 --- a/app/src/main/java/com/hjq/demo/helper/DoubleClickHelper.java +++ b/app/src/main/java/com/hjq/demo/helper/DoubleClickHelper.java @@ -10,7 +10,8 @@ */ public final class DoubleClickHelper { - private static final long[] TIME_ARRAY = new long[2]; // 数组的长度为2代表只记录双击操作 + /** 数组的长度为2代表只记录双击操作 */ + private static final long[] TIME_ARRAY = new long[2]; /** * 是否在短时间内进行了双击操作 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 511d3a2d..e12d2ce8 100644 --- a/app/src/main/java/com/hjq/demo/helper/InputTextHelper.java +++ b/app/src/main/java/com/hjq/demo/helper/InputTextHelper.java @@ -8,6 +8,8 @@ import android.view.View; import android.widget.TextView; +import androidx.annotation.NonNull; + import java.util.ArrayList; import java.util.List; @@ -20,14 +22,16 @@ */ public final class InputTextHelper implements TextWatcher { - private View mView; // 操作按钮的View - private boolean isAlpha; // 是否禁用后设置半透明度 + /** 操作按钮的View */ + private View mView; + /** 是否禁用后设置半透明度 */ + private boolean isAlpha; - private List mViewSet; // TextView集合 + /** TextView集合 */ + private List mViewSet; - public InputTextHelper(View view) { - this(view, false); - } + /** 输入监听器 */ + private OnInputTextListener mListener; /** * 构造函数 @@ -35,19 +39,30 @@ public InputTextHelper(View view) { * @param view 跟随 TextView 输入为空来判断启动或者禁用这个 View * @param alpha 是否需要设置透明度 */ - public InputTextHelper(View view, boolean alpha) { - if (view == null) throw new IllegalArgumentException("The view is empty"); + private InputTextHelper(View view, boolean alpha) { + if (view == null) { + throw new IllegalArgumentException("The view is empty"); + } mView = view; isAlpha = alpha; } + /** + * 创建 Builder + */ + public static Builder with(Activity activity) { + return new Builder(activity); + } + /** * 添加 TextView * * @param views 传入单个或者多个 TextView */ public void addViews(List views) { - if (views == null) return; + if (views == null) { + return; + } if (mViewSet == null) { mViewSet = views; @@ -60,7 +75,7 @@ public void addViews(List views) { } // 触发一次监听 - afterTextChanged(null); + notifyChanged(); } /** @@ -69,25 +84,46 @@ public void addViews(List views) { * @param views 传入单个或者多个 TextView */ public void addViews(TextView... views) { - if (views == null) return; + if (views == null) { + return; + } if (mViewSet == null) { - mViewSet = new ArrayList<>(views.length - 1); + mViewSet = new ArrayList<>(views.length); } for (TextView view : views) { - view.addTextChangedListener(this); - mViewSet.add(view); + // 避免重复添加 + if (!mViewSet.contains(view)) { + view.addTextChangedListener(this); + mViewSet.add(view); + } } // 触发一次监听 - afterTextChanged(null); + notifyChanged(); } /** * 移除 TextView 监听,避免内存泄露 */ - public void removeViews() { - if (mViewSet == null) return; + public void removeViews(TextView... views) { + if (mViewSet != null && mViewSet.size() > 0) { + for (TextView view : views) { + view.removeTextChangedListener(this); + mViewSet.remove(view); + } + // 触发一次监听 + notifyChanged(); + } + } + + /** + * 移除所有 TextView 监听,避免内存泄露 + */ + public void removeAllViews() { + if (mViewSet == null) { + return; + } for (TextView view : mViewSet) { view.removeTextChangedListener(this); @@ -96,6 +132,13 @@ public void removeViews() { mViewSet = null; } + /** + * 设置输入监听 + */ + public void setListener(OnInputTextListener listener) { + mListener = listener; + } + /** * {@link TextWatcher} */ @@ -108,8 +151,18 @@ public void onTextChanged(CharSequence s, int start, int before, int count) {} @Override public void afterTextChanged(Editable s) { - if (mViewSet == null) return; + notifyChanged(); + } + /** + * 通知更新 + */ + public void notifyChanged() { + if (mViewSet == null) { + return; + } + + // 重新遍历所有的输入 for (TextView view : mViewSet) { if ("".equals(view.getText().toString())) { setEnabled(false); @@ -117,7 +170,11 @@ public void afterTextChanged(Editable s) { } } - setEnabled(true); + if (mListener != null) { + setEnabled(mListener.onInputChange(this)); + } else { + setEnabled(true); + } } /** @@ -126,7 +183,9 @@ public void afterTextChanged(Editable s) { * @param enabled 启用或者禁用 View 的事件 */ public void setEnabled(boolean enabled) { - if (enabled == mView.isEnabled()) return; + if (enabled == mView.isEnabled()) { + return; + } if (enabled) { //启用View的事件 @@ -135,7 +194,7 @@ public void setEnabled(boolean enabled) { //设置不透明 mView.setAlpha(1f); } - }else { + } else { //禁用View的事件 mView.setEnabled(false); if (isAlpha) { @@ -145,21 +204,28 @@ public void setEnabled(boolean enabled) { } } - public static final class Builder implements Application.ActivityLifecycleCallbacks { - - private Activity mActivity; // 当前的Activity - private View mView; // 操作按钮的View - private boolean isAlpha; // 是否禁用后设置半透明度 - private List mViewSet = new ArrayList<>(); // TextView集合 - - InputTextHelper mTextHelper; - - public Builder() { - + public static final class Builder { + + /** 当前的 Activity */ + private final Activity mActivity; + /** 操作按钮的 View */ + private View mView; + /** 是否禁用后设置半透明度 */ + private boolean isAlpha; + /** TextView集合 */ + private final List mViewSet = new ArrayList<>(); + /** 文本输入辅助类 */ + private InputTextHelper mTextHelper; + /** 文本 */ + private OnInputTextListener mListener; + + private Builder(Activity activity) { + mActivity = activity; } - public Builder(Activity activity) { - mActivity = activity; + public Builder addView(TextView view) { + mViewSet.add(view); + return this; } public Builder setMain(View view) { @@ -172,55 +238,68 @@ public Builder setAlpha(boolean alpha) { return this; } - public Builder addView(TextView view) { - mViewSet.add(view); + public Builder setListener(OnInputTextListener listener) { + mListener = listener; return this; } public InputTextHelper build(){ - if (mActivity != null) { - mActivity.getApplication().registerActivityLifecycleCallbacks(this); - } mTextHelper = new InputTextHelper(mView, isAlpha); mTextHelper.addViews(mViewSet); + mTextHelper.setListener(mListener); + mActivity.getApplication().registerActivityLifecycleCallbacks(new TextInputLifecycle(mActivity, mTextHelper)); return mTextHelper; } + } - @Override - public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + private static class TextInputLifecycle implements Application.ActivityLifecycleCallbacks { - } - - @Override - public void onActivityStarted(Activity activity) { + private Activity mActivity; + private InputTextHelper mTextHelper; + private TextInputLifecycle(Activity activity, InputTextHelper helper) { + this.mActivity = activity; + this.mTextHelper = helper; } @Override - public void onActivityResumed(Activity activity) { - - } + public void onActivityCreated(@NonNull Activity activity, Bundle savedInstanceState) {} @Override - public void onActivityPaused(Activity activity) { - - } + public void onActivityStarted(@NonNull Activity activity) {} @Override - public void onActivityStopped(Activity activity) { + public void onActivityResumed(@NonNull Activity activity) {} - } + @Override + public void onActivityPaused(@NonNull Activity activity) {} @Override - public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + public void onActivityStopped(@NonNull Activity activity) {} - } + @Override + public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {} @Override - public void onActivityDestroyed(Activity activity) { + public void onActivityDestroyed(@NonNull Activity activity) { if (mActivity != null && mActivity == activity) { - mTextHelper.removeViews(); + mTextHelper.removeAllViews(); + mActivity.getApplication().registerActivityLifecycleCallbacks(this); + mTextHelper = null; + mActivity = null; } } } + + /** + * 文本变化监听器 + */ + public interface OnInputTextListener { + + /** + * 输入发生了变化 + * @return 返回按钮的 Enabled 状态 + */ + boolean onInputChange(InputTextHelper helper); + } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/helper/IntentExtraUtils.java b/app/src/main/java/com/hjq/demo/helper/IntentExtraUtils.java deleted file mode 100644 index 0baa09b5..00000000 --- a/app/src/main/java/com/hjq/demo/helper/IntentExtraUtils.java +++ /dev/null @@ -1,298 +0,0 @@ -package com.hjq.demo.helper; - -import android.app.Activity; -import android.app.Fragment; -import android.content.Context; -import android.content.Intent; - -import java.util.HashMap; -import java.util.List; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2018/10/18 - * desc : 意图数据存取工具类 - */ -public final class IntentExtraUtils { - - private static Class sCurrentClass; - - private static IntentExtraUtils sInstance; - - private static HashMap sMap; - - private IntentExtraUtils() {} - - public static IntentExtraUtils getInstance(Class cls) { - if (sInstance == null) sInstance = new IntentExtraUtils(); - if (sMap == null) sMap = new HashMap<>(); - sCurrentClass = cls; - return sInstance; - } - - /** - * 跳转到Activity - * - * @param context context对象 - */ - public IntentExtraUtils startActivity(Context context) { - return startActivity(context, false); - } - - /** - * 跳转到Activity后再销毁当前Activity - * - * @param activity activity对象 - */ - public IntentExtraUtils startActivityFinish(Activity activity) { - startActivity(activity, false); - activity.finish(); - return this; - } - - /** - * 跳转到Activity - * - * @param context context对象 - * @param newTask 是否开启新的任务栈 - */ - public IntentExtraUtils startActivity(Context context, boolean newTask) { - Intent intent = new Intent(context, sCurrentClass); - if (newTask) { - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - } - context.startActivity(intent); - return this; - } - - /** - * 跳转到Activity - * - * @param activity activity对象 - * @param requestCode 请求码 - */ - public IntentExtraUtils startActivity(Activity activity, int requestCode) { - activity.startActivityForResult(new Intent(activity, sCurrentClass), requestCode); - return this; - } - - public IntentExtraUtils startActivity(Fragment fragment, int requestCode) { - fragment.startActivityForResult(new Intent(fragment.getActivity(), sCurrentClass), requestCode); - return this; - } - - public IntentExtraUtils startActivity(androidx.fragment.app.Fragment fragment, int requestCode) { - fragment.startActivityForResult(new Intent(fragment.getActivity(), sCurrentClass), requestCode); - return this; - } - - /** - * 设置结果码 - * - * @param activity activity对象 - * @param resultCode 结果码 - */ - public IntentExtraUtils setResult(Activity activity, int resultCode) { - activity.setResult(resultCode); - return this; - } - - /** - * 销毁Activity - * - * @param activity activity对象 - */ - public void finish(Activity activity) { - activity.finish(); - } - - // Object - - public IntentExtraUtils put(Class clazz, Object object) { - return put(sCurrentClass + clazz.getName(), object); - } - - public IntentExtraUtils put(String key, Object object) { - sMap.put(key, object); - return this; - } - - public T get(Class clazz) { - return get(sCurrentClass + clazz.getName()); - } - - public T get(String key) { - T t = (T) sMap.get(key); - //移除这个对象,避免内存泄露 - sMap.remove(key); - return t; - } - - // String - - public IntentExtraUtils putString(String s) { - return put(String.class, s); - } - - public IntentExtraUtils putString(String key, String s) { - return put(key, s); - } - - public String getString() { - return get(String.class); - } - - public String getString(String key) { - return get(key); - } - - // Integer - - public IntentExtraUtils putInteger(Integer i) { - return put(Integer.class, i); - } - - public IntentExtraUtils putInteger(String key, Integer i) { - return put(key, i); - } - - public Integer getInteger() { - return get(Integer.class); - } - - public Integer getInteger(String key) { - return get(key); - } - - // Long - - public IntentExtraUtils putLong(Long l) { - return put(Long.class, l); - } - - public IntentExtraUtils putLong(String key, Long l) { - return put(key, l); - } - - public Long getLong() { - return get(Long.class); - } - - public Long getLong(String key) { - return get(key); - } - - // Boolean - - public IntentExtraUtils putBoolean(Boolean b) { - return put(Boolean.class, b); - } - - public IntentExtraUtils putBoolean(String key, Boolean b) { - return put(key, b); - } - - public Boolean getBoolean() { - return get(Boolean.class); - } - - public Boolean getBoolean(String key) { - return get(key); - } - - // Double - - public IntentExtraUtils putDouble(Double d) { - return put(Double.class, d); - } - - public IntentExtraUtils putDouble(String key, Double d) { - return put(key, d); - } - - public Double getDouble() { - return get(Double.class); - } - - public Double getDouble(String key) { - return get(key); - } - - // Float - - public IntentExtraUtils putFloat(Float f) { - return put(Float.class, f); - } - - public IntentExtraUtils putFloat(String key, Float f) { - return put(key, f); - } - - public Float getFloat() { - return get(Float.class); - } - - public Float getFloat(String key) { - return get(key); - } - - // List - - public IntentExtraUtils putList(List list) { - return put(List.class, list); - } - - public IntentExtraUtils putList(String key, List list) { - return put(key, list); - } - - public List getList() { - return get(List.class); - } - - public List getList(String key) { - return get(key); - } - - public static class Key { - - public static final String ID = "id"; // id - public static final String TOKEN = "token"; // token - public static final String ORDER = "order"; // 订单 - public static final String BALANCE = "balance"; // 余额 - public static final String TIME = "time"; // 时间 - public static final String CODE = "code"; // 错误码或者其他码 - public static final String URL = "url"; // URL - public static final String PATH = "path"; // 路径 - public static final String OTHER = "other"; // 其他 - - // 个人信息 - public static final String NAME = "name"; // 姓名 - public static final String AGE = "age"; // 年龄 - public static final String SEX = "sex"; // 性别 - public static final String PHONE = "phone"; // 手机 - public static final String VIP = "vip"; // 会员 - public static final String DESCRIBE = "describe"; // 描述 - public static final String REMARK = "remark"; // 备注 - public static final String CONSTELLATION = "constellation"; // 星座 - - // 地方 - public static final String ADDRESS = "address"; // 地址 - public static final String PROVINCE = "province"; // 省 - public static final String CITY = "city"; // 市 - public static final String DISTRICT = "district"; // 区 - - // 文件类型相关 - public static final String TXT = "txt"; // 文本 - public static final String PICTURE = "picture"; // 图片 - public static final String VOICE = "voice"; // 音频 - public static final String VIDEO = "video"; // 视频 - - // 支付相关 - public static final String BALANCE_PAY = "balance_pay"; // 余额支付 - public static final String WECHAT_PAY = "wechat_pay"; //微信支付 - public static final String ALI_PAY = "ali_pay"; //支付宝支付 - public static final String UNION_PAY = "union_pay"; // 银联支付 - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/helper/KeyboardUtils.java b/app/src/main/java/com/hjq/demo/helper/KeyboardUtils.java index 947605b6..0a013139 100644 --- a/app/src/main/java/com/hjq/demo/helper/KeyboardUtils.java +++ b/app/src/main/java/com/hjq/demo/helper/KeyboardUtils.java @@ -18,11 +18,12 @@ public final class KeyboardUtils { * @param view 依附的View */ public static void showKeyboard(View view) { - if (view == null) return; + if (view == null) { + return; + } InputMethodManager imm = (InputMethodManager) view.getContext() .getSystemService(Context.INPUT_METHOD_SERVICE); if (imm != null) { - //view.requestFocus(); imm.showSoftInput(view, 0); } } @@ -33,7 +34,9 @@ public static void showKeyboard(View view) { * @param view 依附的View */ public static void hideKeyboard(View view) { - if (view == null) return; + if (view == null) { + return; + } InputMethodManager imm = (InputMethodManager) view.getContext() .getSystemService(Context.INPUT_METHOD_SERVICE); if (imm != null) { @@ -47,7 +50,9 @@ public static void hideKeyboard(View view) { * @param view 依附的View */ public static void toggleSoftInput(View view) { - if (view == null) return; + if (view == null) { + return; + } InputMethodManager imm = (InputMethodManager) view.getContext() .getSystemService(Context.INPUT_METHOD_SERVICE); if (imm != null) { diff --git a/app/src/main/java/com/hjq/demo/helper/PopupWindowHelper.java b/app/src/main/java/com/hjq/demo/helper/PopupWindowHelper.java index 1a668c25..25931a6b 100644 --- a/app/src/main/java/com/hjq/demo/helper/PopupWindowHelper.java +++ b/app/src/main/java/com/hjq/demo/helper/PopupWindowHelper.java @@ -3,14 +3,15 @@ import android.content.Context; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; 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 @@ -19,16 +20,19 @@ */ public final class PopupWindowHelper implements PopupWindow.OnDismissListener { - private PopupWindow mPopupWindow; // PopupWindow对象 - private final View mPopupView; //PopupWindow显示的View - private long mDismissTime; // 记录PopupWindow销毁时间 + /** 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 layoutId) { - mPopupView = View.inflate(context, layoutId, null); + public PopupWindowHelper(Context context, int id) { + mPopupView = View.inflate(context, id, null); } /** @@ -109,14 +113,16 @@ public boolean isShowing() { /** * 获取当前的PopupWindow对象 */ - public @Nullable PopupWindow getPopupWindow() { + @Nullable + public PopupWindow getPopupWindow() { return mPopupWindow; } /** * 获取当前的PopupWindow的View对象 */ - public @NonNull View getPopupView() { + @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 9c259636..f561647a 100644 --- a/app/src/main/java/com/hjq/demo/helper/RadioButtonGroupHelper.java +++ b/app/src/main/java/com/hjq/demo/helper/RadioButtonGroupHelper.java @@ -1,10 +1,11 @@ package com.hjq.demo.helper; -import androidx.annotation.IdRes; import android.view.View; import android.widget.CompoundButton; import android.widget.RadioButton; +import androidx.annotation.IdRes; + import java.util.ArrayList; import java.util.List; @@ -16,9 +17,11 @@ */ public final class RadioButtonGroupHelper implements CompoundButton.OnCheckedChangeListener { - private List mViewSet;//RadioButton集合 + /** RadioButton集合 */ + private List mViewSet; - private OnCheckedChangeListener mListener;//多个RadioButton监听对象 + /** 多个RadioButton监听对象 */ + private OnCheckedChangeListener mListener; public RadioButtonGroupHelper(RadioButton... groups) { mViewSet = new ArrayList<>(groups.length - 1); @@ -35,14 +38,15 @@ public RadioButtonGroupHelper(RadioButton... groups) { public RadioButtonGroupHelper(View rootView, @IdRes int... ids) { mViewSet = new ArrayList<>(ids.length - 1); - for (int id : ids) { + for (@IdRes int id : ids) { RadioButton view = rootView.findViewById(id); view.setOnCheckedChangeListener(this); mViewSet.add(view); } } - private boolean mTag; // 监听标记,避免重复回调 + /** 监听标记,避免重复回调 */ + private boolean mTag; /** * {@link CompoundButton.OnCheckedChangeListener} @@ -68,7 +72,9 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { * 移除监听,避免内存泄露 */ public void removeViews() { - if (mViewSet == null) return; + if (mViewSet == null) { + return; + } for (CompoundButton view : mViewSet) { view.setOnCheckedChangeListener(null); @@ -91,8 +97,8 @@ public void clearCheck() { /** * 设置多个RadioButton的监听 */ - public void setOnCheckedChangeListener(OnCheckedChangeListener l) { - mListener = l; + public void setOnCheckedChangeListener(OnCheckedChangeListener listener) { + mListener = listener; } /** diff --git a/app/src/main/java/com/hjq/demo/mvp/IMvpView.java b/app/src/main/java/com/hjq/demo/mvp/IMvpView.java index 2a607057..bc45af0f 100644 --- a/app/src/main/java/com/hjq/demo/mvp/IMvpView.java +++ b/app/src/main/java/com/hjq/demo/mvp/IMvpView.java @@ -1,5 +1,7 @@ package com.hjq.demo.mvp; +import android.content.Context; + /** * author : Android 轮子哥 * github : https://github.com/getActivity/AndroidProject @@ -8,6 +10,11 @@ */ public interface IMvpView { + /** + * 获取上下文对象 + */ + Context getContext(); + /** * 加载中 */ diff --git a/app/src/main/java/com/hjq/demo/mvp/MvpActivity.java b/app/src/main/java/com/hjq/demo/mvp/MvpActivity.java index e804b2f7..f4c95f73 100644 --- a/app/src/main/java/com/hjq/demo/mvp/MvpActivity.java +++ b/app/src/main/java/com/hjq/demo/mvp/MvpActivity.java @@ -1,6 +1,10 @@ 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 轮子哥 @@ -8,33 +12,32 @@ * time : 2018/11/17 * desc : MVP Activity 基类 */ -public abstract class MvpActivity

extends MyActivity implements IMvpView { +public abstract class MvpActivity extends MyActivity implements IMvpView { - private P mPresenter; + private IMvpPresenterProxy mMvpProxy; @Override public void initActivity() { - mPresenter = createPresenter(); - mPresenter.attach(this); + mMvpProxy = createPresenterProxy(); + mMvpProxy.bindPresenter(); super.initActivity(); - mPresenter.start(); + } + + protected IMvpPresenterProxy createPresenterProxy() { + return new MvpPresenterProxyImpl(this); } @Override protected void onDestroy() { - if (mPresenter != null) { - mPresenter.detach(); - mPresenter = null; - } + mMvpProxy.unbindPresenter(); super.onDestroy(); } - public P getPresenter() { - return mPresenter; + @Override + public Context getContext() { + return this; } - protected abstract P createPresenter(); - @Override public void onLoading() { showLoading(); diff --git a/app/src/main/java/com/hjq/demo/mvp/MvpInject.java b/app/src/main/java/com/hjq/demo/mvp/MvpInject.java new file mode 100644 index 00000000..9f932431 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/mvp/MvpInject.java @@ -0,0 +1,16 @@ +package com.hjq.demo.mvp; + +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/05/11 + * desc : Mvp 实例化注解 + */ +@Target(ElementType.FIELD) // 字段注解 +@Retention(RetentionPolicy.RUNTIME) // 运行时注解 +public @interface MvpInject {} \ 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 index 07c3c20a..981be9ac 100644 --- a/app/src/main/java/com/hjq/demo/mvp/MvpLazyFragment.java +++ b/app/src/main/java/com/hjq/demo/mvp/MvpLazyFragment.java @@ -1,6 +1,8 @@ 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 轮子哥 @@ -8,30 +10,26 @@ * time : 2018/11/17 * desc : MVP 懒加载 Fragment 基类 */ -public abstract class MvpLazyFragment

extends MyLazyFragment implements IMvpView { +public abstract class MvpLazyFragment extends MyLazyFragment implements IMvpView { - private P mPresenter; + private IMvpPresenterProxy mMvpProxy; @Override protected void initFragment() { - mPresenter = createPresenter(); - mPresenter.attach(this); + mMvpProxy = createPresenterProxy(); + mMvpProxy.bindPresenter(); super.initFragment(); - mPresenter.start(); + } + + protected IMvpPresenterProxy createPresenterProxy() { + return new MvpPresenterProxyImpl(this); } @Override public void onDestroy() { - if (mPresenter != null) { - mPresenter.detach(); - mPresenter = null; + if (mMvpProxy != null) { + mMvpProxy.unbindPresenter(); } super.onDestroy(); } - - public P getPresenter() { - return mPresenter; - } - - protected abstract P createPresenter(); } \ 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 index 5e38240e..029eab4e 100644 --- a/app/src/main/java/com/hjq/demo/mvp/MvpModel.java +++ b/app/src/main/java/com/hjq/demo/mvp/MvpModel.java @@ -10,8 +10,8 @@ public abstract class MvpModel { private L mListener; - public void setListener(L l) { - mListener = l; + public void setListener(L listener) { + mListener = listener; } public L getListener() { diff --git a/app/src/main/java/com/hjq/demo/mvp/MvpPresenter.java b/app/src/main/java/com/hjq/demo/mvp/MvpPresenter.java index ae8fb017..6060e42e 100644 --- a/app/src/main/java/com/hjq/demo/mvp/MvpPresenter.java +++ b/app/src/main/java/com/hjq/demo/mvp/MvpPresenter.java @@ -1,5 +1,13 @@ 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; @@ -12,19 +20,27 @@ */ public abstract class MvpPresenter implements InvocationHandler { - // 当前 View 对象 + /** View 层 */ private V mView; - // 代理对象 + /** 代理对象 */ private V mProxyView; + private IMvpModelProxy mMvpProxy; + + protected IMvpModelProxy createModelProxyImpl() { + return new MvpModelProxyImpl(this); + } + @SuppressWarnings("unchecked") - public void attach(V view) { + 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(); } /** @@ -38,10 +54,11 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl return isAttached() ? method.invoke(mView, args) : null; } - public void detach() { + public void detachView() { mView = null; // 这里注意不能把代理对象置空 // mProxyView = null; + mMvpProxy.unbindModel(); } public boolean isAttached() { @@ -53,7 +70,24 @@ public V getView() { } /** - * P 层初始化方法 + * 获取上下文 */ - public abstract void start(); + 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 index 8328f9ca..885760b1 100644 --- a/app/src/main/java/com/hjq/demo/mvp/copy/CopyContract.java +++ b/app/src/main/java/com/hjq/demo/mvp/copy/CopyContract.java @@ -14,12 +14,13 @@ public final class CopyContract { public interface View extends IMvpView { - void loginError(String msg); - 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 index 5a0e7de5..91579a35 100644 --- a/app/src/main/java/com/hjq/demo/mvp/copy/CopyModel.java +++ b/app/src/main/java/com/hjq/demo/mvp/copy/CopyModel.java @@ -29,7 +29,7 @@ public void login() { // 为了省事,这里直接回调成功 if ("账户".equals(mAccount) && "密码".equals(mPassword)) { getListener().onSucceed(null); - }else { + } else { getListener().onFail("账户或密码不对哦"); } } 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 index 8960ba4f..3e501af2 100644 --- a/app/src/main/java/com/hjq/demo/mvp/copy/CopyMvpActivity.java +++ b/app/src/main/java/com/hjq/demo/mvp/copy/CopyMvpActivity.java @@ -2,7 +2,9 @@ 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; @@ -12,21 +14,14 @@ * time : 2018/11/17 * desc : 可进行拷贝的MVP Activity 类 */ -public final class CopyMvpActivity extends MvpActivity implements CopyContract.View { +public final class CopyMvpActivity extends MvpActivity implements CopyContract.View { - @Override - protected CopyPresenter createPresenter() { - return new CopyPresenter(); - } + @MvpInject + CopyPresenter mPresenter; @Override protected int getLayoutId() { - return 0; - } - - @Override - protected int getTitleId() { - return 0; + return R.layout.activity_copy; } @Override @@ -41,7 +36,7 @@ protected void initData() { public void onLogin(View view) { // 登录操作 - getPresenter().login("账户", "密码"); + mPresenter.login("账户", "密码"); } /** 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 index 1517d914..ef01ebd2 100644 --- a/app/src/main/java/com/hjq/demo/mvp/copy/CopyPresenter.java +++ b/app/src/main/java/com/hjq/demo/mvp/copy/CopyPresenter.java @@ -1,5 +1,6 @@ package com.hjq.demo.mvp.copy; +import com.hjq.demo.mvp.MvpInject; import com.hjq.demo.mvp.MvpPresenter; import java.util.List; @@ -13,12 +14,8 @@ public final class CopyPresenter extends MvpPresenter implements CopyContract.Presenter, CopyOnListener { - private CopyModel mModel; - - @Override - public void start() { - mModel = new CopyModel(); - } + @MvpInject + CopyModel mModel; /** * {@link CopyContract.Presenter} 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 new file mode 100644 index 00000000..4b0d7c8c --- /dev/null +++ b/app/src/main/java/com/hjq/demo/mvp/proxy/IMvpModelProxy.java @@ -0,0 +1,19 @@ +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 new file mode 100644 index 00000000..5b893c80 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/mvp/proxy/IMvpPresenterProxy.java @@ -0,0 +1,19 @@ +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 new file mode 100644 index 00000000..5f530910 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/mvp/proxy/MvpModelProxyImpl.java @@ -0,0 +1,68 @@ +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 new file mode 100644 index 00000000..32b8e23e --- /dev/null +++ b/app/src/main/java/com/hjq/demo/mvp/proxy/MvpPresenterProxyImpl.java @@ -0,0 +1,73 @@ +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 new file mode 100644 index 00000000..2dd2ffc4 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/other/AppConfig.java @@ -0,0 +1,47 @@ +package com.hjq.demo.other; + +import com.hjq.demo.BuildConfig; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/09/02 + * desc : App 配置管理类 + */ +public final class AppConfig { + + /** + * 当前是否为 Debug 模式 + */ + public static boolean isDebug() { + return BuildConfig.DEBUG; + } + + /** + * 获取当前应用的包名 + */ + public static String getPackageName() { + return BuildConfig.APPLICATION_ID; + } + + /** + * 获取当前应用的版本名 + */ + public static String getVersionName() { + return BuildConfig.VERSION_NAME; + } + + /** + * 获取当前应用的版本码 + */ + public static int getVersionCode() { + return BuildConfig.VERSION_CODE; + } + + /** + * 获取当前应用的渠道名 + */ + public static String getProductFlavors() { + return BuildConfig.FLAVOR; + } +} \ 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 index 4c1b1e43..f8f796cb 100644 --- a/app/src/main/java/com/hjq/demo/other/EventBusManager.java +++ b/app/src/main/java/com/hjq/demo/other/EventBusManager.java @@ -17,13 +17,13 @@ */ public final class EventBusManager { - // EventBus 索引类 + /** EventBus 索引类 */ private static final SubscriberInfoIndex SUBSCRIBE_INDEX = new MyEventBusIndex(); - // 这个类是否需要注册 EventBus + /** 这个类是否需要注册 EventBus */ private static final ArrayMap SUBSCRIBE_EVENT = new ArrayMap<>(); - // 不允许被外部实例化 + /** 不允许被外部实例化 */ private EventBusManager() {} /** @@ -31,9 +31,12 @@ private EventBusManager() {} */ public static void init() { EventBus.builder() - .ignoreGeneratedIndex(false) // 使用 Apt 插件 - .addIndex(SUBSCRIBE_INDEX) // 添加索引类 - .installDefaultEventBus(); // 作为默认配置 + // 使用 Apt 插件 + .ignoreGeneratedIndex(false) + // 添加索引类 + .addIndex(SUBSCRIBE_INDEX) + // 作为默认配置 + .installDefaultEventBus(); } /** diff --git a/app/src/main/java/com/hjq/demo/other/IntentKey.java b/app/src/main/java/com/hjq/demo/other/IntentKey.java new file mode 100644 index 00000000..53a1ef2e --- /dev/null +++ b/app/src/main/java/com/hjq/demo/other/IntentKey.java @@ -0,0 +1,97 @@ +package com.hjq.demo.other; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/05/09 + * desc : Intent Key 管理 + */ +public final class IntentKey { + + // 常用相关 + + /** id */ + public static final String ID = "id"; + /** token */ + public static final String TOKEN = "token"; + /** 索引 */ + public static final String INDEX = "index"; + /** 位置 */ + public static final String POSITION = "position"; + /** 状态 */ + public static final String STATUS = "status"; + /** 类型 */ + public static final String TYPE = "type"; + /** 订单 */ + public static final String ORDER = "order"; + /** 余额 */ + public static final String BALANCE = "balance"; + /** 时间 */ + public static final String TIME = "time"; + /** 代码 */ + public static final String CODE = "code"; + /** URL */ + public static final String URL = "url"; + /** 路径 */ + public static final String PATH = "path"; + /** 数量 */ + public static final String AMOUNT = "amount"; + /** 总数 */ + public static final String COUNT = "count"; + /** 其他 */ + public static final String OTHER = "other"; + + // 个人信息 + + /** 姓名 */ + public static final String NAME = "name"; + /** 年龄 */ + public static final String AGE = "age"; + /** 性别 */ + public static final String SEX = "sex"; + /** 手机 */ + public static final String PHONE = "phone"; + /** 密码 */ + public static final String PASSWORD = "password"; + /** 会员 */ + public static final String VIP = "vip"; + /** 描述 */ + public static final String DESCRIBE = "describe"; + /** 备注 */ + public static final String REMARK = "remark"; + /** 星座 */ + public static final String CONSTELLATION = "constellation"; + + // 地方 + + /** 地址 */ + public static final String ADDRESS = "address"; + /** 省 */ + public static final String PROVINCE = "province"; + /** 市 */ + public static final String CITY = "city"; + /** 区 */ + public static final String AREA = "area"; + + // 文件类型相关 + + /** 文本 */ + public static final String TXT = "txt"; + /** 图片 */ + public static final String PICTURE = "picture"; + /** 音频 */ + public static final String VOICE = "voice"; + /** 视频 */ + public static final String VIDEO = "video"; + + // 支付相关 + + /** 余额支付 */ + public static final String BALANCE_PAY = "balance_pay"; + /** 微信支付 */ + public static final String WECHAT_PAY = "wechat_pay"; + /** 支付宝支付 */ + public static final String ALI_PAY = "ali_pay"; + /** 银联支付 */ + public static final String UNION_PAY = "union_pay"; +} diff --git a/app/src/main/java/com/hjq/demo/other/KeyboardWatcher.java b/app/src/main/java/com/hjq/demo/other/KeyboardWatcher.java new file mode 100644 index 00000000..0ad0dfdd --- /dev/null +++ b/app/src/main/java/com/hjq/demo/other/KeyboardWatcher.java @@ -0,0 +1,150 @@ +package com.hjq.demo.other; + +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; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/07/04 + * desc : 软键盘监听类 + */ +public final class KeyboardWatcher implements + ViewTreeObserver.OnGlobalLayoutListener, + Application.ActivityLifecycleCallbacks { + + private Activity mActivity; + private View mContentView; + private SoftKeyboardStateListener mListeners; + private boolean isSoftKeyboardOpened; + private int mStatusBarHeight; + + public static KeyboardWatcher with(Activity activity) { + return new KeyboardWatcher(activity); + } + + private KeyboardWatcher(Activity activity) { + mActivity = activity; + mContentView = activity.findViewById(Window.ID_ANDROID_CONTENT); + + mActivity.getApplication().registerActivityLifecycleCallbacks(this); + mContentView.getViewTreeObserver().addOnGlobalLayoutListener(this); + + // 获取 status_bar_height 资源的 ID + int resourceId = mActivity.getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + //根据资源 ID 获取响应的尺寸值 + mStatusBarHeight = mActivity.getResources().getDimensionPixelSize(resourceId); + } + } + + /** + * {@link ViewTreeObserver.OnGlobalLayoutListener} + */ + + @Override + public void onGlobalLayout() { + final Rect r = new Rect(); + //r will be populated with the coordinates of your view that area still visible. + mContentView.getWindowVisibleDisplayFrame(r); + + final int heightDiff = mContentView.getRootView().getHeight() - (r.bottom - r.top); + if (!isSoftKeyboardOpened && heightDiff > mContentView.getRootView().getHeight() / 4) { + isSoftKeyboardOpened = true; + if ((mActivity.getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != WindowManager.LayoutParams.FLAG_FULLSCREEN) { + if (mListeners != null) { + mListeners.onSoftKeyboardOpened(heightDiff - mStatusBarHeight); + } + } else { + if (mListeners != null) { + mListeners.onSoftKeyboardOpened(heightDiff); + } + } + + } else if (isSoftKeyboardOpened && heightDiff < mContentView.getRootView().getHeight() / 4) { + isSoftKeyboardOpened = false; + if (mListeners != null) { + mListeners.onSoftKeyboardClosed(); + } + } + } + + /** + * 设置软键盘弹出监听 + */ + public void setListener(SoftKeyboardStateListener listener) { + mListeners = listener; + } + + /** + * {@link Application.ActivityLifecycleCallbacks} + */ + + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + + } + + @Override + public void onActivityStarted(Activity activity) { + + } + + @Override + public void onActivityResumed(Activity activity) { + + } + + @Override + public void onActivityPaused(Activity activity) { + + } + + @Override + public void onActivityStopped(Activity activity) { + + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + + } + + @Override + public void onActivityDestroyed(Activity activity) { + if (mActivity == activity) { + mActivity.getApplication().unregisterActivityLifecycleCallbacks(this); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + mContentView.getViewTreeObserver().removeOnGlobalLayoutListener(this); + } + + mActivity = null; + mContentView = null; + mListeners = null; + } + } + + /** + * 软键盘状态监听器 + */ + public interface SoftKeyboardStateListener { + + /** + * 软键盘弹出了 + * @param keyboardHeight 软键盘高度 + */ + void onSoftKeyboardOpened(int keyboardHeight); + + /** + * 软键盘收起了 + */ + void onSoftKeyboardClosed(); + } +} \ 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/PhotoSpaceDecoration.java new file mode 100644 index 00000000..51959d78 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/other/PhotoSpaceDecoration.java @@ -0,0 +1,50 @@ +package com.hjq.demo.other; + +import android.graphics.Canvas; +import android.graphics.Rect; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/07/25 + * desc : 图片选择列表分割线 + */ +public final class PhotoSpaceDecoration extends RecyclerView.ItemDecoration { + + private final int mSpace; + + public PhotoSpaceDecoration(int space) { + mSpace = space; + } + + @Override + public void onDraw(@NonNull Canvas canvas, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.State state) {} + + @SuppressWarnings("all") + @Override + public void getItemOffsets(@NonNull Rect rect, @NonNull View view, RecyclerView recyclerView, @NonNull RecyclerView.State state) { + int position = recyclerView.getChildAdapterPosition(view); + int spanCount = ((GridLayoutManager) recyclerView.getLayoutManager()).getSpanCount(); + + // 每一行的最后一个才留出右边间隙 + if ((position + 1) % spanCount == 0) { + rect.right = mSpace; + } + + // 只有第一行才留出顶部间隙 + if (position < spanCount) { + rect.top = mSpace; + } + + rect.bottom = mSpace; + rect.left = mSpace; + } + + @Override + public void onDrawOver(@NonNull Canvas canvas, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.State state) {} +} \ 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 index beb17bf7..cb5b4d37 100644 --- a/app/src/main/java/com/hjq/demo/other/StatusManager.java +++ b/app/src/main/java/com/hjq/demo/other/StatusManager.java @@ -4,17 +4,19 @@ 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 android.view.View; -import android.view.ViewGroup; import com.hjq.base.BaseDialog; import com.hjq.demo.R; -import com.hjq.dialog.WaitDialog; -import com.hjq.widget.HintLayout; +import com.hjq.demo.ui.dialog.WaitDialog; +import com.hjq.widget.layout.HintLayout; import static android.Manifest.permission.ACCESS_NETWORK_STATE; @@ -26,19 +28,27 @@ */ 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("加载中...") // 消息文本可以不用填写 + .setMessage(text) .create(); } @@ -65,7 +75,7 @@ public void showComplete() { * 显示空提示 */ public void showEmpty(View view) { - showLayout(view, R.mipmap.icon_hint_empty, R.string.hint_layout_no_data); + showLayout(view, R.drawable.icon_hint_empty, R.string.hint_layout_no_data); } /** @@ -74,17 +84,17 @@ public void showEmpty(View view) { public void showError(View view) { // 判断当前网络是否可用 if (isNetworkAvailable(view.getContext())) { - showLayout(view, R.mipmap.icon_hint_request, R.string.hint_layout_error_request); + showLayout(view, R.drawable.icon_hint_request, R.string.hint_layout_error_request); } else { - showLayout(view, R.mipmap.icon_hint_nerwork, R.string.hint_layout_error_network); + showLayout(view, R.drawable.icon_hint_nerwork, R.string.hint_layout_error_network); } } /** * 显示自定义提示 */ - public void showLayout(View view, @DrawableRes int iconId, @StringRes int textId) { - showLayout(view, view.getResources().getDrawable(iconId), view.getResources().getString(textId)); + 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) { @@ -96,12 +106,13 @@ public void showLayout(View view, Drawable drawable, CharSequence hint) { if (view instanceof HintLayout) { mHintLayout = (HintLayout) view; - }else if (view instanceof ViewGroup) { + } else if (view instanceof ViewGroup) { mHintLayout = findHintLayout((ViewGroup) view); } if (mHintLayout == null) { - throw new IllegalStateException("You didn't add this HintLayout to your Activity layout"); + // 必须在布局中定义一个 HintLayout + throw new IllegalStateException("You didn't add this HintLayout to your layout"); } } mHintLayout.show(); @@ -109,15 +120,6 @@ public void showLayout(View view, Drawable drawable, CharSequence hint) { mHintLayout.setHint(hint); } - /** - * 判断网络功能是否可用 - */ - @RequiresPermission(ACCESS_NETWORK_STATE) - private static boolean isNetworkAvailable(Context context){ - NetworkInfo info = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo(); - return (info != null && info.isConnected()); - } - /** * 智能获取布局中的 HintLayout 对象 */ @@ -128,9 +130,20 @@ private static HintLayout findHintLayout(ViewGroup group) { return (HintLayout) view; } else if (view instanceof ViewGroup) { HintLayout layout = findHintLayout((ViewGroup) view); - if (layout != null) return layout; + 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/AboutActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/AboutActivity.java index f82ff60c..38847f32 100644 --- a/app/src/main/java/com/hjq/demo/ui/activity/AboutActivity.java +++ b/app/src/main/java/com/hjq/demo/ui/activity/AboutActivity.java @@ -16,11 +16,6 @@ protected int getLayoutId() { return R.layout.activity_about; } - @Override - protected int getTitleId() { - return R.id.tb_about_title; - } - @Override protected void initView() { diff --git a/app/src/main/java/com/hjq/demo/ui/activity/CopyActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/CopyActivity.java index 46844b68..d00dc196 100644 --- a/app/src/main/java/com/hjq/demo/ui/activity/CopyActivity.java +++ b/app/src/main/java/com/hjq/demo/ui/activity/CopyActivity.java @@ -16,11 +16,6 @@ protected int getLayoutId() { return R.layout.activity_copy; } - @Override - protected int getTitleId() { - return R.id.tb_copy_title; - } - @Override protected void initView() { 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 new file mode 100644 index 00000000..f6cda734 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/ui/activity/CrashActivity.java @@ -0,0 +1,85 @@ +package com.hjq.demo.ui.activity; + +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.DialogInterface; +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.common.MyActivity; + +import butterknife.OnClick; +import cat.ereza.customactivityoncrash.CustomActivityOnCrash; +import cat.ereza.customactivityoncrash.config.CaocConfig; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/06/27 + * desc : 崩溃捕捉界面 + */ +public final class CrashActivity extends MyActivity { + + private CaocConfig mConfig; + + @Override + protected int getLayoutId() { + return R.layout.activity_crash; + } + + @Override + protected void initView() { + + } + + @Override + protected void initData() { + mConfig = CustomActivityOnCrash.getConfigFromIntent(getIntent()); + if (mConfig == null) { + // 这种情况永远不会发生,只要完成该活动就可以避免递归崩溃。 + finish(); + } + } + + @OnClick({R.id.btn_crash_restart, R.id.btn_crash_log}) + 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 (textView != null) { + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12); + } + break; + default: + break; + } + } + + /** + * 复制报错信息到剪贴板 + */ + @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)); + } +} \ 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 8ef58cee..91861cd5 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 @@ -1,32 +1,35 @@ package com.hjq.demo.ui.activity; -import android.app.Dialog; import android.content.Intent; -import android.content.pm.PackageManager; -import androidx.annotation.Nullable; 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.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.dialog.AddressDialog; -import com.hjq.dialog.DateDialog; -import com.hjq.dialog.InputDialog; -import com.hjq.dialog.MenuDialog; -import com.hjq.dialog.MessageDialog; -import com.hjq.dialog.PayPasswordDialog; -import com.hjq.dialog.ToastDialog; -import com.hjq.dialog.WaitDialog; +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.TimeDialog; +import com.hjq.demo.ui.dialog.ToastDialog; +import com.hjq.demo.ui.dialog.WaitDialog; 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.List; import butterknife.OnClick; @@ -44,11 +47,6 @@ protected int getLayoutId() { return R.layout.activity_dialog; } - @Override - protected int getTitleId() { - return R.id.tb_dialog_title; - } - @Override protected void initView() { @@ -59,214 +57,295 @@ protected void initData() { } -// BaseDialog mWaitDialog; - @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_update, - R.id.btn_dialog_share, R.id.btn_dialog_custom}) + 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}) public void onClick(View v) { switch (v.getId()) { - case R.id.btn_dialog_message: // 消息对话框 + // 消息对话框 + case R.id.btn_dialog_message: new MessageDialog.Builder(this) - .setTitle("我是标题") // 标题可以不用填写 + // 标题可以不用填写 + .setTitle("我是标题") + // 内容必须要填写 .setMessage("我是内容") - .setConfirm("确定") - .setCancel("取消") // 设置 null 表示不显示取消按钮 - //.setAutoDismiss(false) // 设置点击按钮后不关闭对话框 + // 确定按钮文本 + .setConfirm(getString(R.string.common_confirm)) + // 设置 null 表示不显示取消按钮 + .setCancel(getString(R.string.common_cancel)) + // 设置点击按钮后不关闭对话框 + //.setAutoDismiss(false) .setListener(new MessageDialog.OnListener() { @Override - public void onConfirm(Dialog dialog) { + public void onConfirm(BaseDialog dialog) { toast("确定了"); } @Override - public void onCancel(Dialog dialog) { + public void onCancel(BaseDialog dialog) { toast("取消了"); } }) .show(); break; - case R.id.btn_dialog_input: // 输入对话框 + case R.id.btn_dialog_input: + // 输入对话框 new InputDialog.Builder(this) - .setTitle("我是标题") // 标题可以不用填写 + // 标题可以不用填写 + .setTitle("我是标题") + // 内容可以不用填写 .setContent("我是内容") + // 提示可以不用填写 .setHint("我是提示") - .setConfirm("确定") - .setCancel("取消") // 设置 null 表示不显示取消按钮 + // 确定按钮文本 + .setConfirm(getString(R.string.common_confirm)) + // 设置 null 表示不显示取消按钮 + .setCancel(getString(R.string.common_cancel)) //.setAutoDismiss(false) // 设置点击按钮后不关闭对话框 .setListener(new InputDialog.OnListener() { @Override - public void onConfirm(Dialog dialog, String content) { + public void onConfirm(BaseDialog dialog, String content) { toast("确定了:" + content); } @Override - public void onCancel(Dialog dialog) { + public void onCancel(BaseDialog dialog) { toast("取消了"); } }) .show(); break; - case R.id.btn_dialog_bottom_menu: // 底部选择框 + case R.id.btn_dialog_bottom_menu: List data = new ArrayList<>(); for (int i = 0; i < 10; i++) { data.add("我是数据" + i); } + // 底部选择框 new MenuDialog.Builder(this) - .setCancel("取消") // 设置 null 表示不显示取消按钮 - //.setAutoDismiss(false) // 设置点击按钮后不关闭对话框 + // 设置 null 表示不显示取消按钮 + //.setCancel(getString(R.string.common_cancel)) + // 设置点击按钮后不关闭对话框 + //.setAutoDismiss(false) .setList(data) - .setListener(new MenuDialog.OnListener() { + .setListener(new MenuDialog.OnListener() { @Override - public void onSelected(Dialog dialog, int position, String text) { - toast("位置:" + position + ",文本:" + text); + public void onSelected(BaseDialog dialog, int position, String string) { + toast("位置:" + position + ",文本:" + string); } @Override - public void onCancel(Dialog dialog) { + public void onCancel(BaseDialog dialog) { toast("取消了"); } }) - .setGravity(Gravity.BOTTOM) - .setAnimStyle(BaseDialog.AnimStyle.BOTTOM) .show(); break; - case R.id.btn_dialog_center_menu: // 居中选择框 + case R.id.btn_dialog_center_menu: List data1 = new ArrayList<>(); for (int i = 0; i < 10; i++) { data1.add("我是数据" + i); } + // 居中选择框 new MenuDialog.Builder(this) - .setCancel(null) // 设置 null 表示不显示取消按钮 - //.setAutoDismiss(false) // 设置点击按钮后不关闭对话框 + .setGravity(Gravity.CENTER) + // 设置 null 表示不显示取消按钮 + //.setCancel(null) + // 设置点击按钮后不关闭对话框 + //.setAutoDismiss(false) .setList(data1) - .setListener(new MenuDialog.OnListener() { + .setListener(new MenuDialog.OnListener() { @Override - public void onSelected(Dialog dialog, int position, String text) { - toast("位置:" + position + ",文本:" + text); + public void onSelected(BaseDialog dialog, int position, String string) { + toast("位置:" + position + ",文本:" + string); } @Override - public void onCancel(Dialog dialog) { + public void onCancel(BaseDialog dialog) { toast("取消了"); } }) - .setGravity(Gravity.CENTER) - .setAnimStyle(BaseDialog.AnimStyle.SCALE) .show(); break; - case R.id.btn_dialog_succeed_toast: // 成功对话框 + case R.id.btn_dialog_succeed_toast: + // 成功对话框 new ToastDialog.Builder(this) .setType(ToastDialog.Type.FINISH) .setMessage("完成") .show(); break; - case R.id.btn_dialog_fail_toast: // 失败对话框 + case R.id.btn_dialog_fail_toast: + // 失败对话框 new ToastDialog.Builder(this) .setType(ToastDialog.Type.ERROR) .setMessage("错误") .show(); break; - case R.id.btn_dialog_warn_toast: // 警告对话框 + case R.id.btn_dialog_warn_toast: + // 警告对话框 new ToastDialog.Builder(this) .setType(ToastDialog.Type.WARN) .setMessage("警告") .show(); break; - case R.id.btn_dialog_wait: // 等待对话框 + case R.id.btn_dialog_wait: + // 等待对话框 final BaseDialog dialog = new WaitDialog.Builder(this) - .setMessage("加载中...") // 消息文本可以不用填写 + // 消息文本可以不用填写 + .setMessage(getString(R.string.common_loading)) .show(); postDelayed(new Runnable() { @Override public void run() { dialog.dismiss(); } - }, 3000); - - /* - if(mWaitDialog == null){ - mWaitDialog = new WaitDialog.Builder(this) - .create(); - } - mWaitDialog.show(); - - postDelayed(new Runnable() { - - @Override - public void run() { - mWaitDialog.dismiss(); - } - }, 10000); - */ + }, 2000); break; - case R.id.btn_dialog_pay: // 支付密码输入对话框 + case R.id.btn_dialog_pay: + // 支付密码输入对话框 new PayPasswordDialog.Builder(this) - .setTitle("请输入支付密码") + .setTitle(getString(R.string.pay_title)) .setSubTitle("用于购买一个女盆友") .setMoney("¥ 100.00") //.setAutoDismiss(false) // 设置点击按钮后不关闭对话框 .setListener(new PayPasswordDialog.OnListener() { @Override - public void onCompleted(Dialog dialog, String password) { + public void onCompleted(BaseDialog dialog, String password) { toast(password); } @Override - public void onCancel(Dialog dialog) { + public void onCancel(BaseDialog dialog) { toast("取消了"); } }) .show(); break; - case R.id.btn_dialog_address: // 选择地区对话框 + case R.id.btn_dialog_address: + // 选择地区对话框 new AddressDialog.Builder(this) - .setTitle("选择地区") - //.setProvince("广东省") // 设置默认省份 - //.setCity("广州市") // 设置默认城市(必须要先设置默认省份) - //.setIgnoreArea() // 不选择县级区域 + .setTitle(getString(R.string.address_title)) + // 设置默认省份 + //.setProvince("广东省") + // 设置默认城市(必须要先设置默认省份) + //.setCity("广州市") + // 不选择县级区域 + //.setIgnoreArea() .setListener(new AddressDialog.OnListener() { @Override - public void onSelected(Dialog dialog, String province, String city, String area) { + public void onSelected(BaseDialog dialog, String province, String city, String area) { toast(province + city + area); } @Override - public void onCancel(Dialog dialog) { + public void onCancel(BaseDialog dialog) { toast("取消了"); } }) .show(); break; - case R.id.btn_dialog_date: // 日期选择对话框 + case R.id.btn_dialog_date: + // 日期选择对话框 new DateDialog.Builder(this) - .setTitle("请选择日期") + .setTitle(getString(R.string.date_title)) + // 确定按钮文本 + .setConfirm(getString(R.string.common_confirm)) + // 设置 null 表示不显示取消按钮 + .setCancel(getString(R.string.common_cancel)) + // 设置日期 + //.setDate("2018-12-31") + //.setDate("20181231") + //.setDate(1546263036137) + // 设置年份 + //.setYear(2018) + // 设置月份 + //.setMonth(2) + // 设置天数 + //.setDay(20) + // 不选择天数 + //.setIgnoreDay() .setListener(new DateDialog.OnListener() { @Override - public void onSelected(Dialog dialog, int year, int month, int day) { - toast(year + "年" + month + "月" + day + "日"); + public void onSelected(BaseDialog dialog, int year, int month, int day) { + toast(year + getString(R.string.common_year) + month + getString(R.string.common_month) + day + getString(R.string.common_day)); + + // 如果不指定时分秒则默认为现在的时间 + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.YEAR, year); + // 月份从零开始,所以需要减 1 + calendar.set(Calendar.MONTH, month - 1); + calendar.set(Calendar.DAY_OF_MONTH, day); + toast("时间戳:" + calendar.getTimeInMillis()); + //toast(new SimpleDateFormat("yyyy年MM月dd日 kk:mm:ss").format(calendar.getTime())); } @Override - public void onCancel(Dialog dialog) { + public void onCancel(BaseDialog dialog) { + toast("取消了"); + } + }) + .show(); + break; + case R.id.btn_dialog_time: + // 时间选择对话框 + new TimeDialog.Builder(this) + .setTitle(getString(R.string.time_title)) + // 确定按钮文本 + .setConfirm(getString(R.string.common_confirm)) + // 设置 null 表示不显示取消按钮 + .setCancel(getString(R.string.common_cancel)) + // 设置时间 + //.setTime("23:59:59") + //.setTime("235959") + // 设置小时 + //.setHour(23) + // 设置分钟 + //.setMinute(59) + // 设置秒数 + //.setSecond(59) + // 不选择秒数 + //.setIgnoreSecond() + .setListener(new TimeDialog.OnListener() { + + @Override + public void onSelected(BaseDialog dialog, int hour, int minute, int second) { + toast(hour + getString(R.string.common_hour) + minute + getString(R.string.common_minute) + second + getString(R.string.common_second)); + + // 如果不指定年月日则默认为今天的日期 + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR_OF_DAY, hour); + calendar.set(Calendar.MINUTE, minute); + calendar.set(Calendar.SECOND, second); + toast("时间戳:" + calendar.getTimeInMillis()); + //toast(new SimpleDateFormat("yyyy年MM月dd日 kk:mm:ss").format(calendar.getTime())); + } + + @Override + public void onCancel(BaseDialog dialog) { toast("取消了"); } }) .show(); break; case R.id.btn_dialog_share: + toast("记得改好第三方 AppID 和 AppKey,否则会调不起来哦"); + toast("也别忘了改微信 " + WXEntryActivity.class.getSimpleName() + " 类所在的包名哦"); + // 分享对话框 new ShareDialog.Builder(this) - .setShareTitle("分享标题") - .setShareDescription("分享描述") + // 分享标题 + .setShareTitle("Github") + // 分享描述 + .setShareDescription("AndroidProject") + // 分享缩略图 + .setShareLogo("https://avatars1.githubusercontent.com/u/28616817?s=460&v=4") + // 分享链接 .setShareUrl("https://github.com/getActivity/AndroidProject") - .setShareLogo("https://www.baidu.com/img/bd_logo1.png") .setListener(new UmengShare.OnShareListener() { @Override @@ -287,24 +366,23 @@ public void onCancel(Platform platform) { .show(); break; case R.id.btn_dialog_update: - try { - // 本地的版本码和服务器的进行比较 - if (20 > getPackageManager().getPackageInfo(getPackageName(), 0).versionCode) { - - new UpdateDialog.Builder(this) - .setVersionName("v 2.0") // 版本名 - .setFileSize("10 M") // 文件大小 - .setForceUpdate(false) // 是否强制更新 - .setUpdateLog("到底更新了啥") // 更新日志 - .setDownloadUrl("https://raw.githubusercontent.com/getActivity/AndroidProject/master/AndroidProject.apk") // 下载 url - .show(); - } - } catch (PackageManager.NameNotFoundException ignored) {} + new UpdateDialog.Builder(this) + // 版本名 + .setVersionName("v 2.0") + // 文件大小 + .setFileSize("10 M") + // 是否强制更新 + .setForceUpdate(false) + // 更新日志 + .setUpdateLog("到底更新了啥\n到底更新了啥\n到底更新了啥\n到底更新了啥\n到底更新了啥") + // 下载 url + .setDownloadUrl("https://raw.githubusercontent.com/getActivity/AndroidProject/master/AndroidProject.apk") + .show(); break; - case R.id.btn_dialog_custom: // 自定义对话框 + case R.id.btn_dialog_custom: + // 自定义对话框 new BaseDialogFragment.Builder(this) .setContentView(R.layout.dialog_custom) - .setAnimStyle(BaseDialog.AnimStyle.SCALE) //.setText(id, "我是预设置的文本") .setOnClickListener(R.id.btn_dialog_custom_ok, new BaseDialog.OnClickListener() { @@ -331,6 +409,13 @@ public void onDismiss(BaseDialog dialog) { toast("Dialog 销毁了"); } }) + .setOnKeyListener(new BaseDialog.OnKeyListener() { + @Override + public boolean onKey(BaseDialog dialog, KeyEvent event) { + toast("按键代码:" + event.getKeyCode()); + return false; + } + }) .show(); break; default: @@ -341,6 +426,7 @@ public void onDismiss(BaseDialog dialog) { @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); + // 友盟分享回调 UmengClient.onActivityResult(this, requestCode, resultCode, data); } } \ 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 0d85ac65..63216740 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 @@ -1,17 +1,20 @@ package com.hjq.demo.ui.activity; -import androidx.annotation.NonNull; -import com.google.android.material.bottomnavigation.BottomNavigationView; -import androidx.viewpager.widget.ViewPager; import android.view.KeyEvent; import android.view.MenuItem; +import android.view.View; +import androidx.annotation.NonNull; +import androidx.viewpager.widget.ViewPager; + +import com.google.android.material.bottomnavigation.BottomNavigationView; 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.helper.ActivityStackManager; import com.hjq.demo.helper.DoubleClickHelper; +import com.hjq.demo.other.KeyboardWatcher; import com.hjq.demo.ui.fragment.TestFragmentA; import com.hjq.demo.ui.fragment.TestFragmentB; import com.hjq.demo.ui.fragment.TestFragmentC; @@ -27,13 +30,15 @@ */ public final class HomeActivity extends MyActivity implements ViewPager.OnPageChangeListener, - BottomNavigationView.OnNavigationItemSelectedListener { + BottomNavigationView.OnNavigationItemSelectedListener, + KeyboardWatcher.SoftKeyboardStateListener { @BindView(R.id.vp_home_pager) ViewPager mViewPager; @BindView(R.id.bv_home_navigation) BottomNavigationView mBottomNavigationView; + /** ViewPager 适配器 */ private BaseFragmentAdapter mPagerAdapter; @Override @@ -41,11 +46,6 @@ protected int getLayoutId() { return R.layout.activity_home; } - @Override - protected int getTitleId() { - return 0; - } - @Override protected void initView() { mViewPager.addOnPageChangeListener(this); @@ -53,6 +53,9 @@ protected void initView() { // 不使用图标默认变色 mBottomNavigationView.setItemIconTintList(null); mBottomNavigationView.setOnNavigationItemSelectedListener(this); + + KeyboardWatcher.with(this) + .setListener(this); } @Override @@ -91,6 +94,8 @@ public void onPageSelected(int position) { case 3: mBottomNavigationView.setSelectedItemId(R.id.home_me); break; + default: + break; } } @@ -105,30 +110,36 @@ public void onPageScrollStateChanged(int state) {} public boolean onNavigationItemSelected(@NonNull MenuItem item) { switch (item.getItemId()) { case R.id.menu_home: - //mViewPager.setCurrentItem(0); - //mViewPager.setCurrentItem(0, false); - // 如果切换的是相邻之间的 Item 就显示切换动画,如果不是则不要动画 - mViewPager.setCurrentItem(0, mViewPager.getCurrentItem() == 1); + mViewPager.setCurrentItem(0); return true; case R.id.home_found: - //mViewPager.setCurrentItem(1); - //mViewPager.setCurrentItem(1, false); - mViewPager.setCurrentItem(1, mViewPager.getCurrentItem() == 0 || mViewPager.getCurrentItem() == 2); + mViewPager.setCurrentItem(1); return true; case R.id.home_message: - //mViewPager.setCurrentItem(2); - //mViewPager.setCurrentItem(2, false); - mViewPager.setCurrentItem(2, mViewPager.getCurrentItem() == 1 || mViewPager.getCurrentItem() == 3); + mViewPager.setCurrentItem(2); return true; case R.id.home_me: - //mViewPager.setCurrentItem(3); - //mViewPager.setCurrentItem(3, false); - mViewPager.setCurrentItem(3, mViewPager.getCurrentItem() == 2); + mViewPager.setCurrentItem(3); return true; + default: + break; } return false; } + /** + * {@link KeyboardWatcher.SoftKeyboardStateListener} + */ + @Override + public void onSoftKeyboardOpened(int keyboardHeight) { + mBottomNavigationView.setVisibility(View.GONE); + } + + @Override + public void onSoftKeyboardClosed() { + mBottomNavigationView.setVisibility(View.VISIBLE); + } + @Override public boolean onKeyDown(int keyCode, KeyEvent event) { // 回调当前 Fragment 的 onKeyDown 方法 @@ -149,12 +160,12 @@ public void onBackPressed() { public void run() { // 进行内存优化,销毁掉所有的界面 ActivityStackManager.getInstance().finishAllActivities(); - // 销毁进程 - System.exit(0); + // 销毁进程(请注意:调用此 API 可能导致当前 Activity onDestroy 方法无法正常回调) + // System.exit(0); } }, 300); } else { - toast(getResources().getString(R.string.home_exit_hint)); + toast(R.string.home_exit_hint); } } @@ -165,10 +176,4 @@ protected void onDestroy() { mBottomNavigationView.setOnNavigationItemSelectedListener(null); super.onDestroy(); } - - @Override - public boolean isSupportSwipeBack() { - // 不使用侧滑功能 - 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 new file mode 100644 index 00000000..a9697126 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/ui/activity/ImageActivity.java @@ -0,0 +1,87 @@ +package com.hjq.demo.ui.activity; + +import android.content.Context; +import android.content.Intent; + +import androidx.viewpager.widget.ViewPager; + +import com.gyf.immersionbar.BarHide; +import com.hjq.demo.R; +import com.hjq.demo.common.MyActivity; +import com.hjq.demo.other.IntentKey; +import com.hjq.demo.ui.adapter.ImagePagerAdapter; +import com.rd.PageIndicatorView; + +import java.util.ArrayList; + +import butterknife.BindView; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/03/05 + * desc : 查看大图 + */ +public final class ImageActivity extends MyActivity { + + @BindView(R.id.vp_image_pager) + ViewPager mViewPager; + @BindView(R.id.pv_image_indicator) + PageIndicatorView mIndicatorView; + + public static void start(Context context, String url) { + ArrayList images = new ArrayList<>(1); + images.add(url); + start(context, images); + } + + public static void start(Context context, ArrayList urls) { + start(context, urls, 0); + } + + public static void start(Context context, ArrayList urls, int index) { + Intent intent = new Intent(context, ImageActivity.class); + intent.putExtra(IntentKey.PICTURE, urls); + intent.putExtra(IntentKey.INDEX, index); + context.startActivity(intent); + } + + @Override + protected int getLayoutId() { + return R.layout.activity_image; + } + + @Override + protected void initView() { + // 设置状态栏和导航栏参数 + getStatusBarConfig() + // 有导航栏的情况下,activity全屏显示,也就是activity最下面被导航栏覆盖,不写默认非全屏 + .fullScreen(true) + // 隐藏状态栏 + .hideBar(BarHide.FLAG_HIDE_STATUS_BAR) + // 透明导航栏,不写默认黑色(设置此方法,fullScreen()方法自动为true) + .transparentNavigationBar() + .init(); + + mIndicatorView.setViewPager(mViewPager); + } + + @Override + protected void initData() { + ArrayList images = getIntent().getStringArrayListExtra(IntentKey.PICTURE); + int index = getIntent().getIntExtra(IntentKey.INDEX, 0); + if (images != null && images.size() > 0) { + mViewPager.setAdapter(new ImagePagerAdapter(this, images)); + if (index != 0 && index <= images.size()) { + mViewPager.setCurrentItem(index); + } + } else { + finish(); + } + } + + @Override + public boolean statusBarDarkFont() { + 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 07270c05..5c2367ff 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 @@ -1,15 +1,23 @@ package com.hjq.demo.ui.activity; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.content.Intent; -import androidx.annotation.Nullable; import android.view.View; +import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; +import android.widget.LinearLayout; + +import androidx.annotation.Nullable; import com.hjq.demo.R; import com.hjq.demo.common.MyActivity; import com.hjq.demo.helper.InputTextHelper; +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.umeng.Platform; import com.hjq.umeng.UmengClient; @@ -25,10 +33,14 @@ * desc : 登录界面 */ public final class LoginActivity extends MyActivity - implements UmengLogin.OnLoginListener { + implements UmengLogin.OnLoginListener, + KeyboardWatcher.SoftKeyboardStateListener { @BindView(R.id.iv_login_logo) ImageView mLogoView; + + @BindView(R.id.ll_login_body) + LinearLayout mBodyLayout; @BindView(R.id.et_login_phone) EditText mPhoneView; @BindView(R.id.et_login_password) @@ -37,51 +49,92 @@ public final class LoginActivity extends MyActivity @BindView(R.id.btn_login_commit) Button mCommitView; + @BindView(R.id.v_login_blank) + View mBlankView; + + @BindView(R.id.ll_login_other) + View mOtherView; + @BindView(R.id.iv_login_qq) + View mQQView; + @BindView(R.id.iv_login_wx) + View mWeChatView; + + /** logo 缩放比例 */ + private final float mLogoScale = 0.8f; + /** 动画时间 */ + private final int mAnimTime = 300; + @Override protected int getLayoutId() { return R.layout.activity_login; } - @Override - protected int getTitleId() { - return R.id.tb_login_title; - } - @Override protected void initView() { - new InputTextHelper.Builder(this) - .setMain(mCommitView) + InputTextHelper.with(this) .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; + } + }) .build(); + + post(new Runnable() { + + @Override + public void run() { + // 因为在小屏幕手机上面,因为计算规则的因素会导致动画效果特别夸张,所以不在小屏幕手机上面展示这个动画效果 + if (mBlankView.getHeight() > mBodyLayout.getHeight()) { + // 只有空白区域的高度大于登录框区域的高度才展示动画 + KeyboardWatcher.with(LoginActivity.this) + .setListener(LoginActivity.this); + } + } + }); } @Override protected void initData() { + // 判断用户当前有没有安装 QQ + if (!UmengClient.isAppInstalled(this, Platform.QQ)) { + mQQView.setVisibility(View.GONE); + } + + // 判断用户当前有没有安装微信 + if (!UmengClient.isAppInstalled(this, Platform.WECHAT)) { + mWeChatView.setVisibility(View.GONE); + } + // 如果这两个都没有安装就隐藏提示 + if (mQQView.getVisibility() == View.GONE && mWeChatView.getVisibility() == View.GONE) { + mOtherView.setVisibility(View.GONE); + } } @Override public void onRightClick(View v) { // 跳转到注册界面 - startActivity(RegisterActivity.class); - -// startActivityForResult(new Intent(this, RegisterActivity.class), new ActivityCallback() { -// -// @Override -// public void onActivityResult(int resultCode, @Nullable Intent data) { -// toast(String.valueOf(resultCode)); -// } -// }); - } + startActivityForResult(RegisterActivity.class, new ActivityCallback() { - @Override - public boolean isSupportSwipeBack() { - // 不使用侧滑功能 - return false; + @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); + } + } + }); } - @OnClick({R.id.tv_login_forget, R.id.btn_login_commit, R.id.iv_login_qq, R.id.iv_login_wx, R.id.iv_login_wb}) + @OnClick({R.id.tv_login_forget, R.id.btn_login_commit, R.id.iv_login_qq, R.id.iv_login_wx}) public void onClick(View v) { switch (v.getId()) { case R.id.tv_login_forget: @@ -89,25 +142,30 @@ public void onClick(View v) { break; case R.id.btn_login_commit: if (mPhoneView.getText().toString().length() != 11) { - toast(getString(R.string.common_phone_input_error)); + toast(R.string.common_phone_input_error); } else { - // 处理登录 - startActivityFinish(HomeActivity.class); + showLoading(); + postDelayed(new Runnable() { + @Override + public void run() { + showComplete(); + // 处理登录 + startActivityFinish(HomeActivity.class); + } + }, 2000); } break; case R.id.iv_login_qq: case R.id.iv_login_wx: - case R.id.iv_login_wb: + toast("记得改好第三方 AppID 和 AppKey,否则会调不起来哦"); Platform platform; switch (v.getId()) { case R.id.iv_login_qq: platform = Platform.QQ; break; case R.id.iv_login_wx: - platform = Platform.WEIXIN; - break; - case R.id.iv_login_wb: - platform = Platform.SINA; + platform = Platform.WECHAT; + toast("也别忘了改微信 " + WXEntryActivity.class.getSimpleName() + " 类所在的包名哦"); break; default: throw new IllegalStateException("are you ok?"); @@ -122,7 +180,7 @@ public void onClick(View v) { @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); - // 回调友盟第三方登录 SDK 的接口 + // 友盟登录回调 UmengClient.onActivityResult(this, requestCode, resultCode, data); } @@ -141,17 +199,18 @@ protected void onActivityResult(int requestCode, int resultCode, @Nullable Inten public void onSucceed(Platform platform, UmengLogin.LoginData data) { // 判断第三方登录的平台 switch (platform) { - case QQ: // QQ + case QQ: break; - case WEIXIN: // 微信 - break; - case SINA: // 新浪 + case WECHAT: break; default: break; } - ImageLoader.loadCircleImage(mLogoView, data.getIcon()); + ImageLoader.with(this) + .load(data.getIcon()) + .circle() + .into(mLogoView); toast("昵称:" + data.getName() + "\n" + "性别:" + data.getSex()); toast("id:" + data.getId()); @@ -178,4 +237,60 @@ public void onError(Platform platform, Throwable t) { public void onCancel(Platform platform) { toast("取消第三方登录"); } + + /** + * {@link KeyboardWatcher.SoftKeyboardStateListener} + */ + + @Override + public void onSoftKeyboardOpened(int keyboardHeight) { + int screenHeight = getResources().getDisplayMetrics().heightPixels; + int[] location = new int[2]; + // 获取这个 View 在屏幕中的坐标(左上角) + mBodyLayout.getLocationOnScreen(location); + //int x = location[0]; + int y = location[1]; + int bottom = screenHeight - (y + mBodyLayout.getHeight()); + if (keyboardHeight > bottom){ + // 执行位移动画 + ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mBodyLayout, "translationY", 0, -(keyboardHeight - bottom)); + objectAnimator.setDuration(mAnimTime); + objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); + objectAnimator.start(); + + // 执行缩小动画 + mLogoView.setPivotX(mLogoView.getWidth() / 2f); + mLogoView.setPivotY(mLogoView.getHeight()); + AnimatorSet animatorSet = new AnimatorSet(); + ObjectAnimator scaleX = ObjectAnimator.ofFloat(mLogoView, "scaleX", 1.0f, mLogoScale); + ObjectAnimator scaleY = ObjectAnimator.ofFloat(mLogoView, "scaleY", 1.0f, mLogoScale); + ObjectAnimator translationY = ObjectAnimator.ofFloat(mLogoView, "translationY", 0.0f, -(keyboardHeight - bottom)); + animatorSet.play(translationY).with(scaleX).with(scaleY); + animatorSet.setDuration(mAnimTime); + animatorSet.start(); + } + } + + @Override + public void onSoftKeyboardClosed() { + // 执行位移动画 + ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mBodyLayout, "translationY", mBodyLayout.getTranslationY(), 0); + objectAnimator.setDuration(mAnimTime); + objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); + objectAnimator.start(); + + if (mLogoView.getTranslationY() == 0){ + return; + } + // 执行放大动画 + mLogoView.setPivotX(mLogoView.getWidth() / 2f); + mLogoView.setPivotY(mLogoView.getHeight()); + AnimatorSet animatorSet = new AnimatorSet(); + ObjectAnimator scaleX = ObjectAnimator.ofFloat(mLogoView, "scaleX", mLogoScale, 1.0f); + ObjectAnimator scaleY = ObjectAnimator.ofFloat(mLogoView, "scaleY", mLogoScale, 1.0f); + ObjectAnimator translationY = ObjectAnimator.ofFloat(mLogoView, "translationY", mLogoView.getTranslationY(), 0); + animatorSet.play(translationY).with(scaleX).with(scaleY); + animatorSet.setDuration(mAnimTime); + animatorSet.start(); + } } \ 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 627549a9..fe2444c0 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 @@ -7,7 +7,7 @@ import com.hjq.demo.R; import com.hjq.demo.common.MyActivity; import com.hjq.demo.helper.InputTextHelper; -import com.hjq.widget.CountdownView; +import com.hjq.widget.view.CountdownView; import butterknife.BindView; import butterknife.OnClick; @@ -34,17 +34,19 @@ protected int getLayoutId() { return R.layout.activity_password_forget; } - @Override - protected int getTitleId() { - return R.id.tb_password_forget_title; - } - @Override protected void initView() { - new InputTextHelper.Builder(this) - .setMain(mCommitView) + InputTextHelper.with(this) .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; + } + }) .build(); } @@ -60,19 +62,15 @@ public void onClick(View v) { if (mPhoneView.getText().toString().length() != 11) { // 重置验证码倒计时控件 mCountdownView.resetState(); - toast(getString(R.string.common_phone_input_error)); + toast(R.string.common_phone_input_error); } else { // 获取验证码 - toast(getString(R.string.common_send_code_succeed)); + toast(R.string.common_code_send_hint); } break; case R.id.btn_password_forget_commit: - if (mPhoneView.getText().toString().length() != 11) { - toast(getString(R.string.common_phone_input_error)); - } else { - // 重置密码 - startActivityFinish(PasswordResetActivity.class); - } + // 重置密码 + startActivityFinish(PasswordResetActivity.class); 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 453aa476..2dd2f87d 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 @@ -31,17 +31,20 @@ protected int getLayoutId() { return R.layout.activity_password_reset; } - @Override - protected int getTitleId() { - return R.id.tb_password_reset_title; - } - @Override protected void initView() { - new InputTextHelper.Builder(this) - .setMain(mCommitView) + InputTextHelper.with(this) .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()); + } + }) .build(); } @@ -54,11 +57,10 @@ protected void initData() { public void onClick(View v) { switch (v.getId()) { case R.id.btn_password_reset_commit: - if (!mPasswordView1.getText().toString().equals(mPasswordView2.getText().toString())) { - toast(getString(R.string.password_reset_input_error)); - } else { - // 重置密码 - } + toast(R.string.password_reset_success); + finish(); + break; + default: break; } } 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 8e8f44cc..5f25dd11 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 @@ -1,14 +1,17 @@ package com.hjq.demo.ui.activity; -import android.app.Dialog; import android.view.View; import android.widget.ImageView; +import com.hjq.base.BaseDialog; import com.hjq.demo.R; import com.hjq.demo.common.MyActivity; -import com.hjq.dialog.AddressDialog; -import com.hjq.dialog.InputDialog; -import com.hjq.widget.SettingBar; +import com.hjq.demo.ui.dialog.AddressDialog; +import com.hjq.demo.ui.dialog.InputDialog; +import com.hjq.image.ImageLoader; +import com.hjq.widget.layout.SettingBar; + +import java.util.List; import butterknife.BindView; import butterknife.OnClick; @@ -21,8 +24,8 @@ */ public final class PersonalDataActivity extends MyActivity { - @BindView(R.id.iv_person_data_head) - ImageView mHeadView; + @BindView(R.id.iv_person_data_avatar) + ImageView mAvatarView; @BindView(R.id.sb_person_data_id) SettingBar mIDView; @BindView(R.id.sb_person_data_name) @@ -32,16 +35,13 @@ public final class PersonalDataActivity extends MyActivity { @BindView(R.id.sb_person_data_phone) SettingBar mPhoneView; + private String mAvatarUrl; + @Override protected int getLayoutId() { return R.layout.activity_personal_data; } - @Override - protected int getTitleId() { - return R.id.tb_personal_data_title; - } - @Override protected void initView() { @@ -52,51 +52,79 @@ protected void initData() { } - @OnClick({R.id.fl_person_data_head, R.id.sb_person_data_name, R.id.sb_person_data_address, R.id.sb_person_data_phone}) + @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}) public void onClick(View v) { switch (v.getId()) { + case R.id.iv_person_data_avatar: + if (mAvatarUrl != null && !"".equals(mAvatarUrl)) { + // 查看头像 + ImageActivity.start(getActivity(), mAvatarUrl); + } else { + // 选择头像 + onClick(findViewById(R.id.fl_person_data_head)); + } + break; case R.id.fl_person_data_head: - + PhotoActivity.start(getActivity(), new PhotoActivity.OnPhotoSelectListener() { + + @Override + public void onSelect(List data) { + mAvatarUrl = data.get(0); + ImageLoader.with(getActivity()) + .load(mAvatarUrl) + .into(mAvatarView); + } + + @Override + public void onCancel() {} + }); break; case R.id.sb_person_data_name: new InputDialog.Builder(this) - .setTitle(getResources().getString(R.string.personal_data_name_hint)) // 标题可以不用填写 + // 标题可以不用填写 + .setTitle(getString(R.string.personal_data_name_hint)) .setContent(mNameView.getRightText()) - //.setHint(getResources().getString(R.string.personal_data_name_hint)) + //.setHint(getString(R.string.personal_data_name_hint)) //.setConfirm("确定") - //.setCancel("取消") // 设置 null 表示不显示取消按钮 - //.setAutoDismiss(false) // 设置点击按钮后不关闭对话框 + // 设置 null 表示不显示取消按钮 + //.setCancel("取消") + // 设置点击按钮后不关闭对话框 + //.setAutoDismiss(false) .setListener(new InputDialog.OnListener() { @Override - public void onConfirm(Dialog dialog, String content) { + public void onConfirm(BaseDialog dialog, String content) { if (!mNameView.getRightText().equals(content)) { mNameView.setRightText(content); } } @Override - public void onCancel(Dialog dialog) {} + public void onCancel(BaseDialog dialog) {} }) .show(); break; case R.id.sb_person_data_address: new AddressDialog.Builder(this) //.setTitle("选择地区") - //.setProvince("广东省") // 设置默认省份 - //.setCity("广州市") // 设置默认城市(必须要先设置默认省份) - .setIgnoreArea() // 不选择县级区域 + // 设置默认省份 + .setProvince("广东省") + // 设置默认城市(必须要先设置默认省份) + .setCity("广州市") + // 不选择县级区域 + //.setIgnoreArea() .setListener(new AddressDialog.OnListener() { @Override - public void onSelected(Dialog dialog, String province, String city, String area) { - if (!mAddressView.getRightText().equals(province + city)) { - mAddressView.setRightText(province + city); + public void onSelected(BaseDialog dialog, String province, String city, String area) { + String address = province + city + area; + if (!mAddressView.getRightText().equals(address)) { + mAddressView.setRightText(address); } } @Override - public void onCancel(Dialog dialog) {} + 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 5c42064d..226eed81 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 @@ -7,7 +7,7 @@ import com.hjq.demo.R; import com.hjq.demo.common.MyActivity; import com.hjq.demo.helper.InputTextHelper; -import com.hjq.widget.CountdownView; +import com.hjq.widget.view.CountdownView; import butterknife.BindView; import butterknife.OnClick; @@ -36,17 +36,19 @@ protected int getLayoutId() { return R.layout.activity_phone_reset; } - @Override - protected int getTitleId() { - return R.id.tb_phone_reset_title; - } - @Override protected void initView() { - new InputTextHelper.Builder(this) - .setMain(mCommitView) + InputTextHelper.with(this) .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; + } + }) .build(); } @@ -59,23 +61,21 @@ protected void initData() { public void onClick(View v) { switch (v.getId()) { case R.id.cv_phone_reset_countdown: + // 获取验证码 if (mPhoneView.getText().toString().length() != 11) { // 重置验证码倒计时控件 mCountdownView.resetState(); - toast(getString(R.string.common_phone_input_error)); + toast(R.string.common_phone_input_error); } else { - // 获取验证码 - toast(getString(R.string.common_send_code_succeed)); + toast(R.string.common_code_send_hint); } break; case R.id.btn_phone_reset_commit: - if (mPhoneView.getText().toString().length() != 11) { - toast(getString(R.string.common_phone_input_error)); - } else { - // 更换手机号 - toast(getString(R.string.phone_reset_commit_succeed)); - finish(); - } + // 更换手机号 + 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 27b36809..0ae93306 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 @@ -8,7 +8,7 @@ import com.hjq.demo.R; import com.hjq.demo.common.MyActivity; import com.hjq.demo.helper.InputTextHelper; -import com.hjq.widget.CountdownView; +import com.hjq.widget.view.CountdownView; import butterknife.BindView; import butterknife.OnClick; @@ -36,22 +36,24 @@ protected int getLayoutId() { return R.layout.activity_phone_verify; } - @Override - protected int getTitleId() { - return R.id.tb_phone_verify_title; - } - @Override protected void initView() { - new InputTextHelper.Builder(this) - .setMain(mCommitView) + InputTextHelper.with(this) .addView(mCodeView) + .setMain(mCommitView) + .setListener(new InputTextHelper.OnInputTextListener() { + + @Override + public boolean onInputChange(InputTextHelper helper) { + return mCodeView.getText().toString().length() == 4; + } + }) .build(); } @Override protected void initData() { - mPhoneView.setText(String.format(getResources().getString(R.string.phone_verify_current_phone), "18888888888")); + 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}) @@ -59,7 +61,13 @@ public void onClick(View v) { switch (v.getId()) { case R.id.cv_phone_verify_countdown: // 获取验证码 - toast(getString(R.string.common_send_code_succeed)); + if (mPhoneView.getText().toString().length() != 11) { + // 重置验证码倒计时控件 + mCountdownView.resetState(); + toast(R.string.common_phone_input_error); + } else { + toast(R.string.common_code_send_hint); + } break; case R.id.btn_phone_verify_commit: // 修改手机号 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 ffe08742..8db2afd7 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 @@ -1,52 +1,528 @@ -package com.hjq.demo.ui.activity; - -import androidx.viewpager.widget.ViewPager; - -import com.hjq.demo.R; -import com.hjq.demo.common.MyActivity; -import com.hjq.demo.ui.adapter.PhotoPagerAdapter; - -import java.util.ArrayList; - -import butterknife.BindView; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2019/03/05 - * desc : 查看大图 - */ -public final class PhotoActivity extends MyActivity { - - @BindView(R.id.vp_photo_pager) - ViewPager mViewPager; - - @Override - protected int getLayoutId() { - return R.layout.activity_photo; - } - - @Override - protected int getTitleId() { - return 0; - } - - @Override - protected void initView() { - - } - - @Override - protected void initData() { - ArrayList data = new ArrayList<>(); - data.add("https://www.baidu.com/img/bd_logo.png"); - data.add("https://www.baidu.com/img/bd_logo.png"); - data.add("https://www.baidu.com/img/bd_logo.png"); - mViewPager.setAdapter(new PhotoPagerAdapter(this, data)); - } - - @Override - public boolean statusBarDarkFont() { - return !super.statusBarDarkFont(); - } +package com.hjq.demo.ui.activity; + +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.demo.R; +import com.hjq.demo.common.MyActivity; +import com.hjq.demo.other.AppConfig; +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.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 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/07/24 + * desc : 图片选择 + */ +public final class PhotoActivity extends MyActivity + implements BaseRecyclerViewAdapter.OnItemClickListener, + BaseRecyclerViewAdapter.OnItemLongClickListener, + BaseRecyclerViewAdapter.OnChildClickListener, Runnable { + + public static void start(BaseActivity activity, OnPhotoSelectListener listener) { + start(activity, 1, listener); + } + + public static void start(BaseActivity activity, int maxSelect, OnPhotoSelectListener listener) { + if (maxSelect < 1) { + // 最少要选择一个图片 + throw new IllegalArgumentException("are you ok?"); + } + Intent intent = new Intent(activity, PhotoActivity.class); + intent.putExtra(IntentKey.AMOUNT, maxSelect); + activity.startActivityForResult(intent, new ActivityCallback() { + + @Override + public void onActivityResult(int resultCode, @Nullable Intent data) { + if (listener == null || data == null) { + return; + } + + if (resultCode == RESULT_OK) { + listener.onSelect(data.getStringArrayListExtra(IntentKey.PICTURE)); + } else { + listener.onCancel(); + } + } + }); + } + + @BindView(R.id.rv_photo_list) + RecyclerView mRecyclerView; + @BindView(R.id.fab_photo_floating) + FloatingActionButton mFloatingView; + + private PhotoAdapter mAdapter; + + /** 最大选中 */ + private int mMaxSelect = 1; + /** 选中列表 */ + private final ArrayList mSelectPhoto = new ArrayList<>(); + + /** 全部图片 */ + private final ArrayList mAllPhoto = new ArrayList<>(); + /** 图片专辑 */ + private final HashMap> mAllAlbum = new HashMap<>(); + + @Override + protected int getLayoutId() { + return R.layout.activity_photo; + } + + @Override + protected void initView() { + mAdapter = new PhotoAdapter(this, mSelectPhoto); + mAdapter.setOnChildClickListener(R.id.fl_photo_check, this); + mAdapter.setOnItemClickListener(this); + mAdapter.setOnItemLongClickListener(this); + mRecyclerView.setAdapter(mAdapter); + // 禁用动画效果 + mRecyclerView.setItemAnimator(null); + // 添加分割线 + mRecyclerView.addItemDecoration(new PhotoSpaceDecoration((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics()))); + } + + @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(); + } + + @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); + } + } + }); + } + + @Override + public void onRightClick(View v) { + if (mAllPhoto.isEmpty()) { + return; + } + + ArrayList data = new ArrayList<>(mAllAlbum.size() + 1); + data.add(new AlbumDialog.AlbumBean(mAllPhoto.get(0), getString(R.string.photo_all), mAllPhoto.size(), mAdapter.getData() == mAllPhoto)); + Set keys = mAllAlbum.keySet(); + for (String key : keys) { + List temp = mAllAlbum.get(key); + if (temp != null && !temp.isEmpty()) { + data.add(new AlbumDialog.AlbumBean(temp.get(0), key, temp.size(), mAdapter.getData() == temp)); + } + } + + 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(); + } + }) + .show(); + } + + @Override + protected void onRestart() { + super.onRestart(); + // 遍历判断选择了的图片是否被删除了 + for (String path : mSelectPhoto) { + File file = new File(path); + if (!file.isFile()) { + + 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); + } + } + } + } + + @OnClick(R.id.fab_photo_floating) + 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); + } + } + }); + } else { + // 完成选择 + setResult(RESULT_OK, new Intent().putStringArrayListExtra(IntentKey.PICTURE, mSelectPhoto)); + finish(); + } + } + } + + /** + * {@link BaseRecyclerViewAdapter.OnItemClickListener} + * @param recyclerView RecyclerView对象 + * @param itemView 被点击的条目对象 + * @param position 被点击的条目位置 + */ + @Override + public void onItemClick(RecyclerView recyclerView, View itemView, int position) { + if (mSelectPhoto.contains(mAdapter.getItem(position))) { + ImageActivity.start(getActivity(), mSelectPhoto, mSelectPhoto.indexOf(mAdapter.getItem(position))); + } else { + ImageActivity.start(getActivity(), mAdapter.getItem(position)); + } + } + + /** + * {@link BaseRecyclerViewAdapter.OnItemLongClickListener} + * @param recyclerView RecyclerView对象 + * @param itemView 被点击的条目对象 + * @param position 被点击的条目位置 + */ + @Override + public boolean onItemLongClick(RecyclerView recyclerView, View itemView, int position) { + if (mSelectPhoto.size() < mMaxSelect) { + // 长按的时候模拟选中 + return itemView.findViewById(R.id.fl_photo_check).performClick(); + } else { + return false; + } + } + + /** + * {@link BaseRecyclerViewAdapter.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 (mSelectPhoto.isEmpty()) { + mFloatingView.hide(); + postDelayed(new Runnable() { + @Override + public void run() { + mFloatingView.setImageResource(R.drawable.ic_photo_camera); + mFloatingView.show(); + } + }, 200); + } + + } 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); + } + } else { + toast(String.format(getString(R.string.photo_max_hint), mMaxSelect)); + } + } + mAdapter.notifyItemChanged(position); + break; + default: + break; + } + } + + @Override + public void run() { + mAllAlbum.clear(); + mAllPhoto.clear(); + + final Uri contentUri = MediaStore.Files.getContentUri("external"); + final String sortOrder = MediaStore.Files.FileColumns.DATE_MODIFIED + " DESC"; + final String selection = + "(" + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?" + + " OR " + + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?)" + + " AND " + + MediaStore.MediaColumns.SIZE + ">0"; + + final String[] selectionAllArgs = {String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE)}; + + 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}; + } + + Cursor cursor = contentResolver.query(contentUri, projections, selection, selectionAllArgs, sortOrder); + if (cursor != null && cursor.moveToFirst()) { + + 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); + } + + do { + long size = cursor.getLong(sizeIndex); + if (size < 1) { + continue; + } + + String type = cursor.getString(mimeTypeIndex); + String path = cursor.getString(pathIndex); + if (TextUtils.isEmpty(path) || TextUtils.isEmpty(type)) { + 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; + } + } + + File file = new File(path); + if (!file.exists() || !file.isFile()) { + continue; + } + + // 专辑名称 + String albumName = file.getParentFile().getName(); + List files = mAllAlbum.get(albumName); + if (files == null) { + files = new ArrayList<>(); + mAllAlbum.put(albumName, files); + } + files.add(path); + mAllPhoto.add(path); + + } while (cursor.moveToNext()); + + cursor.close(); + } + + postDelayed(new Runnable() { + + @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); + } + + // 执行列表动画 + mRecyclerView.setLayoutAnimation(AnimationUtils.loadLayoutAnimation(getActivity(), R.anim.layout_animation_fall_down)); + mRecyclerView.scheduleLayoutAnimation(); + + // 显示加载完成 + 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; + } + } + + /** + * 图片选择监听 + */ + public interface OnPhotoSelectListener { + /** + * 选择结果回调 + * @param data 图片列表 + */ + void onSelect(List data); + + /** + * 取消回调 + */ + void onCancel(); + } } \ No newline at end of file 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 d5a41072..d86fcaf5 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,14 +1,16 @@ package com.hjq.demo.ui.activity; +import android.content.Intent; import android.view.View; import android.widget.Button; import android.widget.EditText; -import com.gyf.barlibrary.ImmersionBar; +import com.gyf.immersionbar.ImmersionBar; import com.hjq.demo.R; import com.hjq.demo.common.MyActivity; import com.hjq.demo.helper.InputTextHelper; -import com.hjq.widget.CountdownView; +import com.hjq.demo.other.IntentKey; +import com.hjq.widget.view.CountdownView; import butterknife.BindView; import butterknife.OnClick; @@ -42,30 +44,29 @@ protected int getLayoutId() { return R.layout.activity_register; } - @Override - protected int getTitleId() { - return R.id.tb_register_title; - } - @Override protected void initView() { - new InputTextHelper.Builder(this) - .setMain(mCommitView) + 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()); + } + }) .build(); } @Override protected void initData() { -// getHandler().postDelayed(new Runnable() { -// @Override -// public void run() { -// finishResult(RESULT_OK); -// } -// }, 2000); + } @Override @@ -77,27 +78,23 @@ protected ImmersionBar statusBarConfig() { @OnClick({R.id.cv_register_countdown, R.id.btn_register_commit}) public void onClick(View v) { switch (v.getId()) { - case R.id.cv_register_countdown: //获取验证码 - + case R.id.cv_register_countdown: + // 获取验证码 if (mPhoneView.getText().toString().length() != 11) { // 重置验证码倒计时控件 mCountdownView.resetState(); - toast(getString(R.string.common_phone_input_error)); + toast(R.string.common_phone_input_error); } else { - // 获取验证码 - toast(getString(R.string.common_send_code_succeed)); + toast(R.string.common_code_send_hint); } - break; - case R.id.btn_register_commit: //提交注册 - - if (mPhoneView.getText().toString().length() != 11) { - toast(getString(R.string.common_phone_input_error)); - } else if (!mPasswordView1.getText().toString().equals(mPasswordView2.getText().toString())) { - toast(getString(R.string.register_password_input_error)); - } else { - startActivity(LoginActivity.class); - } + case R.id.btn_register_commit: + // 提交注册 + Intent intent = new Intent(); + intent.putExtra(IntentKey.PHONE, mPhoneView.getText().toString()); + intent.putExtra(IntentKey.PASSWORD, mPasswordView1.getText().toString()); + setResult(RESULT_OK, intent); + finish(); break; default: break; 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 a3a5a7ff..d90a4a37 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 @@ -1,14 +1,19 @@ package com.hjq.demo.ui.activity; +import android.view.Gravity; import android.view.View; +import com.hjq.base.BaseDialog; import com.hjq.demo.R; import com.hjq.demo.common.MyActivity; import com.hjq.demo.helper.ActivityStackManager; import com.hjq.demo.helper.CacheDataManager; +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.widget.SettingBar; -import com.hjq.widget.SwitchButton; +import com.hjq.widget.layout.SettingBar; +import com.hjq.widget.view.SwitchButton; import butterknife.BindView; import butterknife.OnClick; @@ -35,11 +40,6 @@ protected int getLayoutId() { return R.layout.activity_setting; } - @Override - protected int getTitleId() { - return R.id.tb_setting_title; - } - @Override protected void initView() { // 设置切换按钮的监听 @@ -57,25 +57,64 @@ protected void initData() { public void onClick(View v) { switch (v.getId()) { case R.id.sb_setting_language: + // 底部选择框 + new MenuDialog.Builder(this) + // 设置点击按钮后不关闭对话框 + //.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) {} + }) + .setGravity(Gravity.BOTTOM) + .setAnimStyle(BaseDialog.AnimStyle.BOTTOM) + .show(); break; case R.id.sb_setting_update: + // 本地的版本码和服务器的进行比较 + if (20 > AppConfig.getVersionCode()) { + + new UpdateDialog.Builder(this) + // 版本名 + .setVersionName("v 2.0") + // 文件大小 + .setFileSize("10 M") + // 是否强制更新 + .setForceUpdate(false) + // 更新日志 + .setUpdateLog("到底更新了啥\n到底更新了啥\n到底更新了啥\n到底更新了啥\n到底更新了啥") + // 下载 url + .setDownloadUrl("https://raw.githubusercontent.com/getActivity/AndroidProject/master/AndroidProject.apk") + .show(); + } else { + toast(R.string.update_no_update); + } break; case R.id.sb_setting_agreement: - startActivity(WebActivity.class); + WebActivity.start(this, "https://github.com/getActivity/Donate"); break; case R.id.sb_setting_about: startActivity(AboutActivity.class); break; - case R.id.sb_setting_auto: // 自动登录 + case R.id.sb_setting_auto: + // 自动登录 mAutoSwitchView.setChecked(!mAutoSwitchView.isChecked()); break; - case R.id.sb_setting_cache: // 清空缓存 + case R.id.sb_setting_cache: + // 清空缓存 ImageLoader.clear(this); CacheDataManager.clearAllCache(this); // 重新获取应用缓存大小 mCleanCacheView.setRightText(CacheDataManager.getTotalCacheSize(this)); break; - case R.id.sb_setting_exit: // 退出登录 + case R.id.sb_setting_exit: + // 退出登录 startActivity(LoginActivity.class); // 进行内存优化,销毁掉所有的界面 ActivityStackManager.getInstance().finishAllActivities(LoginActivity.class); diff --git a/app/src/main/java/com/hjq/demo/ui/activity/LauncherActivity.java b/app/src/main/java/com/hjq/demo/ui/activity/SplashActivity.java similarity index 56% rename from app/src/main/java/com/hjq/demo/ui/activity/LauncherActivity.java rename to app/src/main/java/com/hjq/demo/ui/activity/SplashActivity.java index 4680a303..e6acaea0 100644 --- a/app/src/main/java/com/hjq/demo/ui/activity/LauncherActivity.java +++ b/app/src/main/java/com/hjq/demo/ui/activity/SplashActivity.java @@ -6,9 +6,10 @@ import android.view.animation.RotateAnimation; import android.view.animation.ScaleAnimation; -import com.gyf.barlibrary.BarHide; +import com.gyf.immersionbar.BarHide; import com.hjq.demo.R; import com.hjq.demo.common.MyActivity; +import com.hjq.demo.other.AppConfig; import com.hjq.permissions.OnPermission; import com.hjq.permissions.Permission; import com.hjq.permissions.XXPermissions; @@ -21,52 +22,31 @@ * author : Android 轮子哥 * github : https://github.com/getActivity/AndroidProject * time : 2018/10/18 - * desc : 启动界面 + * desc : 闪屏界面 */ -public final class LauncherActivity extends MyActivity +public final class SplashActivity extends MyActivity implements OnPermission, Animation.AnimationListener { - @BindView(R.id.iv_launcher_bg) + private static final int ANIM_TIME = 1000; + + @BindView(R.id.iv_splash_bg) View mImageView; - @BindView(R.id.iv_launcher_icon) + @BindView(R.id.iv_splash_icon) View mIconView; - @BindView(R.id.iv_launcher_name) - View mTextView; + @BindView(R.id.iv_splash_name) + View mNameView; - @Override - protected int getLayoutId() { - return R.layout.activity_launcher; - } + @BindView(R.id.tv_splash_debug) + View mDebugView; @Override - protected int getTitleId() { - return 0; + protected int getLayoutId() { + return R.layout.activity_splash; } @Override protected void initView() { - //初始化动画 - initStartAnim(); - //设置状态栏和导航栏参数 - getStatusBarConfig() - .fullScreen(true)//有导航栏的情况下,activity全屏显示,也就是activity最下面被导航栏覆盖,不写默认非全屏 - .hideBar(BarHide.FLAG_HIDE_STATUS_BAR)//隐藏状态栏 - .transparentNavigationBar()//透明导航栏,不写默认黑色(设置此方法,fullScreen()方法自动为true) - .init(); - } - - @Override - protected void initData() { - - } - - private static final int ANIM_TIME = 1000; - - /** - * 启动动画 - */ - private void initStartAnim() { - // 渐变展示启动屏 + // 初始化动画 AlphaAnimation aa = new AlphaAnimation(0.4f, 1.0f); aa.setDuration(ANIM_TIME * 2); aa.setAnimationListener(this); @@ -78,7 +58,26 @@ private void initStartAnim() { RotateAnimation ra = new RotateAnimation(180, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); ra.setDuration(ANIM_TIME); - mTextView.startAnimation(ra); + mNameView.startAnimation(ra); + + // 设置状态栏和导航栏参数 + getStatusBarConfig() + // 有导航栏的情况下,activity全屏显示,也就是activity最下面被导航栏覆盖,不写默认非全屏 + .fullScreen(true) + // 隐藏状态栏 + .hideBar(BarHide.FLAG_HIDE_STATUS_BAR) + // 透明导航栏,不写默认黑色(设置此方法,fullScreen()方法自动为true) + .transparentNavigationBar() + .init(); + } + + @Override + protected void initData() { + if (AppConfig.isDebug()) { + mDebugView.setVisibility(View.VISIBLE); + } else { + mDebugView.setVisibility(View.INVISIBLE); + } } private void requestPermission() { @@ -93,23 +92,17 @@ private void requestPermission() { @Override public void hasPermission(List granted, boolean isAll) { - startActivity(HomeActivity.class); - finish(); + startActivityFinish(HomeActivity.class); } @Override public void noPermission(List denied, boolean quick) { if (quick) { - toast("没有权限访问文件,请手动授予权限"); - XXPermissions.gotoPermissionSettings(LauncherActivity.this, true); + toast(R.string.common_permission_fail); + XXPermissions.gotoPermissionSettings(SplashActivity.this, true); } else { - toast("请先授予文件读写权限"); - postDelayed(new Runnable() { - @Override - public void run() { - requestPermission(); - } - }, 1000); + toast(R.string.common_permission_hint); + postDelayed(this::requestPermission, 1000); } } @@ -122,19 +115,13 @@ public void onBackPressed() { @Override protected void onRestart() { super.onRestart(); - if (XXPermissions.isHasPermission(LauncherActivity.this, Permission.Group.STORAGE)) { + if (XXPermissions.isHasPermission(SplashActivity.this, Permission.Group.STORAGE)) { hasPermission(null, true); - }else { + } else { requestPermission(); } } - @Override - public boolean isSupportSwipeBack() { - //不使用侧滑功能 - return false; - } - /** * {@link Animation.AnimationListener} */ @@ -149,14 +136,4 @@ public void onAnimationEnd(Animation animation) { @Override public void onAnimationRepeat(Animation animation) {} - - /** - * Android 8.0踩坑记录:Only fullscreen opaque activities can request orientation - * https://www.jianshu.com/p/d0d907754603 - */ - @Override - protected void initOrientation() { - // 注释父类的固定屏幕方向方法 - // super.initOrientation(); - } } \ 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 5010ffd5..b9e95e4d 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,10 +1,11 @@ package com.hjq.demo.ui.activity; -import android.app.Dialog; +import androidx.core.content.ContextCompat; +import com.hjq.base.BaseDialog; import com.hjq.demo.R; import com.hjq.demo.common.MyActivity; -import com.hjq.dialog.MenuDialog; +import com.hjq.demo.ui.dialog.MenuDialog; /** * author : Android 轮子哥 @@ -19,11 +20,6 @@ protected int getLayoutId() { return R.layout.activity_status; } - @Override - protected int getTitleId() { - return R.id.tb_status_title; - } - @Override protected void initView() { @@ -31,42 +27,41 @@ protected void initView() { @Override protected void initData() { - showLoading(); - postDelayed(new Runnable() { - @Override - public void run() { - - new MenuDialog.Builder(StatusActivity.this) - .setCancelable(false) - //.setAutoDismiss(false) // 设置点击按钮后不关闭对话框 - .setList("请求错误", "空数据提示", "自定义提示") - .setListener(new MenuDialog.OnListener() { - - @Override - public void onSelected(Dialog dialog, int position, String text) { - switch (position) { - case 0: - showError(); - break; - case 1: - showEmpty(); - break; - case 2: - showLayout(getResources().getDrawable(R.mipmap.icon_hint_address), "还没有添加地址"); - break; - default: - break; - } - } + new MenuDialog.Builder(this) + .setCancelable(false) + //.setAutoDismiss(false) // 设置点击按钮后不关闭对话框 + .setList("加载中", "请求错误", "空数据提示", "自定义提示") + .setListener(new MenuDialog.OnListener() { - @Override - public void onCancel(Dialog dialog) { - showComplete(); - } - }) - .show(); + @Override + public void onSelected(BaseDialog dialog, int position, Object object) { + switch (position) { + case 0: + 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; + } + } - } - }, 3000); + @Override + public void onCancel(BaseDialog dialog) {} + }) + .show(); } } \ 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 index 5120c2fa..0d204d94 100644 --- a/app/src/main/java/com/hjq/demo/ui/activity/WebActivity.java +++ b/app/src/main/java/com/hjq/demo/ui/activity/WebActivity.java @@ -1,6 +1,9 @@ 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; @@ -9,6 +12,8 @@ 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; @@ -17,6 +22,7 @@ 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; @@ -28,6 +34,15 @@ */ 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) @@ -38,11 +53,6 @@ protected int getLayoutId() { return R.layout.activity_web; } - @Override - protected int getTitleId() { - return R.id.tb_web_title; - } - @SuppressLint("SetJavaScriptEnabled") @Override protected void initView() { @@ -53,25 +63,23 @@ protected void initView() { WebSettings settings = mWebView.getSettings(); // 允许文件访问 settings.setAllowFileAccess(true); - // 支持javaScript - settings.setJavaScriptEnabled(true); // 允许网页定位 settings.setGeolocationEnabled(true); // 允许保存密码 settings.setSavePassword(true); + // 开启 JavaScript + settings.setJavaScriptEnabled(true); + // 允许网页弹对话框 + settings.setJavaScriptCanOpenWindowsAutomatically(true); + // 加快网页加载完成的速度,等页面完成再加载图片 + settings.setLoadsImagesAutomatically(true); + // 本地 DOM 存储(解决加载某些网页出现白板现象) + settings.setDomStorageEnabled(true); - // 解决Android 5.0上WebView默认不允许加载Http与Https混合内容 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - //两者都可以 + // 解决 Android 5.0 上 WebView 默认不允许加载 Http 与 Https 混合内容 settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); } - - // 加快HTML网页加载完成的速度,等页面finish再加载图片 - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - settings.setLoadsImagesAutomatically(true); - } else { - settings.setLoadsImagesAutomatically(false); - } } @Override @@ -79,7 +87,7 @@ protected void initData() { mWebView.setWebViewClient(new MyWebViewClient()); mWebView.setWebChromeClient(new MyWebChromeClient()); - String url = "https://github.com/getActivity/"; + String url = getIntent().getStringExtra(IntentKey.URL); mWebView.loadUrl(url); } @@ -118,19 +126,39 @@ protected void onDestroy() { private class MyWebViewClient extends WebViewClient { - // 网页加载错误时回调,这个方法会在 onPageFinished 之前调用 + /** + * 同名 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, final String failingUrl) { - showError(); + 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); @@ -139,16 +167,28 @@ public void onPageFinished(WebView view, String url) { @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { - //super.onReceivedSslError(view, handler, error);注意一定要去除这行代码,否则设置无效。 - // handler.cancel();// Android默认的处理方式 - handler.proceed();// 接受所有网站的证书 - // handleMessage(Message msg);// 进行其他处理 + // 注意一定要去除这行代码,否则设置无效。 + //super.onReceivedSslError(view, handler, error); + // Android默认的处理方式 + //handler.cancel(); + // 接受所有网站的证书 + handler.proceed(); } - // 跳转到其他链接 + /** + * 同名 API 兼容 + */ + @TargetApi(Build.VERSION_CODES.N) @Override - public boolean shouldOverrideUrlLoading(WebView view, final String url) { + 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(); @@ -163,7 +203,9 @@ public boolean shouldOverrideUrlLoading(WebView view, final String url) { private class MyWebChromeClient extends WebChromeClient { - // 收到网页标题 + /** + * 收到网页标题 + */ @Override public void onReceivedTitle(WebView view, String title) { if (title != null) { @@ -171,7 +213,9 @@ public void onReceivedTitle(WebView view, String title) { } } - // 收到加载进度变化 + /** + * 收到加载进度变化 + */ @Override public void onProgressChanged(WebView view, int newProgress) { mProgressBar.setProgress(newProgress); 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 f062d0d3..07205dea 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 @@ -13,7 +13,7 @@ * time : 2018/11/05 * desc : 可进行拷贝的副本 */ -public final class CopyAdapter extends MyRecyclerViewAdapter { +public final class CopyAdapter extends MyRecyclerViewAdapter { public CopyAdapter(Context context) { super(context); @@ -26,19 +26,19 @@ public int getItemCount() { @NonNull @Override - public CopyAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - return new ViewHolder(parent, R.layout.item_copy); + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(); } - @Override - public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + final class ViewHolder extends MyRecyclerViewAdapter.ViewHolder { - } + ViewHolder() { + super(R.layout.item_copy); + } - final class ViewHolder extends MyRecyclerViewAdapter.ViewHolder { + @Override + public void onBindView(int position) { - ViewHolder(ViewGroup parent, int layoutId) { - super(parent, layoutId); } } } \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/ui/adapter/PhotoPagerAdapter.java b/app/src/main/java/com/hjq/demo/ui/adapter/ImagePagerAdapter.java similarity index 59% rename from app/src/main/java/com/hjq/demo/ui/adapter/PhotoPagerAdapter.java rename to app/src/main/java/com/hjq/demo/ui/adapter/ImagePagerAdapter.java index 689a505c..21c7dba8 100644 --- a/app/src/main/java/com/hjq/demo/ui/adapter/PhotoPagerAdapter.java +++ b/app/src/main/java/com/hjq/demo/ui/adapter/ImagePagerAdapter.java @@ -1,71 +1,67 @@ -package com.hjq.demo.ui.adapter; - -import android.app.Activity; -import androidx.annotation.NonNull; -import androidx.viewpager.widget.PagerAdapter; -import android.view.View; -import android.view.ViewGroup; - -import com.github.chrisbanes.photoview.PhotoView; -import com.hjq.image.ImageLoader; - -import java.util.List; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/AndroidProject - * time : 2019/03/05 - * desc : 图片加载适配器 - */ -public final class PhotoPagerAdapter extends PagerAdapter implements View.OnClickListener { - - private Activity mActivity; - private List mData; - - public PhotoPagerAdapter(Activity activity, List data) { - mActivity = activity; - mData = data; - } - - // 加载数量,自动回调 - @Override - public int getCount() { - return mData.size(); - } - - // 返回真表示不会重新创建,使用缓存加载。返回假则重新创建 - @Override - public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { - return view == object; - } - - /** - * 实例化条目 - * ViewPager预加载机制:最多保存3个page,超过的将需要被销毁掉 - * 由于最多3个page,所以不需要设置ViewHolder - * 用于将数据设置给ViewItem - */ - - @NonNull - @Override - public Object instantiateItem(@NonNull ViewGroup container, int position) { - PhotoView view = new PhotoView(mActivity); - view.setOnClickListener(this); - ImageLoader.loadImage(view, mData.get(position)); - // 将View添加到ViewPager - container.addView(view); - return view; - } - - // 销毁条目 - @Override - public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { - container.removeView((View) object); - } - - @Override - public void onClick(View v) { - // 点击退出当前的 Activity - mActivity.finish(); - } +package com.hjq.demo.ui.adapter; + +import android.app.Activity; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.viewpager.widget.PagerAdapter; + +import com.github.chrisbanes.photoview.PhotoView; +import com.hjq.image.ImageLoader; + +import java.util.List; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/03/05 + * desc : 图片加载适配器 + */ +public final class ImagePagerAdapter extends PagerAdapter + implements View.OnClickListener { + + private final Activity mActivity; + private final List mData; + + public ImagePagerAdapter(Activity activity, List data) { + mActivity = activity; + mData = data; + } + + @Override + public int getCount() { + return mData.size(); + } + + @Override + public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { + return view == object; + } + + @NonNull + @Override + public Object instantiateItem(@NonNull ViewGroup container, int position) { + PhotoView view = new PhotoView(mActivity); + view.setOnClickListener(this); + ImageLoader.with(container.getContext()) + .load(mData.get(position)) + .gif() + .into(view); + container.addView(view); + return view; + } + + @Override + public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { + container.removeView((View) object); + } + + @Override + public void onClick(View v) { + // 单击图片退出当前的 Activity + if (!mActivity.isFinishing()) { + mActivity.finish(); + } + } } \ No newline at end of file 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 new file mode 100644 index 00000000..da01c7ec --- /dev/null +++ b/app/src/main/java/com/hjq/demo/ui/adapter/PhotoAdapter.java @@ -0,0 +1,66 @@ +package com.hjq.demo.ui.adapter; + +import android.content.Context; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.ImageView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.hjq.demo.R; +import com.hjq.demo.common.MyRecyclerViewAdapter; +import com.hjq.image.ImageLoader; + +import java.util.List; + +import butterknife.BindView; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/07/24 + * desc : 图片选择适配器 + */ +public final class PhotoAdapter extends MyRecyclerViewAdapter { + + private final List mSelectPhoto; + + public PhotoAdapter(Context context, List data) { + super(context); + mSelectPhoto = data; + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(); + } + + final class ViewHolder extends MyRecyclerViewAdapter.ViewHolder { + + @BindView(R.id.iv_photo_image) + ImageView mImageView; + @BindView(R.id.iv_photo_check) + CheckBox mCheckBox; + + ViewHolder() { + super(R.layout.item_photo); + } + + @Override + public void onBindView(int position) { + ImageLoader.with(getContext()) + .load(getItem(position)) + .into(mImageView); + + mCheckBox.setChecked(mSelectPhoto.contains(getItem(position))); + } + } + + @Override + protected RecyclerView.LayoutManager getDefaultLayoutManager(Context context) { + return new GridLayoutManager(context, 3); + } +} \ No newline at end of file diff --git a/dialog/src/main/java/com/hjq/dialog/AddressDialog.java b/app/src/main/java/com/hjq/demo/ui/dialog/AddressDialog.java similarity index 63% rename from dialog/src/main/java/com/hjq/dialog/AddressDialog.java rename to app/src/main/java/com/hjq/demo/ui/dialog/AddressDialog.java index 3363f2db..27d86b7d 100644 --- a/dialog/src/main/java/com/hjq/dialog/AddressDialog.java +++ b/app/src/main/java/com/hjq/demo/ui/dialog/AddressDialog.java @@ -1,12 +1,7 @@ -package com.hjq.dialog; +package com.hjq.demo.ui.dialog; -import android.app.Dialog; import android.content.Context; import android.os.Build; -import androidx.annotation.NonNull; -import com.google.android.material.tabs.TabLayout; -import androidx.fragment.app.FragmentActivity; -import androidx.recyclerview.widget.RecyclerView; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.Gravity; @@ -16,9 +11,17 @@ import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; +import androidx.fragment.app.FragmentActivity; +import androidx.recyclerview.widget.RecyclerView; + +import com.google.android.material.tabs.TabLayout; import com.hjq.base.BaseDialog; -import com.hjq.base.BaseDialogFragment; import com.hjq.base.BaseRecyclerViewAdapter; +import com.hjq.demo.R; +import com.hjq.demo.common.MyDialogFragment; +import com.hjq.demo.common.MyRecyclerViewAdapter; import org.json.JSONArray; import org.json.JSONException; @@ -40,22 +43,22 @@ public final class AddressDialog { public static final class Builder - extends BaseDialogFragment.Builder + extends MyDialogFragment.Builder implements BaseRecyclerViewAdapter.OnItemClickListener, - View.OnClickListener, TabLayout.BaseOnTabSelectedListener, Runnable { + View.OnClickListener, TabLayout.OnTabSelectedListener, Runnable { - private TextView mTitleView; - private ImageView mCloseView; - private TabLayout mTabLayout; + private final TextView mTitleView; + private final ImageView mCloseView; + private final TabLayout mTabLayout; + private final ImageView mHintView; - private RecyclerView mRecyclerView1; - private RecyclerView mRecyclerView2; - private RecyclerView mRecyclerView3; - private ImageView mHintView; + private final RecyclerView mProvinceView; + private final RecyclerView mCityView; + private final RecyclerView mAreaView; - private AddressDialogAdapter mAdapter1; - private AddressDialogAdapter mAdapter2; - private AddressDialogAdapter mAdapter3; + private final AddressDialogAdapter mProvinceAdapter; + private final AddressDialogAdapter mCityAdapter; + private final AddressDialogAdapter mAreaAdapter; private OnListener mListener; @@ -67,49 +70,44 @@ public static final class Builder public Builder(FragmentActivity activity) { super(activity); - setContentView(R.layout.dialog_address); - setAnimStyle(BaseDialog.AnimStyle.LEFT); - setGravity(Gravity.BOTTOM); DisplayMetrics displayMetrics = new DisplayMetrics(); - ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(displayMetrics); - setWidth(MATCH_PARENT); + getSystemService(WindowManager.class).getDefaultDisplay().getMetrics(displayMetrics); setHeight(displayMetrics.heightPixels / 2); - mTitleView = findViewById(R.id.tv_dialog_address_title); - mCloseView = findViewById(R.id.iv_dialog_address_closer); - mTabLayout = findViewById(R.id.tb_dialog_address_tab); + 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); - mRecyclerView1 = findViewById(R.id.rv_dialog_address_list1); - mRecyclerView2 = findViewById(R.id.rv_dialog_address_list2); - mRecyclerView3 = findViewById(R.id.rv_dialog_address_list3); - mHintView = findViewById(R.id.iv_dialog_address_hint); + mProvinceView = findViewById(R.id.rv_address_province); + mCityView = findViewById(R.id.rv_address_city); + mAreaView = findViewById(R.id.rv_address_area); - mAdapter1 = new AddressDialogAdapter(getContext()); - mAdapter2 = new AddressDialogAdapter(getContext()); - mAdapter3 = new AddressDialogAdapter(getContext()); + mProvinceAdapter = new AddressDialogAdapter(getContext()); + mCityAdapter = new AddressDialogAdapter(getContext()); + mAreaAdapter = new AddressDialogAdapter(getContext()); mCloseView.setOnClickListener(this); - mAdapter1.setOnItemClickListener(this); - mAdapter2.setOnItemClickListener(this); - mAdapter3.setOnItemClickListener(this); + mProvinceAdapter.setOnItemClickListener(this); + mCityAdapter.setOnItemClickListener(this); + mAreaAdapter.setOnItemClickListener(this); - mRecyclerView1.setAdapter(mAdapter1); - mRecyclerView2.setAdapter(mAdapter2); - mRecyclerView3.setAdapter(mAdapter3); + mProvinceView.setAdapter(mProvinceAdapter); + mCityView.setAdapter(mCityAdapter); + mAreaView.setAdapter(mAreaAdapter); - mTabLayout.addTab(mTabLayout.newTab().setText(getText(R.string.dialog_select_hint)), true); + mTabLayout.addTab(mTabLayout.newTab().setText(getString(R.string.address_hint)), true); mTabLayout.addOnTabSelectedListener(this); // 显示省份列表 - mAdapter1.setData(ProvinceUtils.getProvinceList(getContext())); + mProvinceAdapter.setData(AddressManager.getProvinceList(getContext())); } - - public Builder setTitle(int resId) { - return setTitle(getText(resId)); + public Builder setTitle(@StringRes int id) { + return setTitle(getString(id)); } public Builder setTitle(CharSequence text) { mTitleView.setText(text); @@ -119,13 +117,14 @@ public Builder setTitle(CharSequence text) { /** * 设置默认省份 */ - public Builder setProvince(String name) { - if (name != null && !"".equals(name)) { - List data = mAdapter1.getData(); + public Builder setProvince(String province) { + if (province != null && !"".equals(province)) { + List data = mProvinceAdapter.getData(); if (data != null && !data.isEmpty()) { for (int i = 0; i < data.size(); i++) { - if (name.equals(data.get(i).getName())) { - onItemClick(mRecyclerView1, null, i); + if (province.equals(data.get(i).getName())) { + onItemClick(mProvinceView, null, i); + break; } } } @@ -136,17 +135,18 @@ public Builder setProvince(String name) { /** * 设置默认城市 */ - public Builder setCity(String name) { + public Builder setCity(String city) { if (mIgnoreArea) { // 已经忽略了县级区域的选择,不能选定指定的城市 throw new IllegalStateException("The selection of county-level regions has been ignored. The designated city cannot be selected"); } - if (name != null && !"".equals(name)) { - List data = mAdapter2.getData(); + if (city != null && !"".equals(city)) { + List data = mCityAdapter.getData(); if (data != null && !data.isEmpty()) { for (int i = 0; i < data.size(); i++) { - if (name.equals(data.get(i).getName())) { - onItemClick(mRecyclerView2, null, i); + if (city.equals(data.get(i).getName())) { + onItemClick(mCityView, null, i); + break; } } } @@ -158,7 +158,7 @@ public Builder setCity(String name) { * 不选择县级区域 */ public Builder setIgnoreArea() { - List data = mAdapter2.getData(); + List data = mCityAdapter.getData(); if (data != null && !data.isEmpty()) { // 已经指定了城市,不能再忽略县级区域 throw new IllegalStateException("Cities have been designated and county-level areas can no longer be ignored"); @@ -167,8 +167,8 @@ public Builder setIgnoreArea() { return this; } - public Builder setListener(OnListener l) { - mListener = l; + public Builder setListener(OnListener listener) { + mListener = listener; return this; } @@ -176,30 +176,31 @@ public Builder setListener(OnListener l) { * {@link BaseRecyclerViewAdapter.OnItemClickListener} */ + @SuppressWarnings("all") @Override public synchronized void onItemClick(RecyclerView recyclerView, View itemView, int position) { - if (recyclerView == mRecyclerView1) { + if (recyclerView == mProvinceView) { // 记录当前选择的省份 - mProvince = mAdapter1.getItem(position).getName(); + mProvince = mProvinceAdapter.getItem(position).getName(); mTabLayout.getTabAt(mTabLayout.getSelectedTabPosition()).setText(mProvince); - mTabLayout.addTab(mTabLayout.newTab().setText(getContext().getResources().getString(R.string.dialog_select_hint)), true); + mTabLayout.addTab(mTabLayout.newTab().setText(getString(R.string.address_hint)), true); - mAdapter2.setData(ProvinceUtils.getCityList(mAdapter1.getItem(position).getNext())); + mCityAdapter.setData(AddressManager.getCityList(mProvinceAdapter.getItem(position).getNext())); - mRecyclerView1.setVisibility(View.GONE); - mRecyclerView2.setVisibility(View.VISIBLE); + mProvinceView.setVisibility(View.GONE); + mCityView.setVisibility(View.VISIBLE); // 如果当前选择的是直辖市,就直接跳过选择城市,直接选择区域 - if (mAdapter2.getItemCount() == 1) { - onItemClick(mRecyclerView2, null, 0); + if (mCityAdapter.getItemCount() == 1) { + onItemClick(mCityView, null, 0); } - }else if (recyclerView == mRecyclerView2) { + } else if (recyclerView == mCityView) { // 记录当前选择的城市 - mCity = mAdapter2.getItem(position).getName(); + mCity = mCityAdapter.getItem(position).getName(); mTabLayout.getTabAt(mTabLayout.getSelectedTabPosition()).setText(mCity); @@ -213,26 +214,26 @@ public synchronized void onItemClick(RecyclerView recyclerView, View itemView, i postDelayed(this, 300); } else { - mTabLayout.addTab(mTabLayout.newTab().setText(getText(R.string.dialog_select_hint)), true); - mAdapter3.setData(ProvinceUtils.getAreaList(mAdapter2.getItem(position).getNext())); + mTabLayout.addTab(mTabLayout.newTab().setText(getString(R.string.address_hint)), true); + mAreaAdapter.setData(AddressManager.getAreaList(mCityAdapter.getItem(position).getNext())); } - mRecyclerView2.setVisibility(View.GONE); + mCityView.setVisibility(View.GONE); if (mIgnoreArea) { mHintView.setVisibility(View.VISIBLE); - }else { - mRecyclerView3.setVisibility(View.VISIBLE); + } else { + mAreaView.setVisibility(View.VISIBLE); } - }else if (recyclerView == mRecyclerView3) { + } else if (recyclerView == mAreaView) { // 记录当前选择的区域 - mArea = mAdapter3.getItem(position).getName(); + mArea = mAreaAdapter.getItem(position).getName(); mTabLayout.getTabAt(mTabLayout.getSelectedTabPosition()).setText(mArea); - mRecyclerView3.setVisibility(View.INVISIBLE); + mAreaView.setVisibility(View.INVISIBLE); mHintView.setVisibility(View.VISIBLE); if (mListener != null) { @@ -276,10 +277,10 @@ public void onClick(View v) { * {@link TabLayout.OnTabSelectedListener} */ - // Tab条目被选中 + /** Tab条目被选中 */ @Override public void onTabSelected(TabLayout.Tab tab) { - tab.setText(getText(R.string.dialog_select_hint)); + tab.setText(getString(R.string.address_hint)); switch (tab.getPosition()) { case 0: mProvince = mCity = mArea = null; @@ -289,48 +290,40 @@ public void onTabSelected(TabLayout.Tab tab) { if (mTabLayout.getTabAt(1) != null) { mTabLayout.removeTabAt(1); } - mRecyclerView1.setVisibility(View.VISIBLE); - mRecyclerView2.setVisibility(View.GONE); - mRecyclerView3.setVisibility(View.GONE); + 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); } - mRecyclerView1.setVisibility(View.GONE); - mRecyclerView2.setVisibility(View.VISIBLE); - mRecyclerView3.setVisibility(View.GONE); + mProvinceView.setVisibility(View.GONE); + mCityView.setVisibility(View.VISIBLE); + mAreaView.setVisibility(View.GONE); break; case 2: mArea = null; - mRecyclerView1.setVisibility(View.GONE); - mRecyclerView2.setVisibility(View.GONE); - mRecyclerView3.setVisibility(View.VISIBLE); + mProvinceView.setVisibility(View.GONE); + mCityView.setVisibility(View.GONE); + mAreaView.setVisibility(View.VISIBLE); break; default: break; } } - // Tab条目被取消选中 + /** Tab条目被取消选中 */ @Override public void onTabUnselected(TabLayout.Tab tab) {} - // Tab条目被重复点击 + /** Tab条目被重复点击 */ @Override public void onTabReselected(TabLayout.Tab tab) {} - -// @Override -// protected BaseDialog createDialog(Context context, int themeResId) { -// if (getGravity() == Gravity.BOTTOM) { -// return new BaseBottomDialog(context, themeResId); -// } -// return super.createDialog(context, themeResId); -// } } - private static final class AddressDialogAdapter extends BaseRecyclerViewAdapter { + private static final class AddressDialogAdapter extends MyRecyclerViewAdapter { private AddressDialogAdapter(Context context) { super(context); @@ -359,16 +352,28 @@ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) { return new ViewHolder(textView); } - @Override - public void onBindViewHolder(@NonNull BaseRecyclerViewAdapter.ViewHolder holder, int position) { - ((TextView) holder.itemView).setText(getItem(position).getName()); + final class ViewHolder extends MyRecyclerViewAdapter.ViewHolder { + + private final TextView mTextView; + + public ViewHolder(View itemView) { + super(itemView); + mTextView = (TextView) getItemView(); + } + + @Override + public void onBindView(int position) { + mTextView.setText(getItem(position).getName()); + } } } private static final class AddressBean { - private String name; // 省、市、区的名称 - private JSONObject next; // 下一级的 Json + /** (省\市\区)的名称 */ + private String name; + /** 下一级的 Json */ + private JSONObject next; private AddressBean(String name, JSONObject next) { this.name = name; @@ -385,9 +390,9 @@ private JSONObject getNext() { } /** - * 省市区读取工具类 + * 省市区数据管理类 */ - private static final class ProvinceUtils { + private static final class AddressManager { /** * 获取省列表 @@ -395,23 +400,24 @@ private static final class ProvinceUtils { private static List getProvinceList(Context context) { try { // 省市区Json数据文件来源:https://github.com/getActivity/ProvinceJson - JSONArray jsonArray = new JSONArray(getAssetsString(context, "province.json")); + JSONArray jsonArray = getProvinceJson(context); - int length = jsonArray.length(); + if (jsonArray != null) { - ArrayList list = new ArrayList<>(length); + int length = jsonArray.length(); + ArrayList list = new ArrayList<>(length); + for (int i = 0; i < length; i++) { + JSONObject jsonObject = jsonArray.getJSONObject(i); + list.add(new AddressBean(jsonObject.getString("name"), jsonObject)); + } - for (int i = 0; i < length; i++) { - JSONObject jsonObject = jsonArray.getJSONObject(i); - list.add(new AddressBean(jsonObject.getString("name"), jsonObject)); + return list; } - return list; - } catch (JSONException e) { e.printStackTrace(); - return null; } + return null; } /** @@ -463,9 +469,9 @@ private static List getAreaList(JSONObject jsonObject) { /** * 获取资产目录下面文件的字符串 */ - private static String getAssetsString(Context context, String file) { + private static JSONArray getProvinceJson(Context context) { try { - InputStream inputStream = context.getAssets().open(file); + InputStream inputStream = context.getAssets().open("province.json"); ByteArrayOutputStream outStream = new ByteArrayOutputStream(); byte[] buffer = new byte[512]; int length; @@ -474,11 +480,13 @@ private static String getAssetsString(Context context, String file) { } outStream.close(); inputStream.close(); - return outStream.toString(); + return new JSONArray(outStream.toString()); } catch (IOException e) { e.printStackTrace(); - return null; + } catch (JSONException e) { + e.printStackTrace(); } + return null; } } @@ -491,11 +499,11 @@ public interface OnListener { * @param city 市 * @param area 区 */ - void onSelected(Dialog dialog, String province, String city, String area); + void onSelected(BaseDialog dialog, String province, String city, String area); /** * 点击取消时回调 */ - void onCancel(Dialog dialog); + 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 new file mode 100644 index 00000000..7aad3c06 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/ui/dialog/AlbumDialog.java @@ -0,0 +1,194 @@ +package com.hjq.demo.ui.dialog; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.fragment.app.FragmentActivity; +import androidx.recyclerview.widget.RecyclerView; + +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 java.util.List; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/07/27 + * desc : 相册专辑选取对话框 + */ +public final class AlbumDialog { + + public static final class Builder + extends MyDialogFragment.Builder + implements BaseRecyclerViewAdapter.OnItemClickListener { + + private OnListener mListener; + + private final RecyclerView mRecyclerView; + private final AlbumAdapter mAdapter; + + public Builder(FragmentActivity activity) { + super(activity); + + setContentView(R.layout.dialog_album); + setHeight(getResources().getDisplayMetrics().heightPixels / 2); + + mRecyclerView = findViewById(R.id.rv_album_list); + mAdapter = new AlbumAdapter(activity); + mAdapter.setOnItemClickListener(this); + mRecyclerView.setAdapter(mAdapter); + } + + public Builder setData(List data) { + mAdapter.setData(data); + // 滚动到选中的位置 + for (int i = 0; i < data.size(); i++) { + if (data.get(i).isSelect()) { + mRecyclerView.scrollToPosition(i); + } + } + return this; + } + + public Builder setListener(OnListener listener) { + mListener = listener; + return this; + } + + @Override + public void onItemClick(RecyclerView recyclerView, View itemView, int position) { + List data = mAdapter.getData(); + if (data == null) { + return; + } + + for (AlbumBean bean : data) { + if (bean.isSelect()) { + bean.setSelect(false); + break; + } + } + mAdapter.getItem(position).setSelect(true); + mAdapter.notifyDataSetChanged(); + + // 延迟消失 + postDelayed(new Runnable() { + @Override + public void run() { + if (mListener != null) { + mListener.onSelected(getDialog(), position, mAdapter.getItem(position)); + } + + dismiss(); + } + }, 300); + } + } + + private static final class AlbumAdapter extends MyRecyclerViewAdapter { + + private AlbumAdapter(Context context) { + super(context); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(); + } + + final class ViewHolder extends MyRecyclerViewAdapter.ViewHolder { + + private final ImageView mIconView; + private final TextView mNameView; + private final TextView mCountView; + private final CheckBox mCheckBox; + + private ViewHolder() { + super(R.layout.item_album); + mIconView = (ImageView) findViewById(R.id.iv_album_icon); + mNameView = (TextView) findViewById(R.id.tv_album_name); + mCountView = (TextView) findViewById(R.id.tv_album_count); + mCheckBox = (CheckBox) findViewById(R.id.rb_album_check); + } + + @Override + public void onBindView(int position) { + AlbumBean bean = getItem(position); + + ImageLoader.with(getContext()) + .load(bean.getIcon()) + .into(mIconView); + + mNameView.setText(bean.getName()); + mCountView.setText(String.format(getString(R.string.photo_total), bean.getCount())); + mCheckBox.setChecked(bean.isSelect()); + mCheckBox.setVisibility(bean.isSelect() ? View.VISIBLE : View.INVISIBLE); + } + } + } + + /** + * 专辑信息类 + */ + public static class AlbumBean { + + /** 封面 */ + private String icon; + /** 名称 */ + private String name; + /** 数量 */ + private int count; + /** 选中 */ + private boolean select; + + public AlbumBean(String icon, String name, int count, boolean select) { + this.icon = icon; + this.name = name; + this.count = count; + this.select = select; + } + + public void setName(String name) { + this.name = name; + } + + public void setSelect(boolean select) { + this.select = select; + } + + public String getIcon() { + return icon; + } + + public String getName() { + return name; + } + + public int getCount() { + return count; + } + + public boolean isSelect() { + return select; + } + } + + public interface OnListener { + + /** + * 选择条目时回调 + */ + void onSelected(BaseDialog dialog, int position, AlbumBean bean); + } +} \ No newline at end of file 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 f6f8ca03..0a6d853d 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,11 +1,12 @@ package com.hjq.demo.ui.dialog; -import androidx.fragment.app.FragmentActivity; import android.view.Gravity; +import androidx.fragment.app.FragmentActivity; + import com.hjq.base.BaseDialog; -import com.hjq.base.BaseDialogFragment; import com.hjq.demo.R; +import com.hjq.demo.common.MyDialogFragment; /** * author : Android 轮子哥 @@ -16,15 +17,14 @@ public final class CopyDialog { public static final class Builder - extends BaseDialogFragment.Builder { + extends MyDialogFragment.Builder { public Builder(FragmentActivity activity) { super(activity); - setContentView(R.layout.item_copy); + setContentView(R.layout.dialog_copy); setAnimStyle(BaseDialog.AnimStyle.BOTTOM); setGravity(Gravity.BOTTOM); - } } } \ No newline at end of file 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 new file mode 100644 index 00000000..929c4bd0 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/ui/dialog/DateDialog.java @@ -0,0 +1,252 @@ +package com.hjq.demo.ui.dialog; + +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.widget.LoopView; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2018/12/17 + * desc : 日期选择对话框 + */ +public final class DateDialog { + + public static final class Builder + extends MyDialogFragment.Builder + implements LoopView.LoopScrollListener, + View.OnClickListener { + + private final int mStartYear = 1920; + private final int mEndYear = 2019; + + private final TextView mTitleView; + private final TextView mCancelView; + private final TextView mConfirmView; + + private final LoopView mYearView; + private final LoopView mMonthView; + private final LoopView mDayView; + + private OnListener mListener; + private boolean mAutoDismiss = true; + + @SuppressWarnings("all") + public Builder(FragmentActivity activity) { + super(activity); + setContentView(R.layout.dialog_date); + + 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.lv_date_year); + mMonthView = findViewById(R.id.lv_date_month); + mDayView = findViewById(R.id.lv_date_day); + + // 生产年份 + ArrayList yearData = new ArrayList<>(10); + for (int i = mStartYear; i <= mEndYear; i++) { + yearData.add(i + " " + getString(R.string.common_year)); + } + + // 生产月份 + ArrayList monthData = new ArrayList<>(12); + for (int i = 1; i <= 12; i++) { + monthData.add(i + " " + getString(R.string.common_month)); + } + + mYearView.setData(yearData); + mMonthView.setData(monthData); + + mYearView.setLoopListener(this); + mMonthView.setLoopListener(this); + + mCancelView.setOnClickListener(this); + mConfirmView.setOnClickListener(this); + + Calendar calendar = Calendar.getInstance(); + 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; + } + + public Builder setListener(OnListener listener) { + mListener = listener; + return this; + } + + public Builder setAutoDismiss(boolean dismiss) { + mAutoDismiss = dismiss; + return this; + } + + /** + * 不选择天数 + */ + public Builder setIgnoreDay() { + mDayView.setVisibility(View.GONE); + return this; + } + + public Builder setDate(long date) { + if (date > 0) { + setDate(new SimpleDateFormat("yyyyMMdd", Locale.getDefault()).format(new Date(date))); + } + return this; + } + + public Builder setDate(String date) { + // 20190519 + if (date.matches("\\d{8}")) { + setYear(date.substring(0, 4)); + setMonth(date.substring(4, 6)); + setDay(date.substring(6, 8)); + // 2019-05-19 + } else if (date.matches("\\d{4}-\\d{2}-\\d{2}")) { + setYear(date.substring(0, 4)); + setMonth(date.substring(5, 7)); + setDay(date.substring(8, 10)); + } + return this; + } + + public Builder setYear(String year) { + return setYear(Integer.valueOf(year)); + } + + public Builder setYear(int year) { + int index = year - mStartYear; + if (index < 0) { + index = 0; + } else if (index > mYearView.getSize() - 1) { + index = mYearView.getSize() - 1; + } + mYearView.setInitPosition(index); + return this; + } + + public Builder setMonth(String month) { + return setMonth(Integer.valueOf(month)); + } + + public Builder setMonth(int month) { + int index = month - 1; + if (index < 0) { + index = 0; + } else if (index > mMonthView.getSize() - 1) { + index = mMonthView.getSize() - 1; + } + mMonthView.setInitPosition(index); + return this; + } + + public Builder setDay(String day) { + return setDay(Integer.valueOf(day)); + } + + public Builder setDay(int day) { + int index = day - 1; + if (index < 0) { + index = 0; + } else if (index > mDayView.getSize() - 1) { + index = mDayView.getSize() - 1; + } + mDayView.setInitPosition(index); + return this; + } + + @Override + public void onItemSelect(LoopView loopView, 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); + } + + 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)); + } + + mDayView.setData(dayData); + } + + /** + * {@link View.OnClickListener} + */ + + @Override + public void onClick(View v) { + if (mAutoDismiss) { + dismiss(); + } + + 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()); + } + } + } + } + + public interface OnListener { + + /** + * 选择完日期后回调 + * + * @param year 年 + * @param month 月 + * @param day 日 + */ + void onSelected(BaseDialog dialog, int year, int month, int day); + + /** + * 点击取消时回调 + */ + void onCancel(BaseDialog dialog); + } +} \ No newline at end of file diff --git a/dialog/src/main/java/com/hjq/dialog/InputDialog.java b/app/src/main/java/com/hjq/demo/ui/dialog/InputDialog.java similarity index 52% rename from dialog/src/main/java/com/hjq/dialog/InputDialog.java rename to app/src/main/java/com/hjq/demo/ui/dialog/InputDialog.java index 579c4649..5d20a110 100644 --- a/dialog/src/main/java/com/hjq/dialog/InputDialog.java +++ b/app/src/main/java/com/hjq/demo/ui/dialog/InputDialog.java @@ -1,16 +1,16 @@ -package com.hjq.dialog; +package com.hjq.demo.ui.dialog; -import android.app.Dialog; -import android.content.Context; -import androidx.fragment.app.FragmentActivity; -import android.view.Gravity; 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.base.BaseDialogFragment; +import com.hjq.demo.R; +import com.hjq.demo.common.MyDialogFragment; /** * author : Android 轮子哥 @@ -21,61 +21,58 @@ public final class InputDialog { public static final class Builder - extends BaseDialogFragment.Builder + extends MyDialogFragment.Builder implements View.OnClickListener, BaseDialog.OnShowListener, BaseDialog.OnDismissListener { private OnListener mListener; - private boolean mAutoDismiss = true; // 设置点击按钮后自动消失 - - private TextView mTitleView; - private EditText mInputView; + private boolean mAutoDismiss = true; - private TextView mCancelView; - private View mLineView; - private TextView mConfirmView; + private final TextView mTitleView; + private final EditText mInputView; - private InputMethodManager mInputManager; + 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); - setGravity(Gravity.CENTER); - mTitleView = findViewById(R.id.tv_dialog_input_title); - mInputView = findViewById(R.id.tv_dialog_input_message); + mTitleView = findViewById(R.id.tv_input_title); + mInputView = findViewById(R.id.tv_input_message); - mCancelView = findViewById(R.id.tv_dialog_input_cancel); - mLineView = findViewById(R.id.v_dialog_input_line); - mConfirmView = findViewById(R.id.tv_dialog_input_confirm); + 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); - mInputManager = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); + addOnShowListener(this); + addOnDismissListener(this); } - public Builder setTitle(int resId) { - return setTitle(getText(resId)); + public Builder setTitle(@StringRes int id) { + return setTitle(getString(id)); } public Builder setTitle(CharSequence text) { mTitleView.setText(text); return this; } - public Builder setHint(int resId) { - return setHint(getText(resId)); + public Builder setHint(@StringRes int id) { + return setHint(getString(id)); } public Builder setHint(CharSequence text) { mInputView.setHint(text); return this; } - public Builder setContent(int resId) { - return setContent(getText(resId)); + public Builder setContent(@StringRes int id) { + return setContent(getString(id)); } public Builder setContent(CharSequence text) { mInputView.setText(text); @@ -87,24 +84,21 @@ public Builder setContent(CharSequence text) { return this; } - public Builder setCancel(int resId) { - return setCancel(getText(resId)); + public Builder setConfirm(@StringRes int id) { + return setConfirm(getString(id)); } - public Builder setCancel(CharSequence text) { - mCancelView.setText(text); - - mCancelView.setVisibility((text == null || "".equals(text.toString())) ? View.GONE : View.VISIBLE); - mLineView.setVisibility((text == null || "".equals(text.toString())) ? View.GONE : View.VISIBLE); - mConfirmView.setBackgroundResource((text == null || "".equals(text.toString())) ? - R.drawable.dialog_message_one_button : R.drawable.dialog_message_right_button); + public Builder setConfirm(CharSequence text) { + mConfirmView.setText(text); return this; } - public Builder setConfirm(int resId) { - return setConfirm(getText(resId)); + public Builder setCancel(@StringRes int id) { + return setCancel(getString(id)); } - public Builder setConfirm(CharSequence text) { - mConfirmView.setText(text); + public Builder setCancel(CharSequence text) { + mCancelView.setText(text); + + mLineView.setVisibility((text == null || "".equals(text.toString())) ? View.GONE : View.VISIBLE); return this; } @@ -113,22 +107,11 @@ public Builder setAutoDismiss(boolean dismiss) { return this; } - public Builder setListener(OnListener l) { - mListener = l; + public Builder setListener(OnListener listener) { + mListener = listener; return this; } - @Override - public BaseDialog create() { - // 如果标题为空就隐藏 - if ("".equals(mTitleView.getText().toString())) { - mTitleView.setVisibility(View.GONE); - } - addOnShowListener(this); - addOnDismissListener(this); - return super.create(); - } - /** * {@link BaseDialog.OnShowListener} */ @@ -137,7 +120,7 @@ public void onShow(BaseDialog dialog) { postDelayed(new Runnable() { @Override public void run() { - mInputManager.showSoftInput(mInputView, 0); + getSystemService(InputMethodManager.class).showSoftInput(mInputView, 0); } }, 500); } @@ -147,7 +130,7 @@ public void run() { */ @Override public void onDismiss(BaseDialog dialog) { - mInputManager.hideSoftInputFromWindow(mInputView.getWindowToken(), 0); + getSystemService(InputMethodManager.class).hideSoftInputFromWindow(mInputView.getWindowToken(), 0); } /** @@ -159,13 +142,13 @@ public void onClick(View v) { dismiss(); } - if (mListener == null) return; - - if (v == mConfirmView) { - // 判断输入是否为空 - mListener.onConfirm(getDialog(), mInputView.getText().toString()); - }else if (v == mCancelView) { - mListener.onCancel(getDialog()); + if (mListener != null) { + if (v == mConfirmView) { + // 判断输入是否为空 + mListener.onConfirm(getDialog(), mInputView.getText().toString()); + } else if (v == mCancelView) { + mListener.onCancel(getDialog()); + } } } } @@ -175,11 +158,11 @@ public interface OnListener { /** * 点击确定时回调 */ - void onConfirm(Dialog dialog, String content); + void onConfirm(BaseDialog dialog, String content); /** * 点击取消时回调 */ - void onCancel(Dialog dialog); + void onCancel(BaseDialog dialog); } } \ No newline at end of file diff --git a/dialog/src/main/java/com/hjq/dialog/MenuDialog.java b/app/src/main/java/com/hjq/demo/ui/dialog/MenuDialog.java similarity index 51% rename from dialog/src/main/java/com/hjq/dialog/MenuDialog.java rename to app/src/main/java/com/hjq/demo/ui/dialog/MenuDialog.java index 62a4ace6..e67f8bf6 100644 --- a/dialog/src/main/java/com/hjq/dialog/MenuDialog.java +++ b/app/src/main/java/com/hjq/demo/ui/dialog/MenuDialog.java @@ -1,19 +1,22 @@ -package com.hjq.dialog; +package com.hjq.demo.ui.dialog; -import android.app.Dialog; import android.content.Context; -import androidx.annotation.NonNull; -import androidx.fragment.app.FragmentActivity; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +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.BaseDialog; -import com.hjq.base.BaseDialogFragment; import com.hjq.base.BaseRecyclerViewAdapter; +import com.hjq.demo.R; +import com.hjq.demo.common.MyDialogFragment; +import com.hjq.demo.common.MyRecyclerViewAdapter; import java.util.ArrayList; import java.util.Arrays; @@ -28,27 +31,24 @@ public final class MenuDialog { public static final class Builder - extends BaseDialogFragment.Builder + extends MyDialogFragment.Builder implements View.OnClickListener, BaseRecyclerViewAdapter.OnItemClickListener { private OnListener mListener; private boolean mAutoDismiss = true; - private RecyclerView mRecyclerView; - private MenuAdapter mAdapter; - private TextView mCancelView; + private final RecyclerView mRecyclerView; + private final MenuAdapter mAdapter; + private final TextView mCancelView; public Builder(FragmentActivity activity) { super(activity); - setContentView(R.layout.dialog_menu); setAnimStyle(BaseDialog.AnimStyle.BOTTOM); - setGravity(Gravity.BOTTOM); - setWidth(MATCH_PARENT); - mRecyclerView = findViewById(R.id.rv_dialog_menu_list); - mCancelView = findViewById(R.id.tv_dialog_menu_cancel); + mRecyclerView = findViewById(R.id.rv_menu_list); + mCancelView = findViewById(R.id.tv_menu_cancel); mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); mAdapter = new MenuAdapter(getContext()); @@ -58,10 +58,27 @@ public Builder(FragmentActivity activity) { mCancelView.setOnClickListener(this); } - public Builder setList(int... resIds) { - List data = new ArrayList<>(resIds.length); - for (int resId : resIds) { - data.add(getString(resId)); + @Override + public Builder setGravity(int gravity) { + switch (gravity) { + // 如果这个是在中间显示的 + case Gravity.CENTER: + case Gravity.CENTER_VERTICAL: + // 不显示取消按钮 + setCancel(null); + // 重新设置动画 + setAnimStyle(BaseDialog.AnimStyle.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); } @@ -70,18 +87,18 @@ public Builder setList(String... data) { return setList(Arrays.asList(data)); } - public Builder setList(List data) { + @SuppressWarnings("all") + public Builder setList(List data) { mAdapter.setData(data); return this; } - public Builder setCancel(int resId) { - return setCancel(getText(resId)); + public Builder setCancel(@StringRes int id) { + return setCancel(getString(id)); } public Builder setCancel(CharSequence text) { mCancelView.setText(text); - mCancelView.setVisibility((text == null || "".equals(text.toString())) ? View.GONE : View.VISIBLE); return this; } @@ -90,8 +107,8 @@ public Builder setAutoDismiss(boolean dismiss) { return this; } - public Builder setListener(OnListener l) { - mListener = l; + public Builder setListener(OnListener listener) { + mListener = listener; return this; } @@ -114,6 +131,7 @@ public void onClick(View v) { /** * {@link BaseRecyclerViewAdapter.OnItemClickListener} */ + @SuppressWarnings("all") @Override public void onItemClick(RecyclerView recyclerView, View itemView, int position) { if (mAutoDismiss) { @@ -124,17 +142,9 @@ public void onItemClick(RecyclerView recyclerView, View itemView, int position) mListener.onSelected(getDialog(), position, mAdapter.getItem(position)); } } - -// @Override -// protected BaseDialog createDialog(Context context, int themeResId) { -// if (getGravity() == Gravity.BOTTOM) { -// return new BaseBottomDialog(context, themeResId); -// } -// return super.createDialog(context, themeResId); -// } } - private static final class MenuAdapter extends BaseRecyclerViewAdapter { + private static final class MenuAdapter extends MyRecyclerViewAdapter { private MenuAdapter(Context context) { super(context); @@ -143,54 +153,50 @@ private MenuAdapter(Context context) { @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - return new ViewHolder(parent, R.layout.item_dialog_menu); + return new ViewHolder(); } - @Override - public void onBindViewHolder(@NonNull ViewHolder holder, int position) { - holder.mTextView.setText(getItem(position)); - - if (position == 0) { - // 当前是否只有一个条目 - if (getItemCount() == 1) { - holder.itemView.setBackgroundResource(R.drawable.dialog_menu_item); - holder.mView.setVisibility(View.GONE); - }else { - holder.itemView.setBackgroundResource(R.drawable.dialog_menu_item_top); - holder.mView.setVisibility(View.VISIBLE); - } - }else if (position == getItemCount() - 1) { - holder.itemView.setBackgroundResource(R.drawable.dialog_menu_item_bottom); - holder.mView.setVisibility(View.GONE); - }else { - holder.itemView.setBackgroundResource(R.drawable.dialog_menu_item_middle); - holder.mView.setVisibility(View.VISIBLE); - } - } + final class ViewHolder extends MyRecyclerViewAdapter.ViewHolder { - final class ViewHolder extends BaseRecyclerViewAdapter.ViewHolder { + private final TextView mTextView; + private final View mView; - private TextView mTextView; - private View mView; + public ViewHolder() { + super(R.layout.item_menu); + mTextView = (TextView) findViewById(R.id.tv_menu_name); + mView = findViewById(R.id.v_menu_line); + } - private ViewHolder(ViewGroup parent, int layoutId) { - super(parent, layoutId); - mTextView = (TextView) findViewById(R.id.tv_dialog_menu_name); - mView = findViewById(R.id.v_dialog_menu_line); + @Override + public void onBindView(int position) { + mTextView.setText(getItem(position).toString()); + + if (position == 0) { + // 当前是否只有一个条目 + if (getItemCount() == 1) { + mView.setVisibility(View.GONE); + } else { + mView.setVisibility(View.VISIBLE); + } + } else if (position == getItemCount() - 1) { + mView.setVisibility(View.GONE); + } else { + mView.setVisibility(View.VISIBLE); + } } } } - public interface OnListener { + public interface OnListener { /** * 选择条目时回调 */ - void onSelected(Dialog dialog, int position, String text); + void onSelected(BaseDialog dialog, int position, T t); /** * 点击取消时回调 */ - void onCancel(Dialog dialog); + void onCancel(BaseDialog dialog); } } \ No newline at end of file diff --git a/dialog/src/main/java/com/hjq/dialog/MessageDialog.java b/app/src/main/java/com/hjq/demo/ui/dialog/MessageDialog.java similarity index 52% rename from dialog/src/main/java/com/hjq/dialog/MessageDialog.java rename to app/src/main/java/com/hjq/demo/ui/dialog/MessageDialog.java index dc45c14c..a1e324db 100644 --- a/dialog/src/main/java/com/hjq/dialog/MessageDialog.java +++ b/app/src/main/java/com/hjq/demo/ui/dialog/MessageDialog.java @@ -1,13 +1,14 @@ -package com.hjq.dialog; +package com.hjq.demo.ui.dialog; -import android.app.Dialog; -import androidx.fragment.app.FragmentActivity; -import android.view.Gravity; 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.BaseDialogFragment; +import com.hjq.demo.R; +import com.hjq.demo.common.MyDialogFragment; /** * author : Android 轮子哥 @@ -18,68 +19,62 @@ public final class MessageDialog { public static final class Builder - extends BaseDialogFragment.Builder + extends MyDialogFragment.Builder implements View.OnClickListener { private OnListener mListener; - private boolean mAutoDismiss = true; // 设置点击按钮后自动消失 + private boolean mAutoDismiss = true; - private TextView mTitleView; - private TextView mMessageView; + private final TextView mTitleView; + private final TextView mMessageView; - private TextView mCancelView; - private View mLineView; - private TextView mConfirmView; + 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); - setGravity(Gravity.CENTER); - mTitleView = findViewById(R.id.tv_dialog_message_title); - mMessageView = findViewById(R.id.tv_dialog_message_message); + mTitleView = findViewById(R.id.tv_message_title); + mMessageView = findViewById(R.id.tv_message_message); - mCancelView = findViewById(R.id.tv_dialog_message_cancel); - mLineView = findViewById(R.id.v_dialog_message_line); - mConfirmView = findViewById(R.id.tv_dialog_message_confirm); + 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(int resId) { - return setTitle(getText(resId)); + public Builder setTitle(@StringRes int id) { + return setTitle(getString(id)); } public Builder setTitle(CharSequence text) { mTitleView.setText(text); return this; } - public Builder setMessage(int resId) { - return setMessage(getText(resId)); + public Builder setMessage(@StringRes int id) { + return setMessage(getString(id)); } public Builder setMessage(CharSequence text) { mMessageView.setText(text); return this; } - public Builder setCancel(int resId) { - return setCancel(getText(resId)); + public Builder setCancel(@StringRes int id) { + return setCancel(getString(id)); } public Builder setCancel(CharSequence text) { mCancelView.setText(text); - - mCancelView.setVisibility((text == null || "".equals(text.toString())) ? View.GONE : View.VISIBLE); mLineView.setVisibility((text == null || "".equals(text.toString())) ? View.GONE : View.VISIBLE); - mConfirmView.setBackgroundResource((text == null || "".equals(text.toString())) ? - R.drawable.dialog_message_one_button : R.drawable.dialog_message_right_button); return this; } - public Builder setConfirm(int resId) { - return setConfirm(getText(resId)); + public Builder setConfirm(@StringRes int id) { + return setConfirm(getString(id)); } public Builder setConfirm(CharSequence text) { mConfirmView.setText(text); @@ -91,17 +86,13 @@ public Builder setAutoDismiss(boolean dismiss) { return this; } - public Builder setListener(OnListener l) { - mListener = l; + public Builder setListener(OnListener listener) { + mListener = listener; return this; } @Override public BaseDialog create() { - // 如果标题为空就隐藏 - if ("".equals(mTitleView.getText().toString())) { - mTitleView.setVisibility(View.GONE); - } // 如果内容为空就抛出异常 if ("".equals(mMessageView.getText().toString())) { throw new IllegalArgumentException("Dialog message not null"); @@ -118,12 +109,12 @@ public void onClick(View v) { dismiss(); } - if (mListener == null) return; - - if (v == mConfirmView) { - mListener.onConfirm(getDialog()); - }else if (v == mCancelView) { - mListener.onCancel(getDialog()); + if (mListener != null) { + if (v == mConfirmView) { + mListener.onConfirm(getDialog()); + } else if (v == mCancelView) { + mListener.onCancel(getDialog()); + } } } } @@ -133,11 +124,11 @@ public interface OnListener { /** * 点击确定时回调 */ - void onConfirm(Dialog dialog); + void onConfirm(BaseDialog dialog); /** * 点击取消时回调 */ - void onCancel(Dialog dialog); + 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 new file mode 100644 index 00000000..da77e265 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/ui/dialog/PayPasswordDialog.java @@ -0,0 +1,246 @@ +package com.hjq.demo.ui.dialog; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +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.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.widget.PasswordView; + +import java.util.Arrays; +import java.util.LinkedList; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2018/12/2 + * desc : 支付密码对话框 + */ +public final class PayPasswordDialog { + + public static final class Builder + extends MyDialogFragment.Builder + implements BaseRecyclerViewAdapter.OnItemClickListener, + View.OnClickListener { + + /** 输入键盘文本 */ + private static final String[] KEYBOARD_TEXT = new String[]{"1", "2", "3", "4", "5", "6", "7", "8", "9", "", "0", ""}; + + private OnListener mListener; + private boolean mAutoDismiss = true; + private final LinkedList mRecordList = new LinkedList<>(); + + private final TextView mTitleView; + private final ImageView mCloseView; + + private final TextView mSubTitleView; + private final TextView mMoneyView; + + private final PasswordView mPasswordView; + + private final RecyclerView mRecyclerView; + + private final KeyboardAdapter mAdapter; + + public Builder(FragmentActivity activity) { + super(activity); + setContentView(R.layout.dialog_pay_password); + setCancelable(false); + + mTitleView = findViewById(R.id.tv_pay_title); + mCloseView = findViewById(R.id.iv_pay_close); + + mSubTitleView = findViewById(R.id.tv_pay_sub_title); + 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)); + mAdapter = new KeyboardAdapter(getContext()); + mAdapter.setData(Arrays.asList(KEYBOARD_TEXT)); + mAdapter.setOnItemClickListener(this); + mRecyclerView.setAdapter(mAdapter); + } + + public Builder setTitle(@StringRes int id) { + return setTitle(getString(id)); + } + + public Builder setTitle(CharSequence title) { + mTitleView.setText(title); + return this; + } + + public Builder setSubTitle(@StringRes int id) { + return setSubTitle(getString(id)); + } + + public Builder setSubTitle(CharSequence subTitle) { + mSubTitleView.setText(subTitle); + return this; + } + + public Builder setMoney(@StringRes int id) { + return setSubTitle(getString(id)); + } + + public Builder setMoney(CharSequence money) { + mMoneyView.setText(money); + return this; + } + + public Builder setAutoDismiss(boolean dismiss) { + mAutoDismiss = dismiss; + return this; + } + + public Builder setListener(OnListener listener) { + mListener = listener; + return this; + } + + /** + * {@link BaseRecyclerViewAdapter.OnItemClickListener} + */ + @Override + public void onItemClick(RecyclerView recyclerView, View itemView, int position) { + switch (mAdapter.getItemViewType(position)) { + case KeyboardAdapter.TYPE_DELETE: + // 点击回退按钮删除 + if (mRecordList.size() != 0) { + mRecordList.removeLast(); + } + break; + case KeyboardAdapter.TYPE_EMPTY: + // 点击空白的地方不做任何操作 + break; + default: + // 判断密码是否已经输入完毕 + if (mRecordList.size() < PasswordView.PASSWORD_COUNT) { + // 点击数字,显示在密码行 + mRecordList.add(KEYBOARD_TEXT[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()); + } + }, 300); + } + } + break; + } + mPasswordView.setPassWordLength(mRecordList.size()); + } + + @Override + public void onClick(View v) { + if (v == mCloseView) { + if (mAutoDismiss) { + dismiss(); + } + + if (mListener != null) { + mListener.onCancel(getDialog()); + } + } + } + } + + private static final class KeyboardAdapter extends MyRecyclerViewAdapter { + + /** 数字按钮条目 */ + private static final int TYPE_NORMAL = 0; + /** 删除按钮条目 */ + private static final int TYPE_DELETE = 1; + /** 空按钮条目 */ + private static final int TYPE_EMPTY = 2; + + private KeyboardAdapter(Context context) { + super(context); + } + + @Override + public int getItemViewType(int position) { + switch (position) { + case 9: + return TYPE_EMPTY; + case 11: + return TYPE_DELETE; + default: + return TYPE_NORMAL; + } + } + + @NonNull + @Override + public MyRecyclerViewAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + switch (viewType) { + case TYPE_DELETE: + return new MyRecyclerViewAdapter.SimpleHolder(R.layout.item_pay_password_delete); + case TYPE_EMPTY: + return new MyRecyclerViewAdapter.SimpleHolder(R.layout.item_pay_password_empty); + default: + return new KeyboardAdapter.ViewHolder(); + } + } + + final class ViewHolder extends MyRecyclerViewAdapter.ViewHolder { + + private final TextView mTextView; + + ViewHolder() { + super(R.layout.item_pay_password_normal); + mTextView = (TextView) getItemView(); + } + + @Override + public void onBindView(int position) { + mTextView.setText(getItem(position)); + } + } + } + + public interface OnListener { + + /** + * 输入完成时回调 + * + * @param password 六位支付密码 + */ + void onCompleted(BaseDialog dialog, String password); + + /** + * 点击取消时回调 + */ + 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 0f6d9f51..e2430a00 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 @@ -1,23 +1,24 @@ package com.hjq.demo.ui.dialog; +import android.content.ClipData; +import android.content.ClipboardManager; import android.content.Context; import android.graphics.drawable.Drawable; -import android.os.Build; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.fragment.app.FragmentActivity; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; -import com.hjq.base.BaseDialog; -import com.hjq.base.BaseDialogFragment; 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.umeng.Platform; import com.hjq.umeng.UmengClient; import com.hjq.umeng.UmengShare; @@ -34,41 +35,36 @@ public final class ShareDialog { public static final class Builder - extends BaseDialogFragment.Builder + extends MyDialogFragment.Builder implements BaseRecyclerViewAdapter.OnItemClickListener { - private ShareAdapter mAdapter; - private RecyclerView mRecyclerView; + private final ShareAdapter mAdapter; + private final RecyclerView mRecyclerView; - private UmengShare.ShareData mData; + private final UmengShare.ShareData mData; private UmengShare.OnShareListener mListener; public Builder(FragmentActivity activity) { super(activity); - mRecyclerView = new RecyclerView(activity); - mRecyclerView.setBackgroundColor(0xFFF7F7F7); - mRecyclerView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - - setContentView(mRecyclerView); - setAnimStyle(BaseDialog.AnimStyle.BOTTOM); - setGravity(Gravity.BOTTOM); - setWidth(MATCH_PARENT); - - mData = new UmengShare.ShareData(getActivity()); + setContentView(R.layout.dialog_share); final List data = new ArrayList<>(); - data.add(new ShareBean(getDrawable(R.mipmap.icon_share_wx), getString(R.string.dialog_share_platform_wx), Platform.WEIXIN)); - data.add(new ShareBean(getDrawable(R.mipmap.icon_share_pyq), getString(R.string.dialog_share_platform_wx_pyq), Platform.CIRCLE)); - data.add(new ShareBean(getDrawable(R.mipmap.icon_share_qq), getString(R.string.dialog_share_platform_qq), Platform.QQ)); - data.add(new ShareBean(getDrawable(R.mipmap.icon_share_qqkj), getString(R.string.dialog_share_platform_qq_kj), Platform.QZONE)); + data.add(new ShareBean(getDrawable(R.drawable.ic_share_wechat), getString(R.string.share_platform_wechat), Platform.WECHAT)); + data.add(new ShareBean(getDrawable(R.drawable.ic_share_moment), getString(R.string.share_platform_moment), Platform.CIRCLE)); + data.add(new ShareBean(getDrawable(R.drawable.ic_share_qq), getString(R.string.share_platform_qq), Platform.QQ)); + 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.setData(data); mAdapter.setOnItemClickListener(this); mRecyclerView.setLayoutManager(new GridLayoutManager(activity, data.size())); mRecyclerView.setAdapter(mAdapter); + + mData = new UmengShare.ShareData(getActivity()); } public Builder setShareTitle(String title) { @@ -86,8 +82,8 @@ public Builder setShareLogo(String url) { return this; } - public Builder setShareLogo(@DrawableRes int resId) { - mData.setShareLogo(resId); + public Builder setShareLogo(@DrawableRes int id) { + mData.setShareLogo(id); return this; } @@ -96,8 +92,8 @@ public Builder setShareUrl(String url) { return this; } - public Builder setListener(UmengShare.OnShareListener l) { - mListener = l; + public Builder setListener(UmengShare.OnShareListener listener) { + mListener = listener; return this; } @@ -106,23 +102,19 @@ public Builder setListener(UmengShare.OnShareListener l) { */ @Override public void onItemClick(RecyclerView recyclerView, View itemView, int position) { - UmengClient.share(getActivity(), mAdapter.getItem(position).getSharePlatform(), mData, mListener); + Platform platform = mAdapter.getItem(position).getSharePlatform(); + if (platform != null) { + UmengClient.share(getActivity(), platform, mData, mListener); + } else { + // 复制到剪贴板 + getSystemService(ClipboardManager.class).setPrimaryClip(ClipData.newPlainText("url", mData.getShareUrl())); + toast(R.string.share_platform_copy_hint); + } dismiss(); } - -// @Override -// protected BaseDialog createDialog(Context context, int themeResId) { -// if (getGravity() == Gravity.BOTTOM) { -// return new BaseBottomDialog(context, themeResId); -// } -// return super.createDialog(context, themeResId); -// } } - /** - * 适配器 - */ - private static class ShareAdapter extends BaseRecyclerViewAdapter { + private static class ShareAdapter extends MyRecyclerViewAdapter { private ShareAdapter(Context context) { super(context); @@ -131,65 +123,54 @@ private ShareAdapter(Context context) { @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - TextView textView = new TextView(getContext()); - textView.setGravity(Gravity.CENTER); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - textView.setBackground(getDrawable(R.drawable.selector_selectable_transparent)); - }else { - textView.setBackgroundDrawable(getDrawable(R.drawable.selector_selectable_transparent)); - } - textView.setTextColor(0xFF333333); - textView.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 5, getResources().getDisplayMetrics())); - textView.setPadding(0, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, getResources().getDisplayMetrics()), - 0, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, getResources().getDisplayMetrics())); - textView.setCompoundDrawablePadding((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics())); - return new ViewHolder(textView); + return new ViewHolder(); } - @Override - public void onBindViewHolder(@NonNull ViewHolder holder, int position) { - ShareBean bean = getItem(position); - - holder.mTextView.setText(bean.getShareName()); - - Drawable drawable = bean.getShareIcon(); - drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); - holder.mTextView.setCompoundDrawables(null, drawable, null, null); - } - - final class ViewHolder extends BaseRecyclerViewAdapter.ViewHolder { + final class ViewHolder extends MyRecyclerViewAdapter.ViewHolder { + private ImageView mImageView; private TextView mTextView; - private ViewHolder(View itemView) { - super(itemView); - mTextView = (TextView) itemView; + private ViewHolder() { + super(R.layout.item_share); + mImageView = (ImageView) findViewById(R.id.iv_share_image); + mTextView = (TextView) findViewById(R.id.tv_share_text); + } + + @Override + public void onBindView(int position) { + ShareBean bean = getItem(position); + mImageView.setImageDrawable(bean.getShareIcon()); + mTextView.setText(bean.getShareName()); } } } private static class ShareBean { - private Drawable mShareIcon; // 分享图标 - private String mShareName; // 分享名称 - private Platform mSharePlatform; // 分享平台 + /** 分享图标 */ + private final Drawable shareIcon; + /** 分享名称 */ + private final String shareName; + /** 分享平台 */ + private final Platform sharePlatform; private ShareBean(Drawable icon, String name, Platform platform) { - mShareIcon = icon; - mShareName = name; - mSharePlatform = platform; + shareIcon = icon; + shareName = name; + sharePlatform = platform; } private Drawable getShareIcon() { - return mShareIcon; + return shareIcon; } private String getShareName() { - return mShareName; + return shareName; } private Platform getSharePlatform() { - return mSharePlatform; + return sharePlatform; } } } \ 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 new file mode 100644 index 00000000..8d9cd90b --- /dev/null +++ b/app/src/main/java/com/hjq/demo/ui/dialog/TimeDialog.java @@ -0,0 +1,227 @@ +package com.hjq.demo.ui.dialog; + +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.widget.LoopView; + +import java.util.ArrayList; +import java.util.Calendar; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2019/08/17 + * desc : 时间选择对话框 + */ +public final class TimeDialog { + + public static final class Builder + extends MyDialogFragment.Builder + implements View.OnClickListener { + + private final TextView mTitleView; + private final TextView mCancelView; + private final View mLineView; + private final TextView mConfirmView; + + private final LoopView mHourView; + private final LoopView mMinuteView; + private final LoopView mSecondView; + + private OnListener mListener; + private boolean mAutoDismiss = true; + + @SuppressWarnings("all") + public Builder(FragmentActivity activity) { + super(activity); + setContentView(R.layout.dialog_time); + setAnimStyle(BaseDialog.AnimStyle.IOS); + + 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.lv_time_hour); + mMinuteView = findViewById(R.id.lv_time_minute); + mSecondView = findViewById(R.id.lv_time_second); + + // 生产小时 + ArrayList hourData = new ArrayList<>(24); + for (int i = 0; i <= 23; i++) { + hourData.add((i < 10 ? "0" : "") + i + " " + getString(R.string.common_hour)); + } + + // 生产分钟 + ArrayList minuteData = new ArrayList<>(60); + for (int i = 0; i <= 59; i++) { + minuteData.add((i < 10 ? "0" : "") + i + " " + getString(R.string.common_minute)); + } + + // 生产秒钟 + ArrayList secondData = new ArrayList<>(60); + for (int i = 0; i <= 59; i++) { + secondData.add((i < 10 ? "0" : "") + i + " " + getString(R.string.common_second)); + } + + mHourView.setData(hourData); + mMinuteView.setData(minuteData); + mSecondView.setData(secondData); + + mCancelView.setOnClickListener(this); + mConfirmView.setOnClickListener(this); + + 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; + } + + public Builder setCancel(@StringRes int id) { + return setCancel(getString(id)); + } + public Builder setCancel(CharSequence text) { + mCancelView.setText(text); + return this; + } + + public Builder setListener(OnListener listener) { + mListener = listener; + return this; + } + + /** + * 不选择秒数 + */ + public Builder setIgnoreSecond() { + mSecondView.setVisibility(View.GONE); + return this; + } + + public Builder setAutoDismiss(boolean dismiss) { + mAutoDismiss = dismiss; + return this; + } + + public Builder setTime(String time) { + // 102030 + if (time.matches("\\d{6}")) { + setHour(time.substring(0, 2)); + setMinute(time.substring(2, 4)); + setSecond(time.substring(4, 6)); + // 10:20:30 + } else if (time.matches("\\d{2}:\\d{2}:\\d{2}")) { + setHour(time.substring(0, 2)); + setMinute(time.substring(3, 5)); + setSecond(time.substring(6, 8)); + } + return this; + } + + public Builder setHour(String hour) { + return setHour(Integer.valueOf(hour)); + } + + 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; + } + mHourView.setInitPosition(index); + return this; + } + + public Builder setMinute(String minute) { + return setMinute(Integer.valueOf(minute)); + } + + public Builder setMinute(int minute) { + int index = minute; + if (index < 0) { + index = 0; + } else if (index > mMinuteView.getSize() - 1) { + index = mMinuteView.getSize() - 1; + } + mMinuteView.setInitPosition(index); + return this; + } + + public Builder setSecond(String second) { + return setSecond(Integer.valueOf(second)); + } + + public Builder setSecond(int second) { + int index = second; + if (index < 0) { + index = 0; + } else if (index > mSecondView.getSize() - 1) { + index = mSecondView.getSize() - 1; + } + mSecondView.setInitPosition(index); + return this; + } + + /** + * {@link View.OnClickListener} + */ + + @Override + public void onClick(View v) { + if (mAutoDismiss) { + dismiss(); + } + + if (mListener != null) { + if (v == mConfirmView) { + mListener.onSelected(getDialog(), mHourView.getSelectedItem(), mMinuteView.getSelectedItem(), mSecondView.getSelectedItem()); + } else if (v == mCancelView) { + mListener.onCancel(getDialog()); + } + } + } + } + + public interface OnListener { + + /** + * 选择完时间后回调 + * + * @param hour 小时 + * @param minute 分钟 + * @param second 秒钟 + */ + void onSelected(BaseDialog dialog, int hour, int minute, int second); + + /** + * 点击取消时回调 + */ + void onCancel(BaseDialog dialog); + } +} \ No newline at end of file diff --git a/dialog/src/main/java/com/hjq/dialog/ToastDialog.java b/app/src/main/java/com/hjq/demo/ui/dialog/ToastDialog.java similarity index 74% rename from dialog/src/main/java/com/hjq/dialog/ToastDialog.java rename to app/src/main/java/com/hjq/demo/ui/dialog/ToastDialog.java index 4a2a3d9e..379d5ab4 100644 --- a/dialog/src/main/java/com/hjq/dialog/ToastDialog.java +++ b/app/src/main/java/com/hjq/demo/ui/dialog/ToastDialog.java @@ -1,12 +1,14 @@ -package com.hjq.dialog; +package com.hjq.demo.ui.dialog; -import androidx.fragment.app.FragmentActivity; -import android.view.Gravity; 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.BaseDialogFragment; +import com.hjq.demo.R; +import com.hjq.demo.common.MyDialogFragment; /** * author : Android 轮子哥 @@ -17,39 +19,41 @@ public final class ToastDialog { public static final class Builder - extends BaseDialogFragment.Builder + extends MyDialogFragment.Builder implements Runnable, BaseDialog.OnShowListener { - private TextView mMessageView; - private ImageView mIconView; + private final TextView mMessageView; + private final ImageView mIconView; private Type mType = Type.WARN; private int mDuration = 2000; public Builder(FragmentActivity activity) { super(activity); - - setThemeStyle(R.style.TransparentDialogStyle); setContentView(R.layout.dialog_toast); setAnimStyle(BaseDialog.AnimStyle.TOAST); - setGravity(Gravity.CENTER); + setBackgroundDimEnabled(false); setCancelable(false); - mMessageView = findViewById(R.id.tv_dialog_toast_message); - mIconView = findViewById(R.id.iv_dialog_toast_icon); + mMessageView = findViewById(R.id.tv_toast_message); + mIconView = findViewById(R.id.iv_toast_icon); + + addOnShowListener(this); } public Builder setType(Type type) { mType = type; switch (type) { case FINISH: - mIconView.setImageResource(R.mipmap.ic_dialog_finish); + mIconView.setImageResource(R.drawable.ic_dialog_finish); break; case ERROR: - mIconView.setImageResource(R.mipmap.ic_dialog_error); + mIconView.setImageResource(R.drawable.ic_dialog_error); break; case WARN: - mIconView.setImageResource(R.mipmap.ic_dialog_warning); + mIconView.setImageResource(R.drawable.ic_dialog_warning); + break; + default: break; } return this; @@ -60,8 +64,8 @@ public Builder setDuration(int duration) { return this; } - public Builder setMessage(int resId) { - return setMessage(getText(resId)); + public Builder setMessage(@StringRes int id) { + return setMessage(getString(id)); } public Builder setMessage(CharSequence text) { mMessageView.setText(text); @@ -78,13 +82,10 @@ public BaseDialog create() { if ("".equals(mMessageView.getText().toString())) { throw new IllegalArgumentException("Dialog message not null"); } - addOnShowListener(this); + return super.create(); } - /** - * {@link BaseDialog.OnShowListener} - */ @Override public void onShow(BaseDialog dialog) { // 延迟自动关闭 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 f93d4732..24c3f977 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 @@ -11,22 +11,21 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; -import androidx.fragment.app.FragmentActivity; -import androidx.core.content.FileProvider; import android.text.format.Formatter; -import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import androidx.core.content.FileProvider; +import androidx.fragment.app.FragmentActivity; + import com.hjq.base.BaseDialog; -import com.hjq.base.BaseDialogFragment; 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.permissions.Permission; import com.hjq.permissions.XXPermissions; -import com.hjq.toast.ToastUtils; -import com.hjq.widget.NumberProgressBar; import java.io.File; import java.util.List; @@ -40,9 +39,9 @@ public final class UpdateDialog { public static final class Builder - extends BaseDialogFragment.Builder - implements OnDownloadListener, - View.OnClickListener, OnPermission { + extends MyDialogFragment.Builder + implements OnDownloadListener, OnPermission, + View.OnClickListener { private TextView mNameView; private TextView mSizeView; @@ -50,34 +49,33 @@ public static final class Builder private NumberProgressBar mProgressView; private TextView mUpdateView; - private ViewGroup mCancelLayout; private View mCloseView; - // 下载地址 + /** 下载地址 */ private String mDownloadUrl; - // 当前下载状态 + /** 当前下载状态 */ private int mDownloadStatus = -1; - // 下载处理对象 + /** 下载处理对象 */ private DownloadHandler mDownloadHandler; public Builder(FragmentActivity activity) { super(activity); setContentView(R.layout.dialog_update); - setAnimStyle(BaseDialog.AnimStyle.TOAST); - setGravity(Gravity.CENTER); + setAnimStyle(BaseDialog.AnimStyle.BOTTOM); + setCancelable(false); - mNameView = findViewById(R.id.tv_dialog_update_name); - mSizeView = findViewById(R.id.tv_dialog_update_size); - mContentView = findViewById(R.id.tv_dialog_update_content); - mProgressView = findViewById(R.id.pb_dialog_update_progress); + 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_dialog_update_update); - mCancelLayout = findViewById(R.id.ll_dialog_update_cancel); - mCloseView = findViewById(R.id.iv_dialog_update_close); + mUpdateView = findViewById(R.id.tv_update_update); + mCancelLayout = findViewById(R.id.ll_update_cancel); + mCloseView = findViewById(R.id.iv_update_close); mUpdateView.setOnClickListener(this); mCloseView.setOnClickListener(this); @@ -117,7 +115,10 @@ public Builder setUpdateLog(CharSequence text) { */ public Builder setForceUpdate(boolean force) { mCancelLayout.setVisibility(force ? View.GONE : View.VISIBLE); - return setCancelable(!force); + if (force) { + setCancelable(true); + } + return this; } /** @@ -144,52 +145,55 @@ public void downloadStateChange(int state) { // 判断下载状态 switch (state) { - case DownloadManager.STATUS_RUNNING: // 下载中 - mUpdateView.setText(R.string.dialog_update_status_running); + // 下载中 + case DownloadManager.STATUS_RUNNING: + mUpdateView.setText(R.string.update_status_running); // 显示进度条 mProgressView.setVisibility(View.VISIBLE); break; - case DownloadManager.STATUS_SUCCESSFUL: // 下载成功 - mUpdateView.setText(R.string.dialog_update_status_successful); + // 下载成功 + 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.dialog_update_status_failed); + // 下载失败 + case DownloadManager.STATUS_FAILED: + mUpdateView.setText(R.string.update_status_failed); // 删除下载的文件 mDownloadHandler.deleteDownloadFile(); break; - case DownloadManager.STATUS_PAUSED: // 下载暂停 - mUpdateView.setText(R.string.dialog_update_status_paused); + // 下载暂停 + case DownloadManager.STATUS_PAUSED: + mUpdateView.setText(R.string.update_status_paused); break; - case DownloadManager.STATUS_PENDING: // 等待下载 - mUpdateView.setText(R.string.dialog_update_status_pending); + // 等待下载 + case DownloadManager.STATUS_PENDING: + mUpdateView.setText(R.string.update_status_pending); break; default: break; } } - /** - * {@link View.OnClickListener,} - */ - @Override public void onClick(View v) { - if (v == mCloseView) { // 点击了下次再说 + if (v == mCloseView) { dismiss(); - }else if (v == mUpdateView) { // 点击了更新按钮 - + } else if (v == mUpdateView) { // 判断下载状态 switch (mDownloadStatus) { - case -1: // 没有任何状态 - case DownloadManager.STATUS_FAILED: // 下载失败 + // 没有任何状态 + case -1: + // 下载失败 + case DownloadManager.STATUS_FAILED: // 重新下载 requestPermission(); break; - case DownloadManager.STATUS_SUCCESSFUL: // 下载成功 + // 下载成功 + case DownloadManager.STATUS_SUCCESSFUL: // 安装 Apk mDownloadHandler.openDownloadFile(); break; @@ -204,9 +208,12 @@ public void onClick(View v) { */ private void requestPermission() { XXPermissions.with(getActivity()) - .constantRequest() // 可设置被拒绝后继续申请,直到用户授权或者永久拒绝 - .permission(Permission.REQUEST_INSTALL_PACKAGES) //安装包权限 - .permission(Permission.Group.STORAGE) // 存储权限 + // 可设置被拒绝后继续申请,直到用户授权或者永久拒绝 + .constantRequest() + // 安装包权限 + .permission(Permission.REQUEST_INSTALL_PACKAGES) + // 存储权限 + .permission(Permission.Group.STORAGE) .request(this); } @@ -221,7 +228,7 @@ public void hasPermission(List granted, boolean isAll) { mDownloadHandler.setDownloadListener(this); if (!mDownloadHandler.createDownload(mDownloadUrl, getString(R.string.app_name) + " " + mNameView.getText().toString() + ".apk", null)) { - mUpdateView.setText(R.string.dialog_update_download_fail); + mUpdateView.setText(R.string.update_download_fail); } else { // 设置对话框不能被取消 setCancelable(false); @@ -233,22 +240,27 @@ public void hasPermission(List granted, boolean isAll) { @Override public void noPermission(List denied, boolean quick) { - ToastUtils.show(R.string.dialog_update_permission_hint); + toast(R.string.update_permission_hint); } } private static final class DownloadHandler extends Handler { - private Context mContext; + private final Context mContext; - private DownloadManager mDownloadManager; // 下载管理器对象 - private DownloadObserver mDownloadObserver; // 下载内容观察者 + /** 下载管理器对象 */ + private final DownloadManager mDownloadManager; + /** 下载内容观察者 */ + private DownloadObserver mDownloadObserver; - private long mDownloadId; // 下载 id + /** 下载文件 id */ + private long mDownloadId; - private OnDownloadListener mListener; // 下载监听 + /** 下载监听 */ + private OnDownloadListener mListener; - private File mDownloadFile; // 下载的文件 + /** 下载的文件 */ + private File mDownloadFile; private DownloadHandler(Context context) { super(Looper.getMainLooper()); @@ -256,23 +268,30 @@ private DownloadHandler(Context context) { mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); } - private void setDownloadListener(OnDownloadListener l) { - this.mListener = l; + private void setDownloadListener(OnDownloadListener listener) { + mListener = listener; } @Override public void handleMessage(Message msg) { - if (mListener == null) return; + if (mListener == null) { + return; + } // 判断下载状态 switch (msg.what) { - case DownloadManager.STATUS_RUNNING: // 下载中 - // 计算下载百分比 - int progress = msg.arg2 * 100 / msg.arg1; + // 下载中 + 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: // 下载失败 + // 下载成功 + case DownloadManager.STATUS_SUCCESSFUL: + // 下载失败 + case DownloadManager.STATUS_FAILED: // 移除内容观察者 if (mDownloadObserver != null) { mContext.getContentResolver().unregisterContentObserver(mDownloadObserver); @@ -293,6 +312,7 @@ public void handleMessage(Message msg) { * @param notificationTitle 通知栏标题 * @return 下载 id */ + @SuppressWarnings("ResultOfMethodCallIgnored") private boolean createDownload(String downloadUrl, String fileName, String notificationTitle) { if (fileName == null) { throw new IllegalArgumentException("The filename cannot be empty"); @@ -362,9 +382,9 @@ void deleteDownloadFile() { private static class DownloadObserver extends ContentObserver { - private Handler mHandler; - private DownloadManager mDownloadManager; - private DownloadManager.Query mQuery; + private final Handler mHandler; + private final DownloadManager mDownloadManager; + private final DownloadManager.Query mQuery; DownloadObserver(Handler handler, DownloadManager manager, DownloadManager.Query query) { super(handler); @@ -388,7 +408,7 @@ public void onChange(boolean selfChange) { // 总需下载的字节数 int totalBytes = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); // 已经下载的字节数 - int downloadedBytes = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); + int downloadBytes = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); // 下载状态 int downloadStatus = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS)); @@ -398,7 +418,7 @@ public void onChange(boolean selfChange) { // 发送更新消息 Message msg = mHandler.obtainMessage(); msg.arg1 = totalBytes; - msg.arg2 = downloadedBytes; + msg.arg2 = downloadBytes; msg.what = downloadStatus; mHandler.sendMessage(msg); } 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 new file mode 100644 index 00000000..d4b70205 --- /dev/null +++ b/app/src/main/java/com/hjq/demo/ui/dialog/WaitDialog.java @@ -0,0 +1,45 @@ +package com.hjq.demo.ui.dialog; + +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; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/AndroidProject + * time : 2018/12/2 + * desc : 等待加载对话框 + */ +public final class WaitDialog { + + public static final class Builder + extends MyDialogFragment.Builder { + + private final TextView mMessageView; + + public Builder(FragmentActivity activity) { + super(activity); + setContentView(R.layout.dialog_wait); + setAnimStyle(BaseDialog.AnimStyle.TOAST); + setBackgroundDimEnabled(false); + setCancelable(false); + + mMessageView = findViewById(R.id.tv_wait_message); + } + + public Builder setMessage(@StringRes int id) { + return setMessage(getString(id)); + } + public Builder setMessage(CharSequence text) { + mMessageView.setText(text); + mMessageView.setVisibility(text == null ? View.GONE : View.VISIBLE); + return this; + } + } +} \ No newline at end of file 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 353e38c2..989849f1 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 @@ -21,11 +21,6 @@ protected int getLayoutId() { return R.layout.fragment_copy; } - @Override - protected int getTitleId() { - return R.id.tb_copy_title; - } - @Override protected void initView() { 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 4bcc7416..0c0ae474 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,11 +1,12 @@ package com.hjq.demo.ui.fragment; -import com.google.android.material.appbar.AppBarLayout; -import androidx.appcompat.widget.Toolbar; import android.widget.TextView; -import com.gyf.barlibrary.ImmersionBar; -import com.hjq.bar.TitleBar; +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.ui.activity.HomeActivity; @@ -28,8 +29,6 @@ public final class TestFragmentA extends MyLazyFragment XCollapsingToolbarLayout mCollapsingToolbarLayout; @BindView(R.id.t_test_title) Toolbar mToolbar; - @BindView(R.id.tb_test_a_bar) - TitleBar mTitleBar; @BindView(R.id.tv_test_address) TextView mAddressView; @@ -45,15 +44,10 @@ protected int getLayoutId() { return R.layout.fragment_test_a; } - @Override - protected int getTitleId() { - return R.id.tb_test_a_bar; - } - @Override protected void initView() { // 给这个ToolBar设置顶部内边距,才能和TitleBar进行对齐 - ImmersionBar.setTitleBar(getBindingActivity(), mToolbar); + ImmersionBar.setTitleBar(getAttachActivity(), mToolbar); //设置渐变监听 mCollapsingToolbarLayout.setOnScrimsListener(this); @@ -81,13 +75,13 @@ public boolean statusBarDarkFont() { * {@link XCollapsingToolbarLayout.OnScrimsListener} */ @Override - public void onScrimsStateChange(boolean shown) { + public void onScrimsStateChange(XCollapsingToolbarLayout layout, boolean shown) { if (shown) { - mAddressView.setTextColor(getResources().getColor(R.color.black)); + mAddressView.setTextColor(ContextCompat.getColor(getAttachActivity(), R.color.black)); mSearchView.setBackgroundResource(R.drawable.bg_home_search_bar_gray); getStatusBarConfig().statusBarDarkFont(true).init(); - }else { - mAddressView.setTextColor(getResources().getColor(R.color.white)); + } else { + mAddressView.setTextColor(ContextCompat.getColor(getAttachActivity(), R.color.white)); mSearchView.setBackgroundResource(R.drawable.bg_home_search_bar_transparent); 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 f48692b4..a55838e5 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 @@ -5,8 +5,8 @@ import com.hjq.demo.R; import com.hjq.demo.common.MyLazyFragment; import com.hjq.demo.ui.activity.HomeActivity; -import com.hjq.widget.CountdownView; -import com.hjq.widget.SwitchButton; +import com.hjq.widget.view.CountdownView; +import com.hjq.widget.view.SwitchButton; import butterknife.BindView; import butterknife.OnClick; @@ -35,11 +35,6 @@ protected int getLayoutId() { return R.layout.fragment_test_b; } - @Override - protected int getTitleId() { - return R.id.tb_test_b_title; - } - @Override protected void initView() { mSwitchButton.setOnCheckedChangeListener(this); @@ -54,7 +49,7 @@ protected void initData() { public void onClick(View v) { switch (v.getId()) { case R.id.cv_test_countdown: - toast(getResources().getString(R.string.common_send_code_succeed)); + toast(R.string.common_code_send_hint); break; default: break; 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 60328465..dc87e6bb 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 @@ -1,11 +1,13 @@ package com.hjq.demo.ui.fragment; +import android.util.TypedValue; import android.view.View; import android.widget.ImageView; import com.hjq.demo.R; import com.hjq.demo.common.MyLazyFragment; 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; @@ -36,11 +38,6 @@ protected int getLayoutId() { return R.layout.fragment_test_c; } - @Override - protected int getTitleId() { - return R.id.tb_test_c_title; - } - @Override protected void initView() { @@ -57,36 +54,65 @@ 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_toast, R.id.btn_test_permission, - R.id.btn_test_state_black, R.id.btn_test_state_white, - R.id.btn_test_swipe_enabled, R.id.btn_test_swipe_disable}) + @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}) public void onClick(View v) { switch (v.getId()) { case R.id.btn_test_image1: - ImageLoader.loadImage(mImageView, "https://www.baidu.com/img/bd_logo.png"); + mImageView.setVisibility(View.VISIBLE); + ImageLoader.with(this) + .load("https://www.baidu.com/img/bd_logo.png") + .into(mImageView); break; case R.id.btn_test_image2: - ImageLoader.loadCircleImage(mImageView, "https://www.baidu.com/img/bd_logo.png"); + mImageView.setVisibility(View.VISIBLE); + ImageLoader.with(this) + .circle() + .load("https://www.baidu.com/img/bd_logo.png") + .into(mImageView); break; case R.id.btn_test_image3: - ImageLoader.loadRoundImage(mImageView, "https://www.baidu.com/img/bd_logo.png", 20); + mImageView.setVisibility(View.VISIBLE); + ImageLoader.with(this) + .circle((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, this.getResources().getDisplayMetrics())) + .load("https://www.baidu.com/img/bd_logo.png") + .into(mImageView); + break; + case R.id.btn_test_image4: + PhotoActivity.start(getAttachActivity(), new PhotoActivity.OnPhotoSelectListener() { + + @Override + public void onSelect(List data) { + mImageView.setVisibility(View.VISIBLE); + ImageLoader.with(getAttachActivity()) + .load(data.get(0)) + .into(mImageView); + } + + @Override + public void onCancel() { + toast("取消了"); + } + }); break; case R.id.btn_test_toast: toast("我是吐司"); break; case R.id.btn_test_permission: - XXPermissions.with(getBindingActivity()) - //.constantRequest() //可设置被拒绝后继续申请,直到用户授权或者永久拒绝 - //.permission(Permission.SYSTEM_ALERT_WINDOW, Permission.REQUEST_INSTALL_PACKAGES) //支持请求6.0悬浮窗权限8.0请求安装权限 - .permission(Permission.CAMERA) //不指定权限则自动获取清单中的危险权限 + XXPermissions.with(getAttachActivity()) + // 可设置被拒绝后继续申请,直到用户授权或者永久拒绝 + //.constantRequest() + // 支持请求6.0悬浮窗权限8.0请求安装权限 + //.permission(Permission.SYSTEM_ALERT_WINDOW, Permission.REQUEST_INSTALL_PACKAGES) + // 不指定权限则自动获取清单中的危险权限 + .permission(Permission.CAMERA) .request(new OnPermission() { @Override public void hasPermission(List granted, boolean isAll) { if (isAll) { toast("获取权限成功"); - }else { + } else { toast("获取权限成功,部分权限未正常授予"); } } @@ -96,26 +122,18 @@ public void noPermission(List denied, boolean quick) { if(quick) { toast("被永久拒绝授权,请手动授予权限"); //如果是被永久拒绝就跳转到应用权限系统设置页面 - XXPermissions.gotoPermissionSettings(getBindingActivity()); - }else { + XXPermissions.gotoPermissionSettings(getAttachActivity()); + } else { toast("获取权限失败"); } } }); break; case R.id.btn_test_state_black: - getBindingActivity().getStatusBarConfig().statusBarDarkFont(true).init(); + getAttachActivity().getStatusBarConfig().statusBarDarkFont(true).init(); break; case R.id.btn_test_state_white: - getBindingActivity().getStatusBarConfig().statusBarDarkFont(false).init(); - break; - case R.id.btn_test_swipe_enabled: - getBindingActivity().getSwipeBackHelper().setSwipeBackEnable(true); - toast("当前界面不会生效,其他界面调用才会有效果"); - break; - case R.id.btn_test_swipe_disable: - getBindingActivity().getSwipeBackHelper().setSwipeBackEnable(false); - toast("当前界面不会生效,其他界面调用才会有效果"); + 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 2e578a6a..5d7108a7 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 @@ -1,16 +1,16 @@ package com.hjq.demo.ui.fragment; -import android.app.Dialog; import android.content.Intent; 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.ui.activity.AboutActivity; import com.hjq.demo.ui.activity.DialogActivity; import com.hjq.demo.ui.activity.HomeActivity; -import com.hjq.demo.ui.activity.PhotoActivity; +import com.hjq.demo.ui.activity.ImageActivity; import com.hjq.demo.ui.activity.LoginActivity; import com.hjq.demo.ui.activity.PasswordForgetActivity; import com.hjq.demo.ui.activity.PasswordResetActivity; @@ -21,7 +21,9 @@ import com.hjq.demo.ui.activity.SettingActivity; import com.hjq.demo.ui.activity.StatusActivity; import com.hjq.demo.ui.activity.WebActivity; -import com.hjq.dialog.MessageDialog; +import com.hjq.demo.ui.dialog.MessageDialog; + +import java.util.ArrayList; import butterknife.OnClick; @@ -42,11 +44,6 @@ protected int getLayoutId() { return R.layout.fragment_test_d; } - @Override - protected int getTitleId() { - return R.id.tb_test_d_title; - } - @Override protected void initView() { @@ -57,9 +54,9 @@ 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_pay}) + @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}) public void onClick(View v) { switch (v.getId()) { case R.id.btn_test_dialog: @@ -96,22 +93,27 @@ public void onClick(View v) { startActivity(AboutActivity.class); break; case R.id.btn_test_browser: - startActivity(WebActivity.class); + WebActivity.start(getAttachActivity(), "https://github.com/getActivity/"); break; case R.id.btn_test_image: - startActivity(PhotoActivity.class); + ArrayList images = new ArrayList<>(); + images.add("https://www.baidu.com/img/bd_logo.png"); + images.add("https://avatars1.githubusercontent.com/u/28616817?s=460&v=4"); + ImageActivity.start(getAttachActivity(), images, images.size() - 1); break; + case R.id.btn_test_crash: + throw new IllegalStateException("are you ok?"); case R.id.btn_test_pay: - new MessageDialog.Builder(getBindingActivity()) - .setTitle("捐赠") // 标题可以不用填写 + new MessageDialog.Builder(getAttachActivity()) + .setTitle("捐赠") .setMessage("如果您觉得这个开源项目很棒,希望它能更好地坚持开发下去,可否愿意花一点点钱(推荐 10.24 元)作为对于开发者的激励") .setConfirm("支付宝") - .setCancel(null) // 设置 null 表示不显示取消按钮 - //.setAutoDismiss(false) // 设置点击按钮后不关闭对话框 + .setCancel(null) + //.setAutoDismiss(false) .setListener(new MessageDialog.OnListener() { @Override - public void onConfirm(Dialog dialog) { + 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); @@ -123,7 +125,7 @@ public void onConfirm(Dialog dialog) { } @Override - public void onCancel(Dialog dialog) {} + public void onCancel(BaseDialog dialog) {} }) .show(); break; diff --git a/dialog/src/main/java/com/hjq/dialog/widget/LoopView.java b/app/src/main/java/com/hjq/demo/widget/LoopView.java similarity index 95% rename from dialog/src/main/java/com/hjq/dialog/widget/LoopView.java rename to app/src/main/java/com/hjq/demo/widget/LoopView.java index 5b08148a..af10b1f6 100644 --- a/dialog/src/main/java/com/hjq/dialog/widget/LoopView.java +++ b/app/src/main/java/com/hjq/demo/widget/LoopView.java @@ -1,4 +1,4 @@ -package com.hjq.dialog.widget; +package com.hjq.demo.widget; import android.annotation.SuppressLint; import android.content.Context; @@ -14,7 +14,7 @@ import android.view.MotionEvent; import android.view.View; -import com.hjq.dialog.R; +import com.hjq.demo.R; import java.util.List; import java.util.concurrent.Executors; @@ -32,19 +32,19 @@ public final class LoopView extends View { private static final String TAG = "LoopView"; - private ScheduledExecutorService mExecutor = Executors.newSingleThreadScheduledExecutor(); + private final ScheduledExecutorService mExecutor = Executors.newSingleThreadScheduledExecutor(); private ScheduledFuture mScheduledFuture; private LoopScrollListener mListener; private List mData; - private Paint mTopBottomTextPaint; - private Paint mCenterTextPaint; - private Paint mCenterLinePaint; + private final Paint mTopBottomTextPaint; + private final Paint mCenterTextPaint; + private final Paint mCenterLinePaint; private int mTotalScrollY; - private GestureDetector mGestureDetector; + private final GestureDetector mGestureDetector; private int mSelectedItem; private int mTextSize; @@ -120,6 +120,8 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { break; case MeasureSpec.EXACTLY: break; + default: + break; } switch (MeasureSpec.getMode(heightMeasureSpec)) { @@ -129,6 +131,8 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { break; case MeasureSpec.EXACTLY: break; + default: + break; } setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); @@ -137,7 +141,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mItemHeight = mLineSpacingMultiplier * mMaxTextHeight; // auto calculate the text's left/right value when draw - mHorizontalPadding = (width - mMaxTextWidth) / 2; + mHorizontalPadding = (width - (float) mMaxTextWidth) / 2; mVerticalPadding = (height - mCircularDiameter) / 2; // topLineY = diameter/2 - itemHeight(mItemHeight) / 2 + mVerticalPadding @@ -148,12 +152,15 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { @Override protected void onDraw(Canvas canvas) { - if (mData == null) return; + if (mData == null) { + return; + } // the length of single item is mItemHeight int mChangingItem = (int) (mTotalScrollY / (mItemHeight)); mCurrentIndex = mInitPosition + mChangingItem % mData.size(); - if (!mCanLoop) { // can loop + // can loop + if (!mCanLoop) { if (mCurrentIndex < 0) { mCurrentIndex = 0; } @@ -254,12 +261,8 @@ protected void onDraw(Canvas canvas) { @Override public boolean onTouchEvent(MotionEvent motionevent) { - switch (motionevent.getAction()) { - case MotionEvent.ACTION_UP: - default: - if (!mGestureDetector.onTouchEvent(motionevent)) { - startSmoothScrollTo(); - } + if (!mGestureDetector.onTouchEvent(motionevent)) { + startSmoothScrollTo(); } return true; } @@ -289,7 +292,9 @@ public int getSelectedItem() { } public void setInitPosition(int initPosition) { - if (mData == null) return; + if (mData == null) { + return; + } if (initPosition > mData.size()) { initPosition = mData.size() - 1; } @@ -300,8 +305,8 @@ public void setInitPosition(int initPosition) { } } - public void setLoopListener(LoopScrollListener l) { - mListener = l; + public void setLoopListener(LoopScrollListener listener) { + mListener = listener; } /** @@ -372,6 +377,14 @@ public final void setData(List data) { invalidate(); } + public List getData() { + return mData; + } + + public int getSize() { + return mData != null ? mData.size() : 0; + } + private void cancelSchedule() { if (mScheduledFuture != null && !mScheduledFuture.isCancelled()) { @@ -445,9 +458,9 @@ public final void run() { */ private class HalfHeightRunnable implements Runnable { + final int offset; int realTotalOffset; int realOffset; - int offset; HalfHeightRunnable(int offset) { this.offset = offset; diff --git a/widget/src/main/java/com/hjq/widget/NumberProgressBar.java b/app/src/main/java/com/hjq/demo/widget/NumberProgressBar.java similarity index 93% rename from widget/src/main/java/com/hjq/widget/NumberProgressBar.java rename to app/src/main/java/com/hjq/demo/widget/NumberProgressBar.java index 3a311218..ff97fe6f 100644 --- a/widget/src/main/java/com/hjq/widget/NumberProgressBar.java +++ b/app/src/main/java/com/hjq/demo/widget/NumberProgressBar.java @@ -1,4 +1,4 @@ -package com.hjq.widget; +package com.hjq.demo.widget; import android.content.Context; import android.content.res.TypedArray; @@ -10,6 +10,8 @@ import android.util.TypedValue; import android.view.View; +import com.hjq.demo.R; + /** * author : Android 轮子哥 * github : https://github.com/getActivity/AndroidProject @@ -18,41 +20,41 @@ */ 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 float mTextOffset; + /** 进度文本偏移量 */ + private final float mTextOffset; public NumberProgressBar(Context context) { this(context, null); @@ -111,6 +113,8 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { break; case MeasureSpec.EXACTLY: break; + default: + break; } switch (MeasureSpec.getMode(heightMeasureSpec)) { case MeasureSpec.AT_MOST: @@ -120,6 +124,8 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { break; case MeasureSpec.EXACTLY: break; + default: + break; } setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); } diff --git a/dialog/src/main/java/com/hjq/dialog/widget/PasswordView.java b/app/src/main/java/com/hjq/demo/widget/PasswordView.java similarity index 80% rename from dialog/src/main/java/com/hjq/dialog/widget/PasswordView.java rename to app/src/main/java/com/hjq/demo/widget/PasswordView.java index 37c8cdd8..2cf50284 100644 --- a/dialog/src/main/java/com/hjq/dialog/widget/PasswordView.java +++ b/app/src/main/java/com/hjq/demo/widget/PasswordView.java @@ -1,14 +1,15 @@ -package com.hjq.dialog.widget; +package com.hjq.demo.widget; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; -import androidx.annotation.Nullable; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; +import androidx.annotation.Nullable; + /** * author : Android 轮子哥 * github : https://github.com/getActivity/AndroidProject @@ -17,28 +18,28 @@ */ public final class PasswordView extends View { - private Paint mPaint; - private Path mPath; - private Paint mPointPaint; + private final Paint mPaint; + private final Path mPath; + private final Paint mPointPaint; - // 单个密码框的宽度 + /** 单个密码框的宽度 */ private int mItemWidth = 44; - // 单个密码框的高度 + /** 单个密码框的高度 */ private int mItemHeight = 41; - // 中心黑点的半径大小 + /** 中心黑点的半径大小 */ private static final int POINT_RADIUS = 15; - // 中心黑点的颜色 + /** 中心黑点的颜色 */ private static final int POINT_COLOR = 0xFF666666; - // 密码框边界线的颜色值 + /** 密码框边界线的颜色值 */ private static final int STROKE_COLOR = 0xFFECECEC; - // 密码总个数 + /** 密码总个数 */ public static final int PASSWORD_COUNT = 6; - // 已经输入的密码个数,也就是需要显示的小黑点个数 + /** 已经输入的密码个数,也就是需要显示的小黑点个数 */ private int mCurrentIndex = 0; public PasswordView(Context context) { @@ -56,9 +57,12 @@ public PasswordView(Context context, @Nullable AttributeSet attrs, int defStyleA mItemHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mItemHeight, getResources().getDisplayMetrics()); mPaint = new Paint(); - mPaint.setAntiAlias(true); //设置抗锯齿 - mPaint.setColor(STROKE_COLOR); //设置颜色 - mPaint.setStyle(Paint.Style.STROKE); //设置描边 + // 设置抗锯齿 + mPaint.setAntiAlias(true); + // 设置颜色 + mPaint.setColor(STROKE_COLOR); + // 设置描边 + mPaint.setStyle(Paint.Style.STROKE); mPath = new Path(); mPath.moveTo(0, 0); @@ -82,6 +86,8 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { break; case MeasureSpec.EXACTLY: break; + default: + break; } switch (MeasureSpec.getMode(heightMeasureSpec)) { @@ -91,6 +97,8 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { break; case MeasureSpec.EXACTLY: break; + default: + break; } setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); @@ -112,7 +120,7 @@ protected void onDraw(Canvas canvas) { return; } for (int i = 1; i <= mCurrentIndex; i++) { - canvas.drawCircle(i * mItemWidth - mItemWidth / 2, mItemHeight / 2, POINT_RADIUS, mPointPaint); + canvas.drawCircle(i * mItemWidth - (float) mItemWidth / 2, (float) mItemHeight / 2, POINT_RADIUS, mPointPaint); } } diff --git a/app/src/main/java/com/hjq/demo/widget/PhotoViewPager.java b/app/src/main/java/com/hjq/demo/widget/PhotoViewPager.java index a6906fd3..49645204 100644 --- a/app/src/main/java/com/hjq/demo/widget/PhotoViewPager.java +++ b/app/src/main/java/com/hjq/demo/widget/PhotoViewPager.java @@ -1,10 +1,11 @@ package com.hjq.demo.widget; import android.content.Context; -import androidx.viewpager.widget.ViewPager; import android.util.AttributeSet; import android.view.MotionEvent; +import androidx.viewpager.widget.ViewPager; + /** * author : Android 轮子哥 * github : https://github.com/getActivity/AndroidProject @@ -23,7 +24,7 @@ public PhotoViewPager(Context context, AttributeSet attrs) { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - // 当PhotoView 和 ViewPager 组合时 ,用双指进行放大时 是没有问题的,但是用双指进行缩小的时候,程序就会崩掉 + // 当 PhotoView 和 ViewPager 组合时 ,用双指进行放大时 是没有问题的,但是用双指进行缩小的时候,程序就会崩掉 // 并且抛出java.lang.IllegalArgumentException: pointerIndex out of range try { return super.onInterceptTouchEvent(ev); diff --git a/app/src/main/java/com/hjq/demo/widget/ProgressView.java b/app/src/main/java/com/hjq/demo/widget/ProgressView.java new file mode 100644 index 00000000..4ed08bcf --- /dev/null +++ b/app/src/main/java/com/hjq/demo/widget/ProgressView.java @@ -0,0 +1,708 @@ +package com.hjq.demo.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +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; +import android.provider.Settings; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; + +import androidx.annotation.NonNull; + +import com.hjq.demo.R; + +/** + * author : Todd-Davies + * github : https://github.com/Todd-Davies/ProgressWheel + * time : 2019/07/13 + * desc : 进度条控件 + */ +public final class ProgressView extends View { + + private final static int BAR_LENGTH = 16; + private final static int BAR_MAX_LENGTH = 270; + private final static long PAUSE_GROWING_TIME = 200; + + /** Sizes (with defaults in DP) */ + private int mCircleRadius = 28; + private int mBarWidth = 4; + private int mRimWidth = 4; + private boolean mFillRadius; + private double mTimeStartGrowing = 0; + private double mBarSpinCycleTime = 400; + private float mBarExtraLength = 0; + private boolean mBarGrowingFromFront = true; + private long mPausedTimeWithoutGrowing = 0; + /** Colors (with defaults) */ + private int mBarColor = 0xAA000000; + private int mRimColor = 0x00FFFFFF; + + /** Paints */ + private final Paint mBarPaint = new Paint(); + private final Paint mRimPaint = new Paint(); + + /** Rectangles */ + private RectF mCircleBounds = new RectF(); + + /** Animation The amount of degrees per second */ + private float mSpinSpeed = 230.0f; + /** private float mSpinSpeed = 120.0f; */ + /** The last time the spinner was animated */ + private long mLastTimeAnimated = 0; + + private boolean mLinearProgress; + + private float mProgress = 0.0f; + private float mTargetProgress = 0.0f; + private boolean isSpinning = false; + + private ProgressCallback mCallback; + + private final boolean mShouldAnimate; + + public ProgressView(Context context) { + this(context, null, 0); + } + + public ProgressView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ProgressView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ProgressView); + mBarWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mBarWidth, getResources().getDisplayMetrics()); + mRimWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mRimWidth, getResources().getDisplayMetrics()); + mCircleRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mCircleRadius, getResources().getDisplayMetrics()); + mCircleRadius = (int) array.getDimension(R.styleable.ProgressView_circleRadius, mCircleRadius); + mFillRadius = array.getBoolean(R.styleable.ProgressView_fillRadius, false); + mBarWidth = (int) array.getDimension(R.styleable.ProgressView_barWidth, mBarWidth); + mRimWidth = (int) array.getDimension(R.styleable.ProgressView_rimWidth, mRimWidth); + float baseSpinSpeed = array.getFloat(R.styleable.ProgressView_spinSpeed, mSpinSpeed / 360.0f); + mSpinSpeed = baseSpinSpeed * 360; + mBarSpinCycleTime = array.getInt(R.styleable.ProgressView_barSpinCycleTime, (int) mBarSpinCycleTime); + mBarColor = array.getColor(R.styleable.ProgressView_barColor, mBarColor); + mRimColor = array.getColor(R.styleable.ProgressView_rimColor, mRimColor); + mLinearProgress = array.getBoolean(R.styleable.ProgressView_linearProgress, false); + if (array.getBoolean(R.styleable.ProgressView_progressIndeterminate, false)) { + spin(); + } + 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); + } + } + + mShouldAnimate = animationValue != 0; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int width; + int height; + + int viewWidth = mCircleRadius + this.getPaddingLeft() + this.getPaddingRight(); + int viewHeight = mCircleRadius + this.getPaddingTop() + this.getPaddingBottom(); + + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + + switch (widthMode) { + case MeasureSpec.EXACTLY: + width = widthSize; + break; + case MeasureSpec.AT_MOST: + case MeasureSpec.UNSPECIFIED: + width = Math.min(viewWidth, widthSize); + break; + default: + width = viewWidth; + break; + } + + if (heightMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.EXACTLY) { + height = heightSize; + } else if (heightMode == MeasureSpec.AT_MOST) { + height = Math.min(viewHeight, heightSize); + } else { + height = viewHeight; + } + + setMeasuredDimension(width, height); + } + + /** + * Use onSizeChanged instead of onAttachedToWindow to get the dimensions of the view, + * because this method is called after measuring the dimensions of MATCH_PARENT & WRAP_CONTENT. + * Use this dimensions to setup the bounds and paints. + */ + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + + setupBounds(w, h); + setupPaints(); + invalidate(); + } + + /** + * Set the properties of the paints we're using to + * draw the progress wheel + */ + private void setupPaints() { + mBarPaint.setColor(mBarColor); + mBarPaint.setAntiAlias(true); + mBarPaint.setStyle(Style.STROKE); + mBarPaint.setStrokeWidth(mBarWidth); + + mRimPaint.setColor(mRimColor); + mRimPaint.setAntiAlias(true); + mRimPaint.setStyle(Style.STROKE); + mRimPaint.setStrokeWidth(mRimWidth); + } + + /** + * Set the bounds of the component + */ + private void setupBounds(int layoutWidth, int layoutHeight) { + int paddingTop = getPaddingTop(); + int paddingBottom = getPaddingBottom(); + int paddingLeft = getPaddingLeft(); + int paddingRight = getPaddingRight(); + + if (!mFillRadius) { + // Width should equal to Height, find the min value to setup the circle + int minValue = Math.min(layoutWidth - paddingLeft - paddingRight, + layoutHeight - paddingBottom - paddingTop); + + int circleDiameter = Math.min(minValue, mCircleRadius * 2 - mBarWidth * 2); + + // Calc the Offset if needed for centering the wheel in the available space + int xOffset = (layoutWidth - paddingLeft - paddingRight - circleDiameter) / 2 + paddingLeft; + int yOffset = (layoutHeight - paddingTop - paddingBottom - circleDiameter) / 2 + paddingTop; + + mCircleBounds = new RectF(xOffset + mBarWidth, yOffset + mBarWidth, xOffset + circleDiameter - mBarWidth, + yOffset + circleDiameter - mBarWidth); + } else { + mCircleBounds = new RectF(paddingLeft + mBarWidth, paddingTop + mBarWidth, + layoutWidth - paddingRight - mBarWidth, layoutHeight - paddingBottom - mBarWidth); + } + } + + public void setCallback(ProgressCallback progressCallback) { + mCallback = progressCallback; + + if (!isSpinning) { + runCallback(); + } + } + + @Override + protected void onDraw(Canvas canvas) { + canvas.drawArc(mCircleBounds, 360, 360, false, mRimPaint); + + boolean mustInvalidate = false; + + if (!mShouldAnimate) { + return; + } + + if (isSpinning) { + //Draw the spinning bar + mustInvalidate = true; + + long deltaTime = (SystemClock.uptimeMillis() - mLastTimeAnimated); + float deltaNormalized = deltaTime * mSpinSpeed / 1000.0f; + + updateBarLength(deltaTime); + + mProgress += deltaNormalized; + if (mProgress > 360) { + mProgress -= 360f; + + // A full turn has been completed + // we run the callback with -1 in case we want to + // do something, like changing the color + runCallback(-1.0f); + } + mLastTimeAnimated = SystemClock.uptimeMillis(); + + float from = mProgress - 90; + float length = BAR_LENGTH + mBarExtraLength; + + if (isInEditMode()) { + from = 0; + length = 135; + } + + canvas.drawArc(mCircleBounds, from, length, false, mBarPaint); + } else { + float oldProgress = mProgress; + + if (mProgress != mTargetProgress) { + //We smoothly increase the progress bar + mustInvalidate = true; + + float deltaTime = (float) (SystemClock.uptimeMillis() - mLastTimeAnimated) / 1000; + float deltaNormalized = deltaTime * mSpinSpeed; + + mProgress = Math.min(mProgress + deltaNormalized, mTargetProgress); + mLastTimeAnimated = SystemClock.uptimeMillis(); + } + + if (oldProgress != mProgress) { + runCallback(); + } + + float offset = 0.0f; + float progress = mProgress; + if (!mLinearProgress) { + float factor = 2.0f; + offset = (float) (1.0f - Math.pow(1.0f - mProgress / 360.0f, 2.0f * factor)) * 360.0f; + progress = (float) (1.0f - Math.pow(1.0f - mProgress / 360.0f, factor)) * 360.0f; + } + + if (isInEditMode()) { + progress = 360; + } + + canvas.drawArc(mCircleBounds, offset - 90, progress, false, mBarPaint); + } + + if (mustInvalidate) { + invalidate(); + } + } + + @Override + protected void onVisibilityChanged(@NonNull View changedView, int visibility) { + super.onVisibilityChanged(changedView, visibility); + + if (visibility == VISIBLE) { + mLastTimeAnimated = SystemClock.uptimeMillis(); + } + } + + private void updateBarLength(long deltaTimeInMilliSeconds) { + if (mPausedTimeWithoutGrowing >= PAUSE_GROWING_TIME) { + mTimeStartGrowing += deltaTimeInMilliSeconds; + + if (mTimeStartGrowing > mBarSpinCycleTime) { + // We completed a size change cycle + // (growing or shrinking) + mTimeStartGrowing -= mBarSpinCycleTime; + //if(mBarGrowingFromFront) { + mPausedTimeWithoutGrowing = 0; + //} + mBarGrowingFromFront = !mBarGrowingFromFront; + } + + float distance = + (float) Math.cos((mTimeStartGrowing / mBarSpinCycleTime + 1) * Math.PI) / 2 + 0.5f; + float destLength = (BAR_MAX_LENGTH - BAR_LENGTH); + + if (mBarGrowingFromFront) { + mBarExtraLength = distance * destLength; + } else { + float newLength = destLength * (1 - distance); + mProgress += (mBarExtraLength - newLength); + mBarExtraLength = newLength; + } + } else { + mPausedTimeWithoutGrowing += deltaTimeInMilliSeconds; + } + } + + /** + * Check if the wheel is currently spinning + */ + + public boolean isSpinning() { + return isSpinning; + } + + /** + * Reset the count (in increment mode) + */ + public void resetCount() { + mProgress = 0.0f; + mTargetProgress = 0.0f; + invalidate(); + } + + /** + * Turn off spin mode + */ + public void stopSpinning() { + isSpinning = false; + mProgress = 0.0f; + mTargetProgress = 0.0f; + invalidate(); + } + + /** + * Puts the view on spin mode + */ + public void spin() { + mLastTimeAnimated = SystemClock.uptimeMillis(); + isSpinning = true; + invalidate(); + } + + private void runCallback(float value) { + if (mCallback != null) { + mCallback.onProgressUpdate(value); + } + } + + private void runCallback() { + if (mCallback != null) { + float normalizedProgress = (float) Math.round(mProgress * 100 / 360.0f) / 100; + mCallback.onProgressUpdate(normalizedProgress); + } + } + + /** + * Set the progress to a specific value, + * the bar will be set instantly to that value + * + * @param progress the progress between 0 and 1 + */ + public void setInstantProgress(float progress) { + if (isSpinning) { + mProgress = 0.0f; + isSpinning = false; + } + + if (progress > 1.0f) { + progress -= 1.0f; + } else if (progress < 0) { + progress = 0; + } + + if (progress == mTargetProgress) { + return; + } + + mTargetProgress = Math.min(progress * 360.0f, 360.0f); + mProgress = mTargetProgress; + mLastTimeAnimated = SystemClock.uptimeMillis(); + invalidate(); + } + + // Great way to save a view's state http://stackoverflow.com/a/7089687/1991053 + @Override + public Parcelable onSaveInstanceState() { + WheelSavedState savedState = new WheelSavedState(super.onSaveInstanceState()); + // We save everything that can be changed at runtime + savedState.mProgress = this.mProgress; + savedState.mTargetProgress = this.mTargetProgress; + savedState.isSpinning = this.isSpinning; + savedState.spinSpeed = this.mSpinSpeed; + savedState.barWidth = this.mBarWidth; + savedState.barColor = this.mBarColor; + savedState.rimWidth = this.mRimWidth; + savedState.rimColor = this.mRimColor; + savedState.circleRadius = this.mCircleRadius; + savedState.linearProgress = this.mLinearProgress; + savedState.fillRadius = this.mFillRadius; + return savedState; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + if (!(state instanceof WheelSavedState)) { + super.onRestoreInstanceState(state); + return; + } + + WheelSavedState savedState = (WheelSavedState) state; + super.onRestoreInstanceState(savedState.getSuperState()); + + this.mProgress = savedState.mProgress; + this.mTargetProgress = savedState.mTargetProgress; + this.isSpinning = savedState.isSpinning; + this.mSpinSpeed = savedState.spinSpeed; + this.mBarWidth = savedState.barWidth; + this.mBarColor = savedState.barColor; + this.mRimWidth = savedState.rimWidth; + this.mRimColor = savedState.rimColor; + this.mCircleRadius = savedState.circleRadius; + this.mLinearProgress = savedState.linearProgress; + this.mFillRadius = savedState.fillRadius; + + this.mLastTimeAnimated = SystemClock.uptimeMillis(); + } + + /** + * @return the current progress between 0.0 and 1.0, + * if the wheel is indeterminate, then the result is -1 + */ + public float getProgress() { + return isSpinning ? -1 : mProgress / 360.0f; + } + + //---------------------------------- + //Getters + setters + //---------------------------------- + + /** + * Set the progress to a specific value, + * the bar will smoothly animate until that value + * + * @param progress the progress between 0 and 1 + */ + public void setProgress(float progress) { + if (isSpinning) { + mProgress = 0.0f; + isSpinning = false; + + runCallback(); + } + + if (progress > 1.0f) { + progress -= 1.0f; + } else if (progress < 0) { + progress = 0; + } + + if (progress == mTargetProgress) { + return; + } + + // If we are currently in the right position + // we set again the last time animated so the + // animation starts smooth from here + if (mProgress == mTargetProgress) { + mLastTimeAnimated = SystemClock.uptimeMillis(); + } + + mTargetProgress = Math.min(progress * 360.0f, 360.0f); + + invalidate(); + } + + /** + * Sets the determinate progress mode + * + * @param isLinear if the progress should increase linearly + */ + public void setLinearProgress(boolean isLinear) { + mLinearProgress = isLinear; + if (!isSpinning) { + invalidate(); + } + } + + /** + * @return the radius of the wheel in pixels + */ + public int getCircleRadius() { + return mCircleRadius; + } + + /** + * Sets the radius of the wheel + * + * @param circleRadius the expected radius, in pixels + */ + public void setCircleRadius(int circleRadius) { + this.mCircleRadius = circleRadius; + if (!isSpinning) { + invalidate(); + } + } + + /** + * @return the width of the spinning bar + */ + public int getBarWidth() { + return mBarWidth; + } + + /** + * Sets the width of the spinning bar + * + * @param barWidth the spinning bar width in pixels + */ + public void setBarWidth(int barWidth) { + this.mBarWidth = barWidth; + if (!isSpinning) { + invalidate(); + } + } + + /** + * @return the color of the spinning bar + */ + public int getBarColor() { + return mBarColor; + } + + /** + * Sets the color of the spinning bar + * + * @param barColor The spinning bar color + */ + public void setBarColor(int barColor) { + this.mBarColor = barColor; + setupPaints(); + if (!isSpinning) { + invalidate(); + } + } + + /** + * @return the color of the wheel's contour + */ + public int getRimColor() { + return mRimColor; + } + + /** + * Sets the color of the wheel's contour + * + * @param rimColor the color for the wheel + */ + public void setRimColor(int rimColor) { + this.mRimColor = rimColor; + setupPaints(); + if (!isSpinning) { + invalidate(); + } + } + + /** + * @return the base spinning speed, in full circle turns per second + * (1.0 equals on full turn in one second), this value also is applied for + * the smoothness when setting a progress + */ + public float getSpinSpeed() { + return mSpinSpeed / 360.0f; + } + + /** + * Sets the base spinning speed, in full circle turns per second + * (1.0 equals on full turn in one second), this value also is applied for + * the smoothness when setting a progress + * + * @param spinSpeed the desired base speed in full turns per second + */ + public void setSpinSpeed(float spinSpeed) { + this.mSpinSpeed = spinSpeed * 360.0f; + } + + /** + * @return the width of the wheel's contour in pixels + */ + public int getRimWidth() { + return mRimWidth; + } + + /** + * Sets the width of the wheel's contour + * + * @param rimWidth the width in pixels + */ + public void setRimWidth(int rimWidth) { + this.mRimWidth = rimWidth; + if (!isSpinning) { + invalidate(); + } + } + + public interface ProgressCallback { + /** + * Method to call when the progress reaches a value + * in order to avoid float precision issues, the progress + * is rounded to a float with two decimals. + *

+ * In indeterminate mode, the callback is called each time + * the wheel completes an animation cycle, with, the progress value is -1.0f + * + * @param progress a double value between 0.00 and 1.00 both included + */ + void onProgressUpdate(float progress); + } + + static class WheelSavedState extends BaseSavedState { + // required field that makes Parcelables from a Parcel + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public WheelSavedState createFromParcel(Parcel in) { + return new WheelSavedState(in); + } + + @Override + public WheelSavedState[] newArray(int size) { + return new WheelSavedState[size]; + } + }; + float mProgress; + float mTargetProgress; + boolean isSpinning; + float spinSpeed; + int barWidth; + int barColor; + int rimWidth; + int rimColor; + int circleRadius; + boolean linearProgress; + boolean fillRadius; + + WheelSavedState(Parcelable superState) { + super(superState); + } + + private WheelSavedState(Parcel in) { + super(in); + this.mProgress = in.readFloat(); + this.mTargetProgress = in.readFloat(); + this.isSpinning = in.readByte() != 0; + this.spinSpeed = in.readFloat(); + this.barWidth = in.readInt(); + this.barColor = in.readInt(); + this.rimWidth = in.readInt(); + this.rimColor = in.readInt(); + this.circleRadius = in.readInt(); + this.linearProgress = in.readByte() != 0; + this.fillRadius = in.readByte() != 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + out.writeFloat(this.mProgress); + out.writeFloat(this.mTargetProgress); + out.writeByte((byte) (isSpinning ? 1 : 0)); + out.writeFloat(this.spinSpeed); + out.writeInt(this.barWidth); + out.writeInt(this.barColor); + out.writeInt(this.rimWidth); + out.writeInt(this.rimColor); + out.writeInt(this.circleRadius); + out.writeByte((byte) (linearProgress ? 1 : 0)); + out.writeByte((byte) (fillRadius ? 1 : 0)); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hjq/demo/widget/XCollapsingToolbarLayout.java b/app/src/main/java/com/hjq/demo/widget/XCollapsingToolbarLayout.java index af5bb9a2..4fa79154 100644 --- a/app/src/main/java/com/hjq/demo/widget/XCollapsingToolbarLayout.java +++ b/app/src/main/java/com/hjq/demo/widget/XCollapsingToolbarLayout.java @@ -1,19 +1,22 @@ package com.hjq.demo.widget; import android.content.Context; -import com.google.android.material.appbar.CollapsingToolbarLayout; import android.util.AttributeSet; +import com.google.android.material.appbar.CollapsingToolbarLayout; + /** * author : Android 轮子哥 * github : https://github.com/getActivity/AndroidProject * time : 2018/10/18 - * desc : 支持监听渐变的CollapsingToolbarLayout + * desc : 支持监听渐变的 CollapsingToolbarLayout */ public final class XCollapsingToolbarLayout extends CollapsingToolbarLayout { - private OnScrimsListener mListener; // 渐变监听 - private boolean isScrimsShown; // 当前渐变状态 + /** 渐变监听 */ + private OnScrimsListener mListener; + /** 当前渐变状态 */ + private boolean isScrimsShown; public XCollapsingToolbarLayout(Context context) { super(context); @@ -35,7 +38,7 @@ public void setScrimsShown(boolean shown, boolean animate) { // 如果是就记录并且回调监听器 isScrimsShown = shown; if (mListener != null) { - mListener.onScrimsStateChange(isScrimsShown); + mListener.onScrimsStateChange(this, isScrimsShown); } } } @@ -50,8 +53,8 @@ public boolean isScrimsShown() { /** * 设置CollapsingToolbarLayout渐变监听 */ - public void setOnScrimsListener(OnScrimsListener l) { - mListener = l; + public void setOnScrimsListener(OnScrimsListener listener) { + mListener = listener; } /** @@ -64,6 +67,6 @@ public interface OnScrimsListener { * * @param shown 渐变开关 */ - void onScrimsStateChange(boolean shown); + void onScrimsStateChange(XCollapsingToolbarLayout layout, boolean shown); } } \ No newline at end of file diff --git a/app/src/main/res/anim/activity_left_in.xml b/app/src/main/res/anim/activity_left_in.xml new file mode 100644 index 00000000..e03efb49 --- /dev/null +++ b/app/src/main/res/anim/activity_left_in.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/app/src/main/res/anim/activity_left_out.xml b/app/src/main/res/anim/activity_left_out.xml new file mode 100644 index 00000000..464168e9 --- /dev/null +++ b/app/src/main/res/anim/activity_left_out.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/app/src/main/res/anim/activity_right_in.xml b/app/src/main/res/anim/activity_right_in.xml new file mode 100644 index 00000000..253a11f3 --- /dev/null +++ b/app/src/main/res/anim/activity_right_in.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/app/src/main/res/anim/activity_right_out.xml b/app/src/main/res/anim/activity_right_out.xml new file mode 100644 index 00000000..912cc356 --- /dev/null +++ b/app/src/main/res/anim/activity_right_out.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/bg_phone_verify.png b/app/src/main/res/drawable-hdpi/bg_phone_verify.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/bg_phone_verify.png rename to app/src/main/res/drawable-hdpi/bg_phone_verify.png diff --git a/app/src/main/res/mipmap-hdpi/ic_head_placeholder.png b/app/src/main/res/drawable-hdpi/ic_head_placeholder.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/ic_head_placeholder.png rename to app/src/main/res/drawable-hdpi/ic_head_placeholder.png diff --git a/app/src/main/res/mipmap-hdpi/ic_small_phone.png b/app/src/main/res/drawable-hdpi/ic_small_phone.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/ic_small_phone.png rename to app/src/main/res/drawable-hdpi/ic_small_phone.png diff --git a/app/src/main/res/mipmap-hdpi/ic_small_safe.png b/app/src/main/res/drawable-hdpi/ic_small_safe.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/ic_small_safe.png rename to app/src/main/res/drawable-hdpi/ic_small_safe.png diff --git a/app/src/main/res/drawable-hdpi/ico_go_black.png b/app/src/main/res/drawable-hdpi/ico_go_black.png new file mode 100644 index 00000000..e3020ab0 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ico_go_black.png differ diff --git a/app/src/main/res/mipmap-hdpi/icon_hint_address.png b/app/src/main/res/drawable-hdpi/icon_hint_address.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/icon_hint_address.png rename to app/src/main/res/drawable-hdpi/icon_hint_address.png diff --git a/app/src/main/res/mipmap-hdpi/icon_hint_empty.png b/app/src/main/res/drawable-hdpi/icon_hint_empty.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/icon_hint_empty.png rename to app/src/main/res/drawable-hdpi/icon_hint_empty.png diff --git a/app/src/main/res/mipmap-hdpi/icon_hint_nerwork.png b/app/src/main/res/drawable-hdpi/icon_hint_nerwork.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/icon_hint_nerwork.png rename to app/src/main/res/drawable-hdpi/icon_hint_nerwork.png diff --git a/app/src/main/res/mipmap-hdpi/icon_hint_request.png b/app/src/main/res/drawable-hdpi/icon_hint_request.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/icon_hint_request.png rename to app/src/main/res/drawable-hdpi/icon_hint_request.png diff --git a/app/src/main/res/mipmap-hdpi/tab_ico_found.png b/app/src/main/res/drawable-hdpi/tab_ico_found.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/tab_ico_found.png rename to app/src/main/res/drawable-hdpi/tab_ico_found.png diff --git a/app/src/main/res/mipmap-hdpi/tab_ico_found_off.png b/app/src/main/res/drawable-hdpi/tab_ico_found_off.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/tab_ico_found_off.png rename to app/src/main/res/drawable-hdpi/tab_ico_found_off.png diff --git a/app/src/main/res/mipmap-hdpi/tab_ico_home.png b/app/src/main/res/drawable-hdpi/tab_ico_home.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/tab_ico_home.png rename to app/src/main/res/drawable-hdpi/tab_ico_home.png diff --git a/app/src/main/res/mipmap-hdpi/tab_ico_home_off.png b/app/src/main/res/drawable-hdpi/tab_ico_home_off.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/tab_ico_home_off.png rename to app/src/main/res/drawable-hdpi/tab_ico_home_off.png diff --git a/app/src/main/res/mipmap-hdpi/tab_ico_me.png b/app/src/main/res/drawable-hdpi/tab_ico_me.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/tab_ico_me.png rename to app/src/main/res/drawable-hdpi/tab_ico_me.png diff --git a/app/src/main/res/mipmap-hdpi/tab_ico_me_off.png b/app/src/main/res/drawable-hdpi/tab_ico_me_off.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/tab_ico_me_off.png rename to app/src/main/res/drawable-hdpi/tab_ico_me_off.png diff --git a/app/src/main/res/mipmap-hdpi/tab_ico_message.png b/app/src/main/res/drawable-hdpi/tab_ico_message.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/tab_ico_message.png rename to app/src/main/res/drawable-hdpi/tab_ico_message.png diff --git a/app/src/main/res/mipmap-hdpi/tab_ico_message_off.png b/app/src/main/res/drawable-hdpi/tab_ico_message_off.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/tab_ico_message_off.png rename to app/src/main/res/drawable-hdpi/tab_ico_message_off.png diff --git a/app/src/main/res/drawable-hdpi/update_app_close.png b/app/src/main/res/drawable-hdpi/update_app_close.png new file mode 100644 index 00000000..4bb3e1f4 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/update_app_close.png 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 new file mode 100644 index 00000000..ea6a0163 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/update_app_top_bg.png differ diff --git a/app/src/main/res/mipmap-xhdpi/bg_phone_verify.png b/app/src/main/res/drawable-xhdpi/bg_phone_verify.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/bg_phone_verify.png rename to app/src/main/res/drawable-xhdpi/bg_phone_verify.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_head_placeholder.png b/app/src/main/res/drawable-xhdpi/ic_head_placeholder.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/ic_head_placeholder.png rename to app/src/main/res/drawable-xhdpi/ic_head_placeholder.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_small_phone.png b/app/src/main/res/drawable-xhdpi/ic_small_phone.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/ic_small_phone.png rename to app/src/main/res/drawable-xhdpi/ic_small_phone.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_small_safe.png b/app/src/main/res/drawable-xhdpi/ic_small_safe.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/ic_small_safe.png rename to app/src/main/res/drawable-xhdpi/ic_small_safe.png diff --git a/app/src/main/res/drawable-xhdpi/ico_go_black.png b/app/src/main/res/drawable-xhdpi/ico_go_black.png new file mode 100644 index 00000000..e9bdd97b Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ico_go_black.png differ diff --git a/app/src/main/res/mipmap-xhdpi/icon_hint_address.png b/app/src/main/res/drawable-xhdpi/icon_hint_address.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/icon_hint_address.png rename to app/src/main/res/drawable-xhdpi/icon_hint_address.png diff --git a/app/src/main/res/mipmap-xhdpi/icon_hint_empty.png b/app/src/main/res/drawable-xhdpi/icon_hint_empty.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/icon_hint_empty.png rename to app/src/main/res/drawable-xhdpi/icon_hint_empty.png diff --git a/app/src/main/res/mipmap-xhdpi/icon_hint_nerwork.png b/app/src/main/res/drawable-xhdpi/icon_hint_nerwork.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/icon_hint_nerwork.png rename to app/src/main/res/drawable-xhdpi/icon_hint_nerwork.png diff --git a/app/src/main/res/mipmap-xhdpi/icon_hint_request.png b/app/src/main/res/drawable-xhdpi/icon_hint_request.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/icon_hint_request.png rename to app/src/main/res/drawable-xhdpi/icon_hint_request.png diff --git a/app/src/main/res/mipmap-xhdpi/tab_ico_found.png b/app/src/main/res/drawable-xhdpi/tab_ico_found.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/tab_ico_found.png rename to app/src/main/res/drawable-xhdpi/tab_ico_found.png diff --git a/app/src/main/res/mipmap-xhdpi/tab_ico_found_off.png b/app/src/main/res/drawable-xhdpi/tab_ico_found_off.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/tab_ico_found_off.png rename to app/src/main/res/drawable-xhdpi/tab_ico_found_off.png diff --git a/app/src/main/res/mipmap-xhdpi/tab_ico_home.png b/app/src/main/res/drawable-xhdpi/tab_ico_home.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/tab_ico_home.png rename to app/src/main/res/drawable-xhdpi/tab_ico_home.png diff --git a/app/src/main/res/mipmap-xhdpi/tab_ico_home_off.png b/app/src/main/res/drawable-xhdpi/tab_ico_home_off.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/tab_ico_home_off.png rename to app/src/main/res/drawable-xhdpi/tab_ico_home_off.png diff --git a/app/src/main/res/mipmap-xhdpi/tab_ico_me.png b/app/src/main/res/drawable-xhdpi/tab_ico_me.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/tab_ico_me.png rename to app/src/main/res/drawable-xhdpi/tab_ico_me.png diff --git a/app/src/main/res/mipmap-xhdpi/tab_ico_me_off.png b/app/src/main/res/drawable-xhdpi/tab_ico_me_off.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/tab_ico_me_off.png rename to app/src/main/res/drawable-xhdpi/tab_ico_me_off.png diff --git a/app/src/main/res/mipmap-xhdpi/tab_ico_message.png b/app/src/main/res/drawable-xhdpi/tab_ico_message.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/tab_ico_message.png rename to app/src/main/res/drawable-xhdpi/tab_ico_message.png diff --git a/app/src/main/res/mipmap-xhdpi/tab_ico_message_off.png b/app/src/main/res/drawable-xhdpi/tab_ico_message_off.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/tab_ico_message_off.png rename to app/src/main/res/drawable-xhdpi/tab_ico_message_off.png diff --git a/app/src/main/res/mipmap-xhdpi/update_app_close.png b/app/src/main/res/drawable-xhdpi/update_app_close.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/update_app_close.png rename to app/src/main/res/drawable-xhdpi/update_app_close.png diff --git a/app/src/main/res/mipmap-xhdpi/update_app_top_bg.png b/app/src/main/res/drawable-xhdpi/update_app_top_bg.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/update_app_top_bg.png rename to app/src/main/res/drawable-xhdpi/update_app_top_bg.png diff --git a/app/src/main/res/drawable-xxhdpi/bg_launcher.jpg b/app/src/main/res/drawable-xxhdpi/bg_launcher.jpg deleted file mode 100644 index b3dfd10e..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/bg_launcher.jpg and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/bg_phone_verify.png b/app/src/main/res/drawable-xxhdpi/bg_phone_verify.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/bg_phone_verify.png rename to app/src/main/res/drawable-xxhdpi/bg_phone_verify.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_head_placeholder.png b/app/src/main/res/drawable-xxhdpi/ic_head_placeholder.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/ic_head_placeholder.png rename to app/src/main/res/drawable-xxhdpi/ic_head_placeholder.png diff --git a/app/src/main/res/drawable-xxhdpi/ic_logo.png b/app/src/main/res/drawable-xxhdpi/ic_logo.png deleted file mode 100644 index 48c1cd53..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_logo.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_small_phone.png b/app/src/main/res/drawable-xxhdpi/ic_small_phone.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/ic_small_phone.png rename to app/src/main/res/drawable-xxhdpi/ic_small_phone.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_small_safe.png b/app/src/main/res/drawable-xxhdpi/ic_small_safe.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/ic_small_safe.png rename to app/src/main/res/drawable-xxhdpi/ic_small_safe.png diff --git a/app/src/main/res/mipmap-xxhdpi/ico_go_black.png b/app/src/main/res/drawable-xxhdpi/ico_go_black.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/ico_go_black.png rename to app/src/main/res/drawable-xxhdpi/ico_go_black.png diff --git a/app/src/main/res/mipmap-xxhdpi/icon_hint_address.png b/app/src/main/res/drawable-xxhdpi/icon_hint_address.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/icon_hint_address.png rename to app/src/main/res/drawable-xxhdpi/icon_hint_address.png diff --git a/app/src/main/res/mipmap-xxhdpi/icon_hint_empty.png b/app/src/main/res/drawable-xxhdpi/icon_hint_empty.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/icon_hint_empty.png rename to app/src/main/res/drawable-xxhdpi/icon_hint_empty.png diff --git a/app/src/main/res/mipmap-xxhdpi/icon_hint_nerwork.png b/app/src/main/res/drawable-xxhdpi/icon_hint_nerwork.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/icon_hint_nerwork.png rename to app/src/main/res/drawable-xxhdpi/icon_hint_nerwork.png diff --git a/app/src/main/res/mipmap-xxhdpi/icon_hint_request.png b/app/src/main/res/drawable-xxhdpi/icon_hint_request.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/icon_hint_request.png rename to app/src/main/res/drawable-xxhdpi/icon_hint_request.png diff --git a/app/src/main/res/mipmap-xxhdpi/tab_ico_found.png b/app/src/main/res/drawable-xxhdpi/tab_ico_found.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/tab_ico_found.png rename to app/src/main/res/drawable-xxhdpi/tab_ico_found.png diff --git a/app/src/main/res/mipmap-xxhdpi/tab_ico_found_off.png b/app/src/main/res/drawable-xxhdpi/tab_ico_found_off.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/tab_ico_found_off.png rename to app/src/main/res/drawable-xxhdpi/tab_ico_found_off.png diff --git a/app/src/main/res/mipmap-xxhdpi/tab_ico_home.png b/app/src/main/res/drawable-xxhdpi/tab_ico_home.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/tab_ico_home.png rename to app/src/main/res/drawable-xxhdpi/tab_ico_home.png diff --git a/app/src/main/res/mipmap-xxhdpi/tab_ico_home_off.png b/app/src/main/res/drawable-xxhdpi/tab_ico_home_off.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/tab_ico_home_off.png rename to app/src/main/res/drawable-xxhdpi/tab_ico_home_off.png diff --git a/app/src/main/res/mipmap-xxhdpi/tab_ico_me.png b/app/src/main/res/drawable-xxhdpi/tab_ico_me.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/tab_ico_me.png rename to app/src/main/res/drawable-xxhdpi/tab_ico_me.png diff --git a/app/src/main/res/mipmap-xxhdpi/tab_ico_me_off.png b/app/src/main/res/drawable-xxhdpi/tab_ico_me_off.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/tab_ico_me_off.png rename to app/src/main/res/drawable-xxhdpi/tab_ico_me_off.png diff --git a/app/src/main/res/mipmap-xxhdpi/tab_ico_message.png b/app/src/main/res/drawable-xxhdpi/tab_ico_message.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/tab_ico_message.png rename to app/src/main/res/drawable-xxhdpi/tab_ico_message.png diff --git a/app/src/main/res/mipmap-xxhdpi/tab_ico_message_off.png b/app/src/main/res/drawable-xxhdpi/tab_ico_message_off.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/tab_ico_message_off.png rename to app/src/main/res/drawable-xxhdpi/tab_ico_message_off.png diff --git a/app/src/main/res/drawable-xxhdpi/update_app_close.png b/app/src/main/res/drawable-xxhdpi/update_app_close.png new file mode 100644 index 00000000..27fc1eef Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/update_app_close.png 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 new file mode 100644 index 00000000..ddbee796 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/update_app_top_bg.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_custom_dialog.webp b/app/src/main/res/drawable-xxxhdpi/bg_custom_dialog.webp new file mode 100644 index 00000000..93731e50 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_custom_dialog.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_launcher.webp b/app/src/main/res/drawable-xxxhdpi/bg_launcher.webp new file mode 100644 index 00000000..2cc55f47 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_launcher.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_custom_dialog.webp b/app/src/main/res/drawable-xxxhdpi/ic_custom_dialog.webp new file mode 100644 index 00000000..44ce2af7 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_custom_dialog.webp differ diff --git a/dialog/src/main/res/mipmap-xxhdpi/ic_dialog_close.png b/app/src/main/res/drawable-xxxhdpi/ic_dialog_close.png similarity index 100% rename from dialog/src/main/res/mipmap-xxhdpi/ic_dialog_close.png rename to app/src/main/res/drawable-xxxhdpi/ic_dialog_close.png diff --git a/app/src/main/res/drawable-xxxhdpi/ic_dialog_delete.webp b/app/src/main/res/drawable-xxxhdpi/ic_dialog_delete.webp new file mode 100644 index 00000000..99bd8ca7 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_dialog_delete.webp differ diff --git a/dialog/src/main/res/mipmap-xxhdpi/ic_dialog_error.png b/app/src/main/res/drawable-xxxhdpi/ic_dialog_error.png similarity index 100% rename from dialog/src/main/res/mipmap-xxhdpi/ic_dialog_error.png rename to app/src/main/res/drawable-xxxhdpi/ic_dialog_error.png diff --git a/dialog/src/main/res/mipmap-xxhdpi/ic_dialog_error_dark.png b/app/src/main/res/drawable-xxxhdpi/ic_dialog_error_dark.png similarity index 100% rename from dialog/src/main/res/mipmap-xxhdpi/ic_dialog_error_dark.png rename to app/src/main/res/drawable-xxxhdpi/ic_dialog_error_dark.png diff --git a/dialog/src/main/res/mipmap-xxhdpi/ic_dialog_finish.png b/app/src/main/res/drawable-xxxhdpi/ic_dialog_finish.png similarity index 100% rename from dialog/src/main/res/mipmap-xxhdpi/ic_dialog_finish.png rename to app/src/main/res/drawable-xxxhdpi/ic_dialog_finish.png diff --git a/dialog/src/main/res/mipmap-xxhdpi/ic_dialog_finish_dark.png b/app/src/main/res/drawable-xxxhdpi/ic_dialog_finish_dark.png similarity index 100% rename from dialog/src/main/res/mipmap-xxhdpi/ic_dialog_finish_dark.png rename to app/src/main/res/drawable-xxxhdpi/ic_dialog_finish_dark.png diff --git a/dialog/src/main/res/mipmap-xxhdpi/ic_dialog_warning.png b/app/src/main/res/drawable-xxxhdpi/ic_dialog_warning.png similarity index 100% rename from dialog/src/main/res/mipmap-xxhdpi/ic_dialog_warning.png rename to app/src/main/res/drawable-xxxhdpi/ic_dialog_warning.png diff --git a/dialog/src/main/res/mipmap-xxhdpi/ic_dialog_warning_dark.png b/app/src/main/res/drawable-xxxhdpi/ic_dialog_warning_dark.png similarity index 100% rename from dialog/src/main/res/mipmap-xxhdpi/ic_dialog_warning_dark.png rename to app/src/main/res/drawable-xxxhdpi/ic_dialog_warning_dark.png diff --git a/app/src/main/res/drawable-xxxhdpi/ic_login_qq.webp b/app/src/main/res/drawable-xxxhdpi/ic_login_qq.webp new file mode 100644 index 00000000..58361575 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_login_qq.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_login_wx.webp b/app/src/main/res/drawable-xxxhdpi/ic_login_wx.webp new file mode 100644 index 00000000..6e15d9ce Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_login_wx.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_logo.webp b/app/src/main/res/drawable-xxxhdpi/ic_logo.webp new file mode 100644 index 00000000..7a7ccd2f Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_logo.webp differ diff --git a/dialog/src/main/res/drawable/dialog_input_text_bg.xml b/app/src/main/res/drawable/bg_dialog_input.xml similarity index 57% rename from dialog/src/main/res/drawable/dialog_input_text_bg.xml rename to app/src/main/res/drawable/bg_dialog_input.xml index 78bcfcff..1a7c53b3 100644 --- a/dialog/src/main/res/drawable/dialog_input_text_bg.xml +++ b/app/src/main/res/drawable/bg_dialog_input.xml @@ -3,10 +3,9 @@ android:shape="rectangle"> + android:width="@dimen/line_size" + android:color="#4A4A4A" /> - + \ No newline at end of file diff --git a/dialog/src/main/res/drawable/dialog_rounded_corner_bg.xml b/app/src/main/res/drawable/bg_dialog_rounded_corner.xml similarity index 100% rename from dialog/src/main/res/drawable/dialog_rounded_corner_bg.xml rename to app/src/main/res/drawable/bg_dialog_rounded_corner.xml diff --git a/app/src/main/res/drawable/bg_home_search_bar_gray.xml b/app/src/main/res/drawable/bg_home_search_bar_gray.xml index 63702a88..3a2cbd47 100644 --- a/app/src/main/res/drawable/bg_home_search_bar_gray.xml +++ b/app/src/main/res/drawable/bg_home_search_bar_gray.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_home_search_bar_transparent.xml b/app/src/main/res/drawable/bg_home_search_bar_transparent.xml index 5c90bf40..32f507e4 100644 --- a/app/src/main/res/drawable/bg_home_search_bar_transparent.xml +++ b/app/src/main/res/drawable/bg_home_search_bar_transparent.xml @@ -1,9 +1,9 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_web_progress_bar.xml b/app/src/main/res/drawable/bg_webview_progress_bar.xml similarity index 83% rename from app/src/main/res/drawable/bg_web_progress_bar.xml rename to app/src/main/res/drawable/bg_webview_progress_bar.xml index 64979446..b6a716ac 100644 --- a/app/src/main/res/drawable/bg_web_progress_bar.xml +++ b/app/src/main/res/drawable/bg_webview_progress_bar.xml @@ -1,26 +1,26 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + \ 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 new file mode 100644 index 00000000..6072bcc4 --- /dev/null +++ b/app/src/main/res/drawable/ic_checkbox_checked.xml @@ -0,0 +1,9 @@ + + + \ 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 new file mode 100644 index 00000000..0b95607e --- /dev/null +++ b/app/src/main/res/drawable/ic_checkbox_disable.xml @@ -0,0 +1,10 @@ + + + \ 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 new file mode 100644 index 00000000..0f7192ad --- /dev/null +++ b/app/src/main/res/drawable/ic_checkbox_normal.xml @@ -0,0 +1,9 @@ + + + \ 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 new file mode 100644 index 00000000..e2d7c079 --- /dev/null +++ b/app/src/main/res/drawable/ic_photo_camera.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_photo_succeed.xml b/app/src/main/res/drawable/ic_photo_succeed.xml new file mode 100644 index 00000000..12e90df5 --- /dev/null +++ b/app/src/main/res/drawable/ic_photo_succeed.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_share_link.xml b/app/src/main/res/drawable/ic_share_link.xml new file mode 100644 index 00000000..748f6daa --- /dev/null +++ b/app/src/main/res/drawable/ic_share_link.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_share_moment.xml b/app/src/main/res/drawable/ic_share_moment.xml new file mode 100644 index 00000000..54cb5a88 --- /dev/null +++ b/app/src/main/res/drawable/ic_share_moment.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_share_qq.xml b/app/src/main/res/drawable/ic_share_qq.xml new file mode 100644 index 00000000..6ffba041 --- /dev/null +++ b/app/src/main/res/drawable/ic_share_qq.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_share_qzone.xml b/app/src/main/res/drawable/ic_share_qzone.xml new file mode 100644 index 00000000..cac566af --- /dev/null +++ b/app/src/main/res/drawable/ic_share_qzone.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_share_wechat.xml b/app/src/main/res/drawable/ic_share_wechat.xml new file mode 100644 index 00000000..908bed90 --- /dev/null +++ b/app/src/main/res/drawable/ic_share_wechat.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_checkbox.xml b/app/src/main/res/drawable/selector_checkbox.xml new file mode 100644 index 00000000..2cdcf993 --- /dev/null +++ b/app/src/main/res/drawable/selector_checkbox.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_countdown.xml b/app/src/main/res/drawable/selector_countdown.xml new file mode 100644 index 00000000..a077bf50 --- /dev/null +++ b/app/src/main/res/drawable/selector_countdown.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_countdown_color.xml b/app/src/main/res/drawable/selector_countdown_color.xml new file mode 100644 index 00000000..f0f83cdf --- /dev/null +++ b/app/src/main/res/drawable/selector_countdown_color.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_selector_selectable_home_message.xml b/app/src/main/res/drawable/selector_home_found.xml similarity index 59% rename from app/src/main/res/drawable/ic_selector_selectable_home_message.xml rename to app/src/main/res/drawable/selector_home_found.xml index ffad59e4..c63e5c1e 100644 --- a/app/src/main/res/drawable/ic_selector_selectable_home_message.xml +++ b/app/src/main/res/drawable/selector_home_found.xml @@ -1,10 +1,10 @@ - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_selector_selectable_home_me.xml b/app/src/main/res/drawable/selector_home_index.xml similarity index 54% rename from app/src/main/res/drawable/ic_selector_selectable_home_me.xml rename to app/src/main/res/drawable/selector_home_index.xml index 564f1b85..3350309c 100644 --- a/app/src/main/res/drawable/ic_selector_selectable_home_me.xml +++ b/app/src/main/res/drawable/selector_home_index.xml @@ -1,10 +1,10 @@ - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_selector_selectable_home_index.xml b/app/src/main/res/drawable/selector_home_me.xml similarity index 59% rename from app/src/main/res/drawable/ic_selector_selectable_home_index.xml rename to app/src/main/res/drawable/selector_home_me.xml index 5838d4f4..8982add8 100644 --- a/app/src/main/res/drawable/ic_selector_selectable_home_index.xml +++ b/app/src/main/res/drawable/selector_home_me.xml @@ -1,10 +1,10 @@ - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_selector_selectable_home_found.xml b/app/src/main/res/drawable/selector_home_message.xml similarity index 53% rename from app/src/main/res/drawable/ic_selector_selectable_home_found.xml rename to app/src/main/res/drawable/selector_home_message.xml index 7dc5b4e3..342df43d 100644 --- a/app/src/main/res/drawable/ic_selector_selectable_home_found.xml +++ b/app/src/main/res/drawable/selector_home_message.xml @@ -1,10 +1,10 @@ - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/txt_home_bottom_nav_selector.xml b/app/src/main/res/drawable/selector_home_navigation_color.xml similarity index 60% rename from app/src/main/res/drawable/txt_home_bottom_nav_selector.xml rename to app/src/main/res/drawable/selector_home_navigation_color.xml index eb2a5444..aa66a679 100644 --- a/app/src/main/res/drawable/txt_home_bottom_nav_selector.xml +++ b/app/src/main/res/drawable/selector_home_navigation_color.xml @@ -1,10 +1,10 @@ - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_selectable_transparent.xml b/app/src/main/res/drawable/selector_selectable_transparent.xml deleted file mode 100644 index 362a89f2..00000000 --- a/app/src/main/res/drawable/selector_selectable_transparent.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_transparent.xml b/app/src/main/res/drawable/selector_transparent.xml new file mode 100644 index 00000000..e10d84f0 --- /dev/null +++ b/app/src/main/res/drawable/selector_transparent.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_dialog_update_bottom.xml b/app/src/main/res/drawable/selector_update_bottom.xml similarity index 78% rename from app/src/main/res/drawable/bg_dialog_update_bottom.xml rename to app/src/main/res/drawable/selector_update_bottom.xml index 9bb90422..4abd0b3a 100644 --- a/app/src/main/res/drawable/bg_dialog_update_bottom.xml +++ b/app/src/main/res/drawable/selector_update_bottom.xml @@ -4,7 +4,7 @@ + android:topLeftRadius="0px" + android:topRightRadius="0px" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_dialog_update_button.xml b/app/src/main/res/drawable/selector_update_button.xml similarity index 100% rename from app/src/main/res/drawable/bg_dialog_update_button.xml rename to app/src/main/res/drawable/selector_update_button.xml diff --git a/app/src/main/res/drawable/selector_selectable_white.xml b/app/src/main/res/drawable/selector_white.xml similarity index 72% rename from app/src/main/res/drawable/selector_selectable_white.xml rename to app/src/main/res/drawable/selector_white.xml index 8ba591e0..c378a12a 100644 --- a/app/src/main/res/drawable/selector_selectable_white.xml +++ b/app/src/main/res/drawable/selector_white.xml @@ -1,9 +1,17 @@ - - - - - - - - + + + + + + + + + + + + + + + + \ 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 ce38912e..dc30af74 100644 --- a/app/src/main/res/layout/activity_about.xml +++ b/app/src/main/res/layout/activity_about.xml @@ -9,52 +9,52 @@ tools:context=".ui.activity.AboutActivity"> - + - + android:textSize="18sp" /> - + android:textSize="14sp" /> - + android:textSize="14sp" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_copy.xml b/app/src/main/res/layout/activity_copy.xml index 90cfc658..e155dfaf 100644 --- a/app/src/main/res/layout/activity_copy.xml +++ b/app/src/main/res/layout/activity_copy.xml @@ -8,7 +8,6 @@ tools:context=".ui.activity.CopyActivity"> diff --git a/app/src/main/res/layout/activity_crash.xml b/app/src/main/res/layout/activity_crash.xml new file mode 100644 index 00000000..91062e98 --- /dev/null +++ b/app/src/main/res/layout/activity_crash.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_dialog.xml b/app/src/main/res/layout/activity_dialog.xml index 04043064..4fcb8e62 100644 --- a/app/src/main/res/layout/activity_dialog.xml +++ b/app/src/main/res/layout/activity_dialog.xml @@ -5,10 +5,10 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - tools:context=".ui.activity.DialogActivity"> + tools:context=".ui.activity.DialogActivity" + tools:ignore="HardcodedText"> @@ -20,121 +20,129 @@ -