diff --git a/.gitignore b/.gitignore index 39fb081..2a42e3e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,20 @@ *.iml .gradle /local.properties -/.idea/workspace.xml +/.idea/caches /.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +/.idea/ +/.idea/misc.xml .DS_Store /build /captures .externalNativeBuild +.cxx +*.apk +*.ap_ +*.dex +*.class diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..0d15693 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,134 @@ + + + + + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+
+
\ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index f9330ab..08809d1 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,10 +1,13 @@ + diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 70540aa..6d07175 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,6 +1,52 @@ \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 9ac5bbb..360a949 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -2,47 +2,51 @@ - + diff --git a/.idea/modules.xml b/.idea/modules.xml index 3f19e18..0f2e166 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,13 +2,14 @@ - - - - - - - + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 7ff70a8..9358faf 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,14 @@ # AndroidModuleDemo -基于MVP搭建的组件化开发框架
+基于MVP搭建的组件化开发框架 ,[详细介绍](https://www.jianshu.com/p/00746e6fb48a)
# 效果图 -
+
-## 主要用于学习android组件化的学习, +## 主要用于学习android组件化框架 ## 封装了工作中常用的一些控件,基类等,可以做到快速开发 +
+电商 商品详情 +
+
+ +~ diff --git a/Screenshot/1.gif b/Screenshot/1.gif deleted file mode 100644 index dc077d2..0000000 Binary files a/Screenshot/1.gif and /dev/null differ diff --git a/Screenshot/1.png b/Screenshot/1.png new file mode 100644 index 0000000..0401f08 Binary files /dev/null and b/Screenshot/1.png differ diff --git a/Screenshot/2.png b/Screenshot/2.png index 89069c1..dfa949a 100644 Binary files a/Screenshot/2.png and b/Screenshot/2.png differ diff --git a/Screenshot/4.png b/Screenshot/4.png new file mode 100644 index 0000000..f8b91db Binary files /dev/null and b/Screenshot/4.png differ diff --git a/app/build.gradle b/app/build.gradle index e7daf42..357bfc5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,26 @@ apply plugin: 'com.android.application' +/** + * 打包时间 + */ +static String releaseTime() { + return "\"" + new Date().format("yyyy-MM-dd HH:mm") + "\"" +} + +/** + * 根据build时间自动生成versionCode + */ +static def createVersionCode() { + def year = new Date().format("yy") + def month = new Date().format("MM") + def other = new Date().format("ddHHmm") + //年份-1 月份+12 + year = year.toInteger() - 1 + month = month.toInteger() + 12 + def code = year.toString() + month.toString() + other + return code.toInteger(); +} + android { compileSdkVersion rootProject.ext.versions.compileSdkVersion buildToolsVersion rootProject.ext.versions.buildToolsVersion @@ -7,48 +28,115 @@ android { defaultConfig { minSdkVersion rootProject.ext.versions.minSdkVersion targetSdkVersion rootProject.ext.versions.targetSdkVersion - versionCode rootProject.ext.versions.versionCode - versionName rootProject.ext.versions.versionName - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - //MultiDex分包方法 - multiDexEnabled true - - //Arouter路由配置 - javaCompileOptions { - annotationProcessorOptions { - arguments = [moduleName: project.getName()] - includeCompileClasspath = true + + signingConfigs { + config { + keyAlias 'amd' + keyPassword '123456' + storeFile file('../key/amd.jsk') + storePassword '123456' + } + } + + defaultConfig { + minSdkVersion rootProject.ext.versions.minSdkVersion + targetSdkVersion rootProject.ext.versions.targetSdkVersion + versionCode createVersionCode() + multiDexEnabled true + signingConfig signingConfigs.config + + ndk { + abiFilters "armeabi-v7a" + } + + javaCompileOptions { + annotationProcessorOptions { + arguments = [AROUTER_MODULE_NAME: project.getName()] + includeCompileClasspath = true + } } } - //GreenDao -// greendao { -// schemaVersion 1 -// } } + buildTypes { release { + lintOptions { + checkReleaseBuilds false + abortOnError false + } + //不使用混淆 minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } -} + compileOptions { + targetCompatibility rootProject.ext.versions.javaSDKVersion + sourceCompatibility rootProject.ext.versions.javaSDKVersion + } + + dexOptions { + javaMaxHeapSize "5g" + } + repositories { + flatDir { + dirs project(':common_base').file('libs') + } + } + //多维度标签 + flavorDimensions "default" + //多渠道配置 + productFlavors { + //开发 + DEV { + dimension "default" + versionName rootProject.ext.versions.versionNameDEV + applicationId rootProject.ext.versions.applicationIdDEV + resValue "string", "app_name", "AMD_DEV" + buildConfigField("String", "RELEASE_TIME", releaseTime()) + manifestPlaceholders = [ + //7.0文件权限 + FILE_PROVIDER: applicationId, + ] + } + + //生产 + PRO { + dimension "default" + versionName rootProject.ext.versions.versionName + applicationId rootProject.ext.versions.applicationId + resValue "string", "app_name", "AMD" + buildConfigField("String", "RELEASE_TIME", releaseTime()) + manifestPlaceholders = [ + //7.0文件权限 + FILE_PROVIDER: applicationId, + ] + } + } + android.applicationVariants.all { variant -> + variant.outputs.all { output -> + outputFileName = "AMD_v${variant.versionName}_${variant.productFlavors[0].name}_${variant.buildType.name}.apk" + } + } -configurations.all { - resolutionStrategy.force 'com.android.support:support-annotations:27.1.1' } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - annotationProcessor rootProject.ext.dependencies["butterknife_compiler"] + annotationProcessor rootProject.ext.dependencies["butterknifeCompiler"] //公用依赖包 implementation project(':common_base') if (!Boolean.valueOf(rootProject.ext.isModule)) { + //main模块 implementation project(':module_main') + //商城模块 implementation project(':module_market') + //用户模块 + implementation project(':module_user') + //玩Android模块 implementation project(':module_wan_android') } -} +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index f1b4245..c77405b 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -19,3 +19,178 @@ # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile + +#-------------------------------------------定制化区域---------------------------------------------- + +#---------------------------------1.实体类--------------------------------- +-keep class com.wss.common.base.bean.** {*;} +-keep public class * extends com.wss.common.base.bean + +#------------------------------------------------------------------------- + +#---------------------------------2.第三方包-------------------------------- +#ARouter +-keep public class com.alibaba.android.arouter.routes.**{*;} +-keep public class com.alibaba.android.arouter.facade.**{*;} +-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;} + +# If you use the byType method to obtain Service, add the following rules to protect the interface: +#-keep interface * implements com.alibaba.android.arouter.facade.template.IProvider + +# If single-type injection is used, that is, no interface is defined to implement IProvider, +#the following rules need to be added to protect the implementation +#-keep class * implements com.alibaba.android.arouter.facade.template.IProvider + +#butterknife 7.0+ +-keep class butterknife.** {*;} +-dontwarn butterknife.internal.** +-keep class **$$ViewBinder {*;} +-keepclasseswithmembernames class * { + @butterknife.* ; +} +-keepclasseswithmembernames class * { + @butterknife.* ; +} +-keepclasseswithmembernames class * { + @butterknife.* ; +} + +#EventBus 3.0+ +-keepattributes *Annotation* +-keepclassmembers class * { + @org.greenrobot.eventbus.Subscribe ; +} +-keep enum org.greenrobot.eventbus.ThreadMode {*;} + +# Only required if you use AsyncExecutor +#-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent { +# (java.lang.Throwable); +#} + +# novate +-keep class com.tamic.novate.** {*;} + +#fastjson +-keepattributes Signature +-dontwarn com.alibaba.fastjson.** +-keep class com.alibaba.fastjson.**{*;} + +#immersionbar 沉浸栏 +-keep class com.gyf.immersionbar.* {*;} + +# banner +-keep class com.youth.banner.** {*;} + +# picasso +-dontwarn com.squareup.okhttp.** + +#GreenDao 3.2+ +-keep class org.greenrobot.greendao.**{*;} +-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao { + public static java.lang.String TABLENAME; + } +-keep class **$Properties + + + + + + + +#------------------------------------------------------------------------- + +#---------------------------------3.与js互相调用的类------------------------ + + + +#------------------------------------------------------------------------- + +#---------------------------------4.反射相关的类和方法----------------------- + + + +#---------------------------------------------------------------------------- +#--------------------------------------------------------------------------------------------------- + + +#-------------------------------------------基本不用动区域-------------------------------------------- +#---------------------------------基本指令区---------------------------------- +-optimizationpasses 5 +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-dontskipnonpubliclibraryclassmembers +-dontpreverify +-verbose +-printmapping proguardMapping.txt +-optimizations !code/simplification/cast,!field/*,!class/merging/* +-keepattributes *Annotation*,InnerClasses +-keepattributes Signature +-keepattributes SourceFile,LineNumberTable +#---------------------------------------------------------------------------- + +#---------------------------------默认保留区--------------------------------- +-keep public class * extends android.app.Activity +-keep public class * extends android.support.v4.app.FragmentActivity +-keep public class * extends android.app.Application +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class * extends android.app.backup.BackupAgentHelper +-keep public class * extends android.preference.Preference +-keep public class * extends android.view.View +-keep public class com.android.vending.licensing.ILicensingService +-keep class android.support.** {*;} + +-keepclasseswithmembernames class * { + native ; +} +-keepclassmembers class * extends android.app.Activity{ + public void *(android.view.View); +} +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} +-keep public class * extends android.view.View{ + *** get*(); + void set*(***); + public (android.content.Context); + public (android.content.Context, android.util.AttributeSet); + public (android.content.Context, android.util.AttributeSet, int); +} +-keepclasseswithmembers class * { + public (android.content.Context, android.util.AttributeSet); + public (android.content.Context, android.util.AttributeSet, int); +} +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} +-keepclassmembers class * implements java.io.Serializable { + static final long serialVersionUID; + private static final java.io.ObjectStreamField[] serialPersistentFields; + private void writeObject(java.io.ObjectOutputStream); + private void readObject(java.io.ObjectInputStream); + java.lang.Object writeReplace(); + java.lang.Object readResolve(); +} +-keep class **.R$* { + *; +} +-keepclassmembers class * { + void *(**On*Event); +} +#---------------------------------------------------------------------------- + +#---------------------------------webview------------------------------------ +-keepclassmembers class fqcn.of.javascript.interface.for.Webview { + public *; +} +-keepclassmembers class * extends android.webkit.WebViewClient { + public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap); + public boolean *(android.webkit.WebView, java.lang.String); +} +-keepclassmembers class * extends android.webkit.WebViewClient { + public void *(android.webkit.WebView, jav.lang.String); +} +#---------------------------------------------------------------------------- +#--------------------------------------------------------------------------------------------------- diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index eae4fc2..55ada32 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -10,6 +10,20 @@ android:supportsRtl="true" android:theme="@style/AdmTheme"> + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/Test.html b/app/src/main/assets/Test.html new file mode 100644 index 0000000..b38a6f4 --- /dev/null +++ b/app/src/main/assets/Test.html @@ -0,0 +1,24 @@ + + + test page + + + + +获取分辨率 +
+ + \ No newline at end of file diff --git a/app/src/main/java/com/wss/amd/app/AppApplication.java b/app/src/main/java/com/wss/amd/app/AppApplication.java index e1951e4..6b49668 100644 --- a/app/src/main/java/com/wss/amd/app/AppApplication.java +++ b/app/src/main/java/com/wss/amd/app/AppApplication.java @@ -1,12 +1,19 @@ package com.wss.amd.app; +import com.wss.amd.profile.ProfileFactory; import com.wss.common.base.BaseApplication; +import com.wss.common.profile.ProfileManager; /** - * Describe: + * Describe: Application * Created by 吴天强 on 2018/10/15. */ - public class AppApplication extends BaseApplication { + @Override + public void onCreate() { + super.onCreate(); + ProfileManager.inst.factory(new ProfileFactory()); + + } } diff --git a/app/src/main/java/com/wss/amd/main/LoadingActivity.java b/app/src/main/java/com/wss/amd/main/LoadingActivity.java new file mode 100644 index 0000000..06e3aea --- /dev/null +++ b/app/src/main/java/com/wss/amd/main/LoadingActivity.java @@ -0,0 +1,41 @@ +package com.wss.amd.main; + +import android.os.Bundle; +import android.os.Handler; +import android.view.WindowManager; +import android.widget.TextView; + +import com.wss.amd.R; +import com.wss.common.base.BaseFullScreenActivity; +import com.wss.common.base.mvp.BasePresenter; +import com.wss.common.manage.ActivityToActivity; +import com.wss.common.utils.Utils; +import com.wss.module.main.ui.main.MainActivity; + +import androidx.annotation.Nullable; + +/** + * Describe:应用启动页 + * Created by 吴天强 on 2018/10/16. + */ +public class LoadingActivity extends BaseFullScreenActivity { + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_loading); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + new Handler().postDelayed(() -> { + ActivityToActivity.toActivity(this, MainActivity.class); + }, 1200); + + TextView tvVersion = findViewById(R.id.tv_version); + tvVersion.setText(String.format("V%s(%s)", Utils.getVersionName(), Utils.getVersionCode())); + } + + @Override + protected BasePresenter createPresenter() { + return null; + } + +} diff --git a/app/src/main/java/com/wss/amd/note/NoteTest.java b/app/src/main/java/com/wss/amd/note/NoteTest.java new file mode 100644 index 0000000..3916479 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/NoteTest.java @@ -0,0 +1,371 @@ +package com.wss.amd.note; + +import com.wss.amd.note.designpattern.adapter.classs.Dc5V; +import com.wss.amd.note.designpattern.adapter.classs.Dc5VAdapter; +import com.wss.amd.note.designpattern.adapter.obj.SixiDumplings; +import com.wss.amd.note.designpattern.adapter.obj.SteamedBunsAdapter; +import com.wss.amd.note.designpattern.bridge.Circle; +import com.wss.amd.note.designpattern.bridge.Rectangle; +import com.wss.amd.note.designpattern.bridge.Shape; +import com.wss.amd.note.designpattern.bridge.pen.GreenPen; +import com.wss.amd.note.designpattern.bridge.pen.RedPen; +import com.wss.amd.note.designpattern.builder.TestDialog; +import com.wss.amd.note.designpattern.combination.Employee; +import com.wss.amd.note.designpattern.command.WaiterInvoker; +import com.wss.amd.note.designpattern.command.command.NoodleCommand; +import com.wss.amd.note.designpattern.command.command.RiceCommand; +import com.wss.amd.note.designpattern.command.command.StirFryCommand; +import com.wss.amd.note.designpattern.decorator.Egg; +import com.wss.amd.note.designpattern.decorator.GreenPepperShreddedMeat; +import com.wss.amd.note.designpattern.decorator.PotatoesBurnFireSirloin; +import com.wss.amd.note.designpattern.decorator.Vegetables; +import com.wss.amd.note.designpattern.decorator.base.Food; +import com.wss.amd.note.designpattern.decorator.base.Noodle; +import com.wss.amd.note.designpattern.decorator.base.Rice; +import com.wss.amd.note.designpattern.facade.FoodMaker; +import com.wss.amd.note.designpattern.fracory.abstractfactory.AssembleComputer; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.IComputerFactory; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.impl.computer.HaweiComputerFactoryFactory; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.impl.computer.IntelComputerFactoryFactory; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.impl.computer.MacComputerFactoryFactory; +import com.wss.amd.note.designpattern.fracory.normalfactory.ConfigFactory; +import com.wss.amd.note.designpattern.fracory.simplefactory.SimpleFactory; +import com.wss.amd.note.designpattern.fracory.simplefactory.config.IConfig; +import com.wss.amd.note.designpattern.interpreter.GameEnvironment; +import com.wss.amd.note.designpattern.interpreter.GameUser; +import com.wss.amd.note.designpattern.iterator.Iterator; +import com.wss.amd.note.designpattern.iterator.UserAggregate; +import com.wss.amd.note.designpattern.mediator.Colleague; +import com.wss.amd.note.designpattern.mediator.ConcreteColleague1; +import com.wss.amd.note.designpattern.mediator.ConcreteColleague2; +import com.wss.amd.note.designpattern.mediator.ConcreteMediator; +import com.wss.amd.note.designpattern.mediator.Mediator; +import com.wss.amd.note.designpattern.memorandum.Caretaker; +import com.wss.amd.note.designpattern.memorandum.Originator; +import com.wss.amd.note.designpattern.observer.BinaryObserver; +import com.wss.amd.note.designpattern.observer.HexaObserver; +import com.wss.amd.note.designpattern.observer.Subject; +import com.wss.amd.note.designpattern.proxy.User; +import com.wss.amd.note.designpattern.proxy.UserService; +import com.wss.amd.note.designpattern.proxy.UserServiceProxy; +import com.wss.amd.note.designpattern.responsibility.LocationRuleHandler; +import com.wss.amd.note.designpattern.responsibility.NewUserRuleHandler; +import com.wss.amd.note.designpattern.responsibility.ParticipantsRuleHandler; +import com.wss.amd.note.designpattern.responsibility.People; +import com.wss.amd.note.designpattern.state.InvisibleState; +import com.wss.amd.note.designpattern.state.OnLineState; +import com.wss.amd.note.designpattern.state.QQUser; +import com.wss.amd.note.designpattern.strategy.NoodleStrategy; +import com.wss.amd.note.designpattern.strategy.NoonEatWhat; +import com.wss.amd.note.designpattern.strategy.RiceStrategy; +import com.wss.amd.note.designpattern.template.AbsTemplateMethod; +import com.wss.amd.note.designpattern.template.TemplateMethod; +import com.wss.amd.note.designpattern.visitor.SetMaterial; +import com.wss.amd.note.designpattern.visitor.company.ArtCompany; +import com.wss.amd.note.designpattern.visitor.company.MintCompany; +import com.wss.amd.note.designpattern.visitor.material.Copper; +import com.wss.amd.note.designpattern.visitor.material.Paper; +import com.wss.common.base.BaseApplication; + +import org.junit.Test; + +/** + * Describe:测试类 + * Created by 吴天强 on 2022/1/17. + */ +public class NoteTest { + + @Test + public void testSimpleFactory() { + + //创建开发环境 + IConfig devConfig = SimpleFactory.createConfig(1); + + //创建生产环境 + IConfig proConfig = SimpleFactory.createConfig(0); + + } + + @Test + public void testConfigFactory() { + //普通工厂 + ConfigFactory configFactory = new ConfigFactory(); + IConfig config = configFactory.createConfig(); + String serviceUrl = config.getServiceUrl(); + } + + @Test + public void testAbstractFactory() { + //抽象工厂 + IComputerFactory factory; + + //创建华为工厂、组装华为电脑 + factory = new HaweiComputerFactoryFactory(); + AssembleComputer huaweiComputer = new AssembleComputer(factory.createCPU(), factory.createHardDisk() + , factory.createMainBoard()); + + //创建Intel工厂、组装Intel电脑 + factory = new IntelComputerFactoryFactory(); + AssembleComputer intelComputer = new AssembleComputer(factory.createCPU(), factory.createHardDisk() + , factory.createMainBoard()); + + //创建苹果工厂、组装苹果电脑 + factory = new MacComputerFactoryFactory(); + AssembleComputer macComputer = new AssembleComputer(factory.createCPU(), factory.createHardDisk() + , factory.createMainBoard()); + + } + + @Test + public void testBuilderDialog() { + //建造者 + new TestDialog.Builder(BaseApplication.i()) + .setContent("对话框内容") + .setCanCancel(false) + .build() + .show(); + } + + @Test + public void testUserProxy() { + //代理模式 + UserService service = new UserServiceProxy(); + User admin = service.creteAdmin(); + User user = service.createUser(); + } + + @Test + public void testObjAdapter() { + //测试 对象适配器 + SixiDumplings sixiDumplings = new SixiDumplings(); + SteamedBunsAdapter bunsAdapter = new SteamedBunsAdapter(sixiDumplings); + bunsAdapter.bunsFilling(); + bunsAdapter.steam(); + } + + @Test + public void testClassAdapter() { + //测试类适配器 + Dc5V dc5V = new Dc5VAdapter(); + int i = dc5V.dc5v(); + } + + @Test + public void testBridge() { + //桥梁模式 + + //红色的圆圈 + Shape circle = new Circle(10, new RedPen()); + circle.draw(); + + //绿色的矩形 + Shape rectangle = new Rectangle(10, 10, new GreenPen()); + rectangle.draw(); + } + + @Test + public void testFoodMaker() { + //门面模式 + FoodMaker foodMaker = new FoodMaker(); + foodMaker.makeBreakfast(); + foodMaker.makeLunch(); + foodMaker.makeDinner(); + } + + + @Test + public void testDecorator() { + //餐馆来了一位小哥, 他对老板说 + //老板,来米饭 + Food rice = new Rice(); + System.out.println(rice.getName() + ",价格:" + rice.getPrice() + "元"); + //加一分土豆烧牛腩 + rice = new PotatoesBurnFireSirloin(rice); + System.out.println(rice.getName() + ",价格:" + rice.getPrice() + "元"); + //再加个鸡蛋 + rice = new Egg(rice); + System.out.println(rice.getName() + ",价格:" + rice.getPrice() + "元"); + System.out.println(); + + //餐馆又来了一位大哥,他饭量比较好,需要双份的土豆牛腩和双份的青椒肉丝以及青菜、一个鸡蛋 + //老板,来双份面条 + Food noodle = new Noodle(); + System.out.println(noodle.getName() + ",价格:" + noodle.getPrice() + "元"); + //加双份土豆牛腩 + noodle = new PotatoesBurnFireSirloin(new PotatoesBurnFireSirloin(noodle)); + System.out.println(noodle.getName() + ",价格:" + noodle.getPrice() + "元"); + //再加双份的青椒肉丝 + noodle = new GreenPepperShreddedMeat(new GreenPepperShreddedMeat(noodle)); + System.out.println(noodle.getName() + ",价格:" + noodle.getPrice() + "元"); + //再来青菜 + noodle = new Vegetables(noodle); + System.out.println(noodle.getName() + ",价格:" + noodle.getPrice() + "元"); + //再来一个鸡蛋 + noodle = new Egg(noodle); + System.out.println(noodle.getName() + ",价格:" + noodle.getPrice() + "元"); + + } + + @Test + public void testCombination() { + + Employee employee = new Employee("张三", "信息部"); + employee.add(new Employee("李四", "信息部")); + employee.add(new Employee("王五", "信息部")); + + System.out.println(employee.toString()); + } + + @Test + public void testStrategy() { + //10个人去吃米饭 + NoonEatWhat noonEatWhat = new NoonEatWhat(new RiceStrategy()); + noonEatWhat.executeEat(10); + + //5个人去吃面条 + NoonEatWhat noonEatWhat1 = new NoonEatWhat(new NoodleStrategy()); + noonEatWhat1.executeEat(5); + } + + + @Test + public void testObserver() { + + //定义主题(被观察者) + Subject subject = new Subject(); + + //定义两个观察者 + new BinaryObserver(subject); + new HexaObserver(subject); + + //模拟数据变更,被观察者有了变化 + subject.setState(22); + } + + + @Test + public void testResponsibility() { + + People people = new People(); + people.setNewUser(true); + people.setLocation("上海"); + people.setParticipants(222); + + NewUserRuleHandler newUserRuleHandler = new NewUserRuleHandler(); + LocationRuleHandler locationRuleHandler = new LocationRuleHandler(); + ParticipantsRuleHandler participantsRuleHandler = new ParticipantsRuleHandler(); + + locationRuleHandler.setSuccessor(newUserRuleHandler); + participantsRuleHandler.setSuccessor(locationRuleHandler); + newUserRuleHandler.apply(people); + } + + @Test + public void testAbsTemplateMethod() { + AbsTemplateMethod templateMethod = new TemplateMethod(); + templateMethod.templateMethod(); + } + + @Test + public void testState() { + QQUser qqUser = new QQUser("人生如梦"); + + //隐身 + InvisibleState invisibleState = new InvisibleState(); + invisibleState.doAction(qqUser); + System.out.println(qqUser.getName() + "当前状态:" + qqUser.getState().toString()); + + //上线 + OnLineState onLineState = new OnLineState(); + onLineState.doAction(qqUser); + System.out.println(qqUser.getName() + "当前状态:" + qqUser.getState().toString()); + } + + @Test + public void testIterator() { + UserAggregate aggregate = new UserAggregate<>(); + aggregate.add(new User("张三")); + aggregate.add(new User("李四")); + aggregate.add(new User("王五")); + + Iterator iterator = aggregate.iterator(); + System.out.println("First:" + iterator.first()); + System.out.println("Has next:" + iterator.hasNext()); + System.out.println("Next:" + iterator.next()); + System.out.println("Next:" + iterator.next()); + System.out.println("Has next:" + iterator.hasNext()); + + } + + @Test + public void testCommand() { + //饭店对外生成了一个服务员,同时也生成了米饭、面条、炒菜三个菜单 + WaiterInvoker waiterInvoker = new WaiterInvoker(new RiceCommand(), new NoodleCommand(), new StirFryCommand()); + //饭店开张,来了一位顾客,选择了米饭 + waiterInvoker.chooseRice(); + //又继续点了一份面条 + waiterInvoker.chooseNoodle(); + //最后又加了一份炒菜 + waiterInvoker.chooseStirFry(); + } + + @Test + public void testMemorandum() { + Originator originator = new Originator(); + Caretaker caretaker = new Caretaker(); + originator.setState("create"); + System.out.println("当前状态:" + originator.getState()); + //保存当前状态到备忘录 + caretaker.setMemento(originator.createMemento()); + originator.setState("working"); + System.out.println("当前状态:" + originator.getState()); + originator.restoreMemento(caretaker.getMemento()); + System.out.println("当前状态:" + originator.getState()); + } + + @Test + public void testVisitor() { + + //定义材料集 + SetMaterial setMaterial = new SetMaterial(); + //添加纸 + setMaterial.add(new Paper()); + //添加铜 + setMaterial.add(new Copper()); + + //艺术公司制作工艺品 + String artSet = setMaterial.accept(new ArtCompany()); + System.out.println("艺术公司成果:" + artSet); + //造币公司制作钞票 + String mintSet = setMaterial.accept(new MintCompany()); + System.out.println("造币公司成果:" + mintSet); + + } + + @Test + public void testMediator() { + Mediator mediator = new ConcreteMediator(); + Colleague concreteColleague1 = new ConcreteColleague1(); + Colleague concreteColleague2 = new ConcreteColleague2(); + mediator.register(concreteColleague1); + mediator.register(concreteColleague2); + concreteColleague1.send(); + concreteColleague2.send(); + } + + @Test + public void testInterpreter() { + //假设:阵营是齐国、吴国且等级大于50的玩家只能参加普通副本《捉泥鳅》, + //其他的则可以参加高级副本《鹊桥仙》 + //定义用户环境 + GameEnvironment gameEnvironment = new GameEnvironment(); + //第一位 来自齐国的66级的李逍遥 + gameEnvironment.operation(new GameUser("李逍遥", 66, "齐国")); + //第二位 来自楚国的51级的张小凡 + gameEnvironment.operation(new GameUser("张小凡", 51, "楚国")); + //第三位 来自燕国的11级的高渐离 + gameEnvironment.operation(new GameUser("高渐离", 11, "燕国")); + + } + +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/adapter/classs/Ac220V.java b/app/src/main/java/com/wss/amd/note/designpattern/adapter/classs/Ac220V.java new file mode 100644 index 0000000..207499d --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/adapter/classs/Ac220V.java @@ -0,0 +1,13 @@ +package com.wss.amd.note.designpattern.adapter.classs; + +/** + * Describe: + * Created by 吴天强 on 2022/1/17. + */ +public class Ac220V { + + //获取220v电压 + public int outPut220() { + return 220; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/adapter/classs/Dc5V.java b/app/src/main/java/com/wss/amd/note/designpattern/adapter/classs/Dc5V.java new file mode 100644 index 0000000..65ebce5 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/adapter/classs/Dc5V.java @@ -0,0 +1,10 @@ +package com.wss.amd.note.designpattern.adapter.classs; + +/** + * Describe: + * Created by 吴天强 on 2022/1/17. + */ +public interface Dc5V { + + int dc5v(); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/adapter/classs/Dc5VAdapter.java b/app/src/main/java/com/wss/amd/note/designpattern/adapter/classs/Dc5VAdapter.java new file mode 100644 index 0000000..819a71c --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/adapter/classs/Dc5VAdapter.java @@ -0,0 +1,13 @@ +package com.wss.amd.note.designpattern.adapter.classs; + +/** + * Describe: + * Created by 吴天强 on 2022/1/17. + */ +public class Dc5VAdapter extends Ac220V implements Dc5V { + @Override + public int dc5v() { + int ac220v = outPut220(); + return ac220v / 44; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/adapter/normal/AppHttpRequestListener.java b/app/src/main/java/com/wss/amd/note/designpattern/adapter/normal/AppHttpRequestListener.java new file mode 100644 index 0000000..75d1943 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/adapter/normal/AppHttpRequestListener.java @@ -0,0 +1,32 @@ +package com.wss.amd.note.designpattern.adapter.normal; + +/** + * Describe: + * Created by 吴天强 on 2022/1/17. + */ +public class AppHttpRequestListener implements HttpRequestListener { + @Override + public void onStart() { + + } + + @Override + public void onProgress(int progress, int count) { + + } + + @Override + public void onSuccess() { + + } + + @Override + public void onError() { + + } + + @Override + public void onComplete() { + + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/adapter/normal/HttpRequestListener.java b/app/src/main/java/com/wss/amd/note/designpattern/adapter/normal/HttpRequestListener.java new file mode 100644 index 0000000..92138ab --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/adapter/normal/HttpRequestListener.java @@ -0,0 +1,18 @@ +package com.wss.amd.note.designpattern.adapter.normal; + +/** + * Describe: + * Created by 吴天强 on 2022/1/17. + */ +public interface HttpRequestListener { + + void onStart(); + + void onProgress(int progress, int count); + + void onSuccess(); + + void onError(); + + void onComplete(); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/adapter/normal/XxAppHttpRequestListener.java b/app/src/main/java/com/wss/amd/note/designpattern/adapter/normal/XxAppHttpRequestListener.java new file mode 100644 index 0000000..ade3dc8 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/adapter/normal/XxAppHttpRequestListener.java @@ -0,0 +1,18 @@ +package com.wss.amd.note.designpattern.adapter.normal; + +/** + * Describe: + * Created by 吴天强 on 2022/1/17. + */ +public class XxAppHttpRequestListener extends AppHttpRequestListener { + + @Override + public void onSuccess() { + //doSomething + } + + @Override + public void onError() { + //doSomething + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/adapter/obj/Buns.java b/app/src/main/java/com/wss/amd/note/designpattern/adapter/obj/Buns.java new file mode 100644 index 0000000..92172af --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/adapter/obj/Buns.java @@ -0,0 +1,12 @@ +package com.wss.amd.note.designpattern.adapter.obj; + +/** + * Describe: + * Created by 吴天强 on 2022/1/17. + */ +public interface Buns { + + void bunsFilling(); + + void steam(); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/adapter/obj/Dumplings.java b/app/src/main/java/com/wss/amd/note/designpattern/adapter/obj/Dumplings.java new file mode 100644 index 0000000..38fe600 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/adapter/obj/Dumplings.java @@ -0,0 +1,12 @@ +package com.wss.amd.note.designpattern.adapter.obj; + +/** + * Describe: + * Created by 吴天强 on 2022/1/17. + */ +public interface Dumplings { + + void dumplingsFilling(); + + void steam(); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/adapter/obj/SixiDumplings.java b/app/src/main/java/com/wss/amd/note/designpattern/adapter/obj/SixiDumplings.java new file mode 100644 index 0000000..5c5a1be --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/adapter/obj/SixiDumplings.java @@ -0,0 +1,17 @@ +package com.wss.amd.note.designpattern.adapter.obj; + +/** + * Describe: + * Created by 吴天强 on 2022/1/17. + */ +public class SixiDumplings implements Dumplings { + @Override + public void dumplingsFilling() { + System.out.println("四喜水饺馅儿"); + } + + @Override + public void steam() { + System.out.println("上锅蒸"); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/adapter/obj/SteamedBunsAdapter.java b/app/src/main/java/com/wss/amd/note/designpattern/adapter/obj/SteamedBunsAdapter.java new file mode 100644 index 0000000..cefb32d --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/adapter/obj/SteamedBunsAdapter.java @@ -0,0 +1,25 @@ +package com.wss.amd.note.designpattern.adapter.obj; + +/** + * Describe: + * Created by 吴天强 on 2022/1/17. + */ +public class SteamedBunsAdapter implements Buns { + + private Dumplings dumplings; + + public SteamedBunsAdapter(Dumplings dumplings) { + this.dumplings = dumplings; + } + + @Override + public void bunsFilling() { + //包子的内部实现其实是饺子馅儿 + dumplings.dumplingsFilling(); + } + + @Override + public void steam() { + dumplings.steam(); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/bridge/Circle.java b/app/src/main/java/com/wss/amd/note/designpattern/bridge/Circle.java new file mode 100644 index 0000000..a817f50 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/bridge/Circle.java @@ -0,0 +1,19 @@ +package com.wss.amd.note.designpattern.bridge; + +/** + * Describe: + * Created by 吴天强 on 2022/1/17. + */ +public class Circle extends Shape { + private int radius; + + public Circle(int radius, DrawApi drawApi) { + super(drawApi); + this.radius = radius; + } + + @Override + public void draw() { + drawApi.draw(radius, 0, 0); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/bridge/DrawApi.java b/app/src/main/java/com/wss/amd/note/designpattern/bridge/DrawApi.java new file mode 100644 index 0000000..95040ff --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/bridge/DrawApi.java @@ -0,0 +1,10 @@ +package com.wss.amd.note.designpattern.bridge; + +/** + * Describe: + * Created by 吴天强 on 2022/1/17. + */ +public interface DrawApi { + + void draw(int radius, int x, int y); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/bridge/Rectangle.java b/app/src/main/java/com/wss/amd/note/designpattern/bridge/Rectangle.java new file mode 100644 index 0000000..1a4b576 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/bridge/Rectangle.java @@ -0,0 +1,20 @@ +package com.wss.amd.note.designpattern.bridge; + +/** + * Describe: + * Created by 吴天强 on 2022/1/17. + */ +public class Rectangle extends Shape { + private int x, y; + + public Rectangle(int x, int y, DrawApi drawApi) { + super(drawApi); + this.x = x; + this.y = y; + } + + @Override + public void draw() { + drawApi.draw(0, x, y); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/bridge/Shape.java b/app/src/main/java/com/wss/amd/note/designpattern/bridge/Shape.java new file mode 100644 index 0000000..894c5d0 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/bridge/Shape.java @@ -0,0 +1,16 @@ +package com.wss.amd.note.designpattern.bridge; + +/** + * Describe: + * Created by 吴天强 on 2022/1/17. + */ +public abstract class Shape { + + protected DrawApi drawApi; + + public Shape(DrawApi drawApi) { + this.drawApi = drawApi; + } + + public abstract void draw(); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/bridge/pen/GreenPen.java b/app/src/main/java/com/wss/amd/note/designpattern/bridge/pen/GreenPen.java new file mode 100644 index 0000000..d28b9d5 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/bridge/pen/GreenPen.java @@ -0,0 +1,14 @@ +package com.wss.amd.note.designpattern.bridge.pen; + +import com.wss.amd.note.designpattern.bridge.DrawApi; + +/** + * Describe: + * Created by 吴天强 on 2022/1/17. + */ +public class GreenPen implements DrawApi { + @Override + public void draw(int radius, int x, int y) { + System.out.println("使用绿笔画图,radius:" + radius + ",x:" + x + ",y:" + y); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/bridge/pen/RedPen.java b/app/src/main/java/com/wss/amd/note/designpattern/bridge/pen/RedPen.java new file mode 100644 index 0000000..a2cabbb --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/bridge/pen/RedPen.java @@ -0,0 +1,14 @@ +package com.wss.amd.note.designpattern.bridge.pen; + +import com.wss.amd.note.designpattern.bridge.DrawApi; + +/** + * Describe: + * Created by 吴天强 on 2022/1/17. + */ +public class RedPen implements DrawApi { + @Override + public void draw(int radius, int x, int y) { + System.out.println("使用红笔画图,radius:" + radius + ",x:" + x + ",y:" + y); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/builder/TestDialog.java b/app/src/main/java/com/wss/amd/note/designpattern/builder/TestDialog.java new file mode 100644 index 0000000..103a92d --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/builder/TestDialog.java @@ -0,0 +1,64 @@ +package com.wss.amd.note.designpattern.builder; + +import android.app.Dialog; +import android.content.Context; +import android.view.Gravity; +import android.view.View; +import android.view.Window; +import android.widget.TextView; + +import com.wss.amd.R; + +/** + * Describe: + * Created by 吴天强 on 2022/1/17. + */ +public class TestDialog { + + private Builder builder; + + private TestDialog(Builder builder) { + this.builder = builder; + } + + + public void show() { + View dialogView = View.inflate(builder.context, R.layout.dialog_app, null); + ((TextView) dialogView.findViewById(R.id.tv_content)).setText(builder.content); + Dialog dialog = new Dialog(builder.context, R.style.DialogStyle); + dialog.setContentView(dialogView); + Window window = dialog.getWindow(); + if (window != null) { + window.getAttributes().gravity = Gravity.CENTER; + } + dialog.setCancelable(builder.canCancel); + dialog.show(); + } + + public static class Builder { + + private Context context; + private String content; + private boolean canCancel; + + public Builder(Context context) { + this.context = context; + } + + public Builder setContent(String content) { + this.content = content; + return this; + } + + public Builder setCanCancel(boolean canCancel) { + this.canCancel = canCancel; + return this; + } + + + public TestDialog build() { + return new TestDialog(this); + } + + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/combination/Employee.java b/app/src/main/java/com/wss/amd/note/designpattern/combination/Employee.java new file mode 100644 index 0000000..6e15c78 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/combination/Employee.java @@ -0,0 +1,41 @@ +package com.wss.amd.note.designpattern.combination; + +import java.util.ArrayList; +import java.util.List; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public class Employee { + private String name; + private String dept; + private List subordinates = new ArrayList<>(); + + public Employee(String name, String dept) { + this.name = name; + this.dept = dept; + } + + + public void add(Employee e) { + subordinates.add(e); + } + + public void remove(Employee e) { + subordinates.remove(e); + } + + public List getSubordinates() { + return subordinates; + } + + @Override + public String toString() { + return "Employee{" + + "name='" + name + '\'' + + ", dept='" + dept + '\'' + + ", subordinates=" + subordinates + + '}'; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/command/WaiterInvoker.java b/app/src/main/java/com/wss/amd/note/designpattern/command/WaiterInvoker.java new file mode 100644 index 0000000..cba241d --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/command/WaiterInvoker.java @@ -0,0 +1,35 @@ +package com.wss.amd.note.designpattern.command; + +import com.wss.amd.note.designpattern.command.command.AbsCommand; + +/** + * Describe:具体调度者 + * Created by 吴天强 on 2022/1/19. + */ +public class WaiterInvoker { + private AbsCommand riceCommand; + private AbsCommand noodleCommand; + private AbsCommand stirFryCommand; + + public WaiterInvoker(AbsCommand riceCommand, AbsCommand noodleCommand, AbsCommand stirFryCommand) { + this.riceCommand = riceCommand; + this.noodleCommand = noodleCommand; + this.stirFryCommand = stirFryCommand; + } + + + public void chooseRice() { + riceCommand.execute(); + } + + public void chooseNoodle() { + noodleCommand.execute(); + } + + public void chooseStirFry() { + stirFryCommand.execute(); + } + + +} + diff --git a/app/src/main/java/com/wss/amd/note/designpattern/command/command/AbsCommand.java b/app/src/main/java/com/wss/amd/note/designpattern/command/command/AbsCommand.java new file mode 100644 index 0000000..474024b --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/command/command/AbsCommand.java @@ -0,0 +1,14 @@ +package com.wss.amd.note.designpattern.command.command; + +import com.wss.amd.note.designpattern.command.receiver.CookReceiver; + +/** + * Describe: + * Created by 吴天强 on 2022/1/19. + */ +public abstract class AbsCommand { + + protected CookReceiver receiver; + + public abstract void execute(); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/command/command/NoodleCommand.java b/app/src/main/java/com/wss/amd/note/designpattern/command/command/NoodleCommand.java new file mode 100644 index 0000000..3b0103f --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/command/command/NoodleCommand.java @@ -0,0 +1,18 @@ +package com.wss.amd.note.designpattern.command.command; + +import com.wss.amd.note.designpattern.command.receiver.NoodleCookeReceiver; + +/** + * Describe:面条命令 + * Created by 吴天强 on 2022/1/19. + */ +public class NoodleCommand extends AbsCommand { + public NoodleCommand() { + this.receiver = new NoodleCookeReceiver(); + } + + @Override + public void execute() { + receiver.cooking(); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/command/command/RiceCommand.java b/app/src/main/java/com/wss/amd/note/designpattern/command/command/RiceCommand.java new file mode 100644 index 0000000..2aae7e2 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/command/command/RiceCommand.java @@ -0,0 +1,19 @@ +package com.wss.amd.note.designpattern.command.command; + +import com.wss.amd.note.designpattern.command.receiver.RiceCookReceiver; + +/** + * Describe:做米饭具体命令 + * Created by 吴天强 on 2022/1/19. + */ +public class RiceCommand extends AbsCommand { + + public RiceCommand() { + this.receiver = new RiceCookReceiver(); + } + + @Override + public void execute() { + receiver.cooking(); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/command/command/StirFryCommand.java b/app/src/main/java/com/wss/amd/note/designpattern/command/command/StirFryCommand.java new file mode 100644 index 0000000..f57904a --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/command/command/StirFryCommand.java @@ -0,0 +1,18 @@ +package com.wss.amd.note.designpattern.command.command; + +import com.wss.amd.note.designpattern.command.receiver.StirFryReceiver; + +/** + * Describe:炒菜命令 + * Created by 吴天强 on 2022/1/19. + */ +public class StirFryCommand extends AbsCommand { + public StirFryCommand() { + this.receiver = new StirFryReceiver(); + } + + @Override + public void execute() { + receiver.cooking(); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/command/receiver/CookReceiver.java b/app/src/main/java/com/wss/amd/note/designpattern/command/receiver/CookReceiver.java new file mode 100644 index 0000000..605dbf1 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/command/receiver/CookReceiver.java @@ -0,0 +1,10 @@ +package com.wss.amd.note.designpattern.command.receiver; + +/** + * Describe: + * Created by 吴天强 on 2022/1/19. + */ +public interface CookReceiver { + + void cooking(); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/command/receiver/NoodleCookeReceiver.java b/app/src/main/java/com/wss/amd/note/designpattern/command/receiver/NoodleCookeReceiver.java new file mode 100644 index 0000000..bc9bfa0 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/command/receiver/NoodleCookeReceiver.java @@ -0,0 +1,12 @@ +package com.wss.amd.note.designpattern.command.receiver; + +/** + * Describe: + * Created by 吴天强 on 2022/1/19. + */ +public class NoodleCookeReceiver implements CookReceiver { + @Override + public void cooking() { + System.out.println("面条厨子开始做面条了~~~"); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/command/receiver/RiceCookReceiver.java b/app/src/main/java/com/wss/amd/note/designpattern/command/receiver/RiceCookReceiver.java new file mode 100644 index 0000000..026fd5d --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/command/receiver/RiceCookReceiver.java @@ -0,0 +1,14 @@ +package com.wss.amd.note.designpattern.command.receiver; + +/** + * Describe:米饭厨子 + * Created by 吴天强 on 2022/1/19. + */ +public class RiceCookReceiver implements CookReceiver { + + + @Override + public void cooking() { + System.out.println("米饭厨子开始做米饭~"); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/command/receiver/StirFryReceiver.java b/app/src/main/java/com/wss/amd/note/designpattern/command/receiver/StirFryReceiver.java new file mode 100644 index 0000000..415be9b --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/command/receiver/StirFryReceiver.java @@ -0,0 +1,12 @@ +package com.wss.amd.note.designpattern.command.receiver; + +/** + * Describe: + * Created by 吴天强 on 2022/1/19. + */ +public class StirFryReceiver implements CookReceiver { + @Override + public void cooking() { + System.out.println("炒菜厨子开始炒菜了~"); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/decorator/Condiment.java b/app/src/main/java/com/wss/amd/note/designpattern/decorator/Condiment.java new file mode 100644 index 0000000..12a5762 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/decorator/Condiment.java @@ -0,0 +1,10 @@ +package com.wss.amd.note.designpattern.decorator; + +import com.wss.amd.note.designpattern.decorator.base.Food; + +/** + * Describe:定义调料,也就是装饰者的基类,此类必须继承自Food + * Created by 吴天强 on 2022/1/18. + */ +public abstract class Condiment extends Food { +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/decorator/Egg.java b/app/src/main/java/com/wss/amd/note/designpattern/decorator/Egg.java new file mode 100644 index 0000000..49eee56 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/decorator/Egg.java @@ -0,0 +1,28 @@ +package com.wss.amd.note.designpattern.decorator; + +import com.wss.amd.note.designpattern.decorator.base.Food; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public class Egg extends Condiment { + private Food food; + + //这里很关键,需要传入具体的食物(大米、面条),当然也可以传入已经调好的(装饰过)食物 + public Egg(Food food) { + this.food = food; + } + + @Override + public String getName() { + //加个鸡蛋装饰 + return food.getName() + ",加个鸡蛋"; + } + + @Override + public double getPrice() { + //价格装饰 + return food.getPrice() + 2; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/decorator/GreenPepperShreddedMeat.java b/app/src/main/java/com/wss/amd/note/designpattern/decorator/GreenPepperShreddedMeat.java new file mode 100644 index 0000000..401dcb1 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/decorator/GreenPepperShreddedMeat.java @@ -0,0 +1,29 @@ +package com.wss.amd.note.designpattern.decorator; + +import com.wss.amd.note.designpattern.decorator.base.Food; + +/** + * Describe:青椒肉丝浇头 + * Created by 吴天强 on 2022/1/18. + */ +public class GreenPepperShreddedMeat extends Condiment { + + private Food food; + + //这里很关键,需要传入具体的食物(大米、面条),当然也可以传入已经调好的(装饰过)食物 + public GreenPepperShreddedMeat(Food food) { + this.food = food; + } + + @Override + public String getName() { + //进行青椒肉丝的装饰 + return food.getName() + ",加青椒肉丝"; + } + + @Override + public double getPrice() { + //进行价格装饰 + return food.getPrice() + 4; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/decorator/PotatoesBurnFireSirloin.java b/app/src/main/java/com/wss/amd/note/designpattern/decorator/PotatoesBurnFireSirloin.java new file mode 100644 index 0000000..f0298e3 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/decorator/PotatoesBurnFireSirloin.java @@ -0,0 +1,29 @@ +package com.wss.amd.note.designpattern.decorator; + +import com.wss.amd.note.designpattern.decorator.base.Food; + +/** + * Describe:土豆烧牛腩浇头 + * Created by 吴天强 on 2022/1/18. + */ +public class PotatoesBurnFireSirloin extends Condiment { + + private Food food; + + //这里很关键,需要传入具体的食物(大米、面条),当然也可以传入已经调好的(装饰过)食物 + public PotatoesBurnFireSirloin(Food food) { + this.food = food; + } + + @Override + public String getName() { + //进行土豆烧牛腩的装饰 + return food.getName() + ",加土豆烧牛腩"; + } + + @Override + public double getPrice() { + //进行价格装饰 + return food.getPrice() + 6; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/decorator/Vegetables.java b/app/src/main/java/com/wss/amd/note/designpattern/decorator/Vegetables.java new file mode 100644 index 0000000..273bf85 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/decorator/Vegetables.java @@ -0,0 +1,29 @@ +package com.wss.amd.note.designpattern.decorator; + +import com.wss.amd.note.designpattern.decorator.base.Food; + +/** + * Describe:加青菜 + * Created by 吴天强 on 2022/1/18. + */ +public class Vegetables extends Condiment { + + private Food food; + + //这里很关键,需要传入具体的食物(大米、面条),当然也可以传入已经调好的(装饰过)食物 + public Vegetables(Food food) { + this.food = food; + } + + @Override + public String getName() { + //进行素菜装饰 + return food.getName() + ",加一份青菜"; + } + + @Override + public double getPrice() { + //进行价格装饰 + return food.getPrice() + 1; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/decorator/base/Food.java b/app/src/main/java/com/wss/amd/note/designpattern/decorator/base/Food.java new file mode 100644 index 0000000..95c5eb1 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/decorator/base/Food.java @@ -0,0 +1,12 @@ +package com.wss.amd.note.designpattern.decorator.base; + +/** + * Describe:定义食物抽象基类 + * Created by 吴天强 on 2022/1/18. + */ +public abstract class Food { + + public abstract String getName(); + + public abstract double getPrice(); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/decorator/base/Noodle.java b/app/src/main/java/com/wss/amd/note/designpattern/decorator/base/Noodle.java new file mode 100644 index 0000000..b455c17 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/decorator/base/Noodle.java @@ -0,0 +1,17 @@ +package com.wss.amd.note.designpattern.decorator.base; + +/** + * Describe:定义普通面条 + * Created by 吴天强 on 2022/1/18. + */ +public class Noodle extends Food { + @Override + public String getName() { + return "清汤面"; + } + + @Override + public double getPrice() { + return 5; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/decorator/base/Rice.java b/app/src/main/java/com/wss/amd/note/designpattern/decorator/base/Rice.java new file mode 100644 index 0000000..d60cb03 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/decorator/base/Rice.java @@ -0,0 +1,17 @@ +package com.wss.amd.note.designpattern.decorator.base; + +/** + * Describe:定义普通米饭 + * Created by 吴天强 on 2022/1/18. + */ +public class Rice extends Food { + @Override + public String getName() { + return "白米饭"; + } + + @Override + public double getPrice() { + return 6; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/facade/BreakfastFood.java b/app/src/main/java/com/wss/amd/note/designpattern/facade/BreakfastFood.java new file mode 100644 index 0000000..9734ae8 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/facade/BreakfastFood.java @@ -0,0 +1,12 @@ +package com.wss.amd.note.designpattern.facade; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public class BreakfastFood implements Food { + @Override + public void make() { + System.out.println("开始制作早餐啦~~~"); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/facade/DinnerFood.java b/app/src/main/java/com/wss/amd/note/designpattern/facade/DinnerFood.java new file mode 100644 index 0000000..3e518e4 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/facade/DinnerFood.java @@ -0,0 +1,12 @@ +package com.wss.amd.note.designpattern.facade; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public class DinnerFood implements Food { + @Override + public void make() { + System.out.println("开始制作晚餐啦~~~"); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/facade/Food.java b/app/src/main/java/com/wss/amd/note/designpattern/facade/Food.java new file mode 100644 index 0000000..197e4e9 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/facade/Food.java @@ -0,0 +1,10 @@ +package com.wss.amd.note.designpattern.facade; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public interface Food { + + void make(); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/facade/FoodMaker.java b/app/src/main/java/com/wss/amd/note/designpattern/facade/FoodMaker.java new file mode 100644 index 0000000..322faef --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/facade/FoodMaker.java @@ -0,0 +1,31 @@ +package com.wss.amd.note.designpattern.facade; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public class FoodMaker { + + private BreakfastFood breakfastFood; + private LunchFood lunchFood; + private DinnerFood dinnerFood; + + public FoodMaker() { + breakfastFood = new BreakfastFood(); + lunchFood = new LunchFood(); + dinnerFood = new DinnerFood(); + } + //下面定义一堆方法,具体应该调用什么方法,有这个门面来决定 + + public void makeBreakfast() { + breakfastFood.make(); + } + + public void makeLunch() { + lunchFood.make(); + } + + public void makeDinner() { + dinnerFood.make(); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/facade/LunchFood.java b/app/src/main/java/com/wss/amd/note/designpattern/facade/LunchFood.java new file mode 100644 index 0000000..920218d --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/facade/LunchFood.java @@ -0,0 +1,12 @@ +package com.wss.amd.note.designpattern.facade; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public class LunchFood implements Food { + @Override + public void make() { + System.out.println("开始制作午餐啦~~~"); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/flyweight/Flyweight.java b/app/src/main/java/com/wss/amd/note/designpattern/flyweight/Flyweight.java new file mode 100644 index 0000000..063ce96 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/flyweight/Flyweight.java @@ -0,0 +1,21 @@ +package com.wss.amd.note.designpattern.flyweight; + +import java.util.WeakHashMap; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public class Flyweight { + + private WeakHashMap weakHashMap = new WeakHashMap<>(); + + public User getUser(int index) { + User user = weakHashMap.get(index); + if (user == null) { + user = new User(); + weakHashMap.put(index, user); + } + return user; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/flyweight/User.java b/app/src/main/java/com/wss/amd/note/designpattern/flyweight/User.java new file mode 100644 index 0000000..0c011ea --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/flyweight/User.java @@ -0,0 +1,9 @@ +package com.wss.amd.note.designpattern.flyweight; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public class User { + +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/AssembleComputer.java b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/AssembleComputer.java new file mode 100644 index 0000000..2ae7460 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/AssembleComputer.java @@ -0,0 +1,23 @@ +package com.wss.amd.note.designpattern.fracory.abstractfactory; + +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.ICPU; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.IHardDisk; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.IMainBoard; + +/** + * Describe:组装电脑 + * Created by 吴天强 on 2022/1/14. + */ +public class AssembleComputer { + + private ICPU cpu; + private IHardDisk hardDisk; + private IMainBoard mainBoard; + + public AssembleComputer(ICPU cpu, IHardDisk hardDisk, IMainBoard mainBoard) { + this.cpu = cpu; + this.hardDisk = hardDisk; + this.mainBoard = mainBoard; + } +} + diff --git a/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/ICPU.java b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/ICPU.java new file mode 100644 index 0000000..48fb8d6 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/ICPU.java @@ -0,0 +1,8 @@ +package com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces; + +/** + * Describe:CPU 接口 + * Created by 吴天强 on 2022/1/14. + */ +public interface ICPU { +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/IComputerFactory.java b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/IComputerFactory.java new file mode 100644 index 0000000..c45fb55 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/IComputerFactory.java @@ -0,0 +1,14 @@ +package com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces; + +/** + * Describe:电脑接口 + * Created by 吴天强 on 2022/1/14. + */ +public interface IComputerFactory { + + ICPU createCPU(); + + IHardDisk createHardDisk(); + + IMainBoard createMainBoard(); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/IHardDisk.java b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/IHardDisk.java new file mode 100644 index 0000000..147cbda --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/IHardDisk.java @@ -0,0 +1,8 @@ +package com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces; + +/** + * Describe:硬盘接口 + * Created by 吴天强 on 2022/1/14. + */ +public interface IHardDisk { +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/IMainBoard.java b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/IMainBoard.java new file mode 100644 index 0000000..d87784a --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/IMainBoard.java @@ -0,0 +1,8 @@ +package com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces; + +/** + * Describe:主板接口 + * Created by 吴天强 on 2022/1/14. + */ +public interface IMainBoard { +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/computer/HaweiComputerFactoryFactory.java b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/computer/HaweiComputerFactoryFactory.java new file mode 100644 index 0000000..02dd705 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/computer/HaweiComputerFactoryFactory.java @@ -0,0 +1,31 @@ +package com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.impl.computer; + +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.ICPU; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.IComputerFactory; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.IHardDisk; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.IMainBoard; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.impl.cpu.AmdCPU; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.impl.harddisk.ToshibaHardDisk; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.impl.mainboard.AsusMainBoard; + +/** + * Describe:华为电脑工厂 + * Created by 吴天强 on 2022/1/14. + */ +public class HaweiComputerFactoryFactory implements IComputerFactory { + + @Override + public ICPU createCPU() { + return new AmdCPU(); + } + + @Override + public IHardDisk createHardDisk() { + return new ToshibaHardDisk(); + } + + @Override + public IMainBoard createMainBoard() { + return new AsusMainBoard(); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/computer/IntelComputerFactoryFactory.java b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/computer/IntelComputerFactoryFactory.java new file mode 100644 index 0000000..40b0d2e --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/computer/IntelComputerFactoryFactory.java @@ -0,0 +1,31 @@ +package com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.impl.computer; + +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.ICPU; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.IComputerFactory; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.IHardDisk; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.IMainBoard; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.impl.cpu.IntelCPU; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.impl.harddisk.HpHardDisk; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.impl.mainboard.MsiMainBoard; + +/** + * Describe:英特尔电脑工厂 + * Created by 吴天强 on 2022/1/14. + */ +public class IntelComputerFactoryFactory implements IComputerFactory { + + @Override + public ICPU createCPU() { + return new IntelCPU(); + } + + @Override + public IHardDisk createHardDisk() { + return new HpHardDisk(); + } + + @Override + public IMainBoard createMainBoard() { + return new MsiMainBoard(); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/computer/MacComputerFactoryFactory.java b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/computer/MacComputerFactoryFactory.java new file mode 100644 index 0000000..06de11a --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/computer/MacComputerFactoryFactory.java @@ -0,0 +1,29 @@ +package com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.impl.computer; + +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.ICPU; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.IComputerFactory; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.IHardDisk; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.IMainBoard; +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.impl.cpu.AmdCPU; + +/** + * Describe:MAC电脑工厂 + * Created by 吴天强 on 2022/1/14. + */ +public class MacComputerFactoryFactory implements IComputerFactory { + + @Override + public ICPU createCPU() { + return new AmdCPU(); + } + + @Override + public IHardDisk createHardDisk() { + return null; + } + + @Override + public IMainBoard createMainBoard() { + return null; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/cpu/AmdCPU.java b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/cpu/AmdCPU.java new file mode 100644 index 0000000..0e45fc1 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/cpu/AmdCPU.java @@ -0,0 +1,10 @@ +package com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.impl.cpu; + +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.ICPU; + +/** + * Describe:AMD CPU + * Created by 吴天强 on 2022/1/14. + */ +public class AmdCPU implements ICPU { +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/cpu/IntelCPU.java b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/cpu/IntelCPU.java new file mode 100644 index 0000000..bd3b656 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/cpu/IntelCPU.java @@ -0,0 +1,10 @@ +package com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.impl.cpu; + +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.ICPU; + +/** + * Describe:Intel CPU + * Created by 吴天强 on 2022/1/14. + */ +public class IntelCPU implements ICPU { +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/harddisk/HpHardDisk.java b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/harddisk/HpHardDisk.java new file mode 100644 index 0000000..3c147ca --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/harddisk/HpHardDisk.java @@ -0,0 +1,10 @@ +package com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.impl.harddisk; + +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.IHardDisk; + +/** + * Describe:惠普硬盘 + * Created by 吴天强 on 2022/1/14. + */ +public class HpHardDisk implements IHardDisk { +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/harddisk/ToshibaHardDisk.java b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/harddisk/ToshibaHardDisk.java new file mode 100644 index 0000000..a4848b6 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/harddisk/ToshibaHardDisk.java @@ -0,0 +1,10 @@ +package com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.impl.harddisk; + +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.IHardDisk; + +/** + * Describe:东芝硬盘 + * Created by 吴天强 on 2022/1/14. + */ +public class ToshibaHardDisk implements IHardDisk { +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/mainboard/AsusMainBoard.java b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/mainboard/AsusMainBoard.java new file mode 100644 index 0000000..6b65dab --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/mainboard/AsusMainBoard.java @@ -0,0 +1,10 @@ +package com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.impl.mainboard; + +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.IMainBoard; + +/** + * Describe:华硕主板 + * Created by 吴天强 on 2022/1/14. + */ +public class AsusMainBoard implements IMainBoard { +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/mainboard/MsiMainBoard.java b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/mainboard/MsiMainBoard.java new file mode 100644 index 0000000..0903947 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/fracory/abstractfactory/interfaces/impl/mainboard/MsiMainBoard.java @@ -0,0 +1,10 @@ +package com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.impl.mainboard; + +import com.wss.amd.note.designpattern.fracory.abstractfactory.interfaces.IMainBoard; + +/** + * Describe:微星主板 + * Created by 吴天强 on 2022/1/14. + */ +public class MsiMainBoard implements IMainBoard { +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/fracory/normalfactory/ConfigFactory.java b/app/src/main/java/com/wss/amd/note/designpattern/fracory/normalfactory/ConfigFactory.java new file mode 100644 index 0000000..56042e3 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/fracory/normalfactory/ConfigFactory.java @@ -0,0 +1,21 @@ +package com.wss.amd.note.designpattern.fracory.normalfactory; + +import com.wss.amd.BuildConfig; +import com.wss.amd.note.designpattern.fracory.simplefactory.config.DevConfig; +import com.wss.amd.note.designpattern.fracory.simplefactory.config.IConfig; +import com.wss.amd.note.designpattern.fracory.simplefactory.config.ProConfig; + +/** + * Describe:创建环境工厂 + * Created by 吴天强 on 2022/1/14. + */ +public class ConfigFactory implements IConfigFactory { + + @Override + public IConfig createConfig() { + if (!BuildConfig.DEBUG) { + return new ProConfig(); + } + return new DevConfig(); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/fracory/normalfactory/IConfigFactory.java b/app/src/main/java/com/wss/amd/note/designpattern/fracory/normalfactory/IConfigFactory.java new file mode 100644 index 0000000..b46393a --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/fracory/normalfactory/IConfigFactory.java @@ -0,0 +1,12 @@ +package com.wss.amd.note.designpattern.fracory.normalfactory; + + +import com.wss.amd.note.designpattern.fracory.simplefactory.config.IConfig; + +/** + * Describe:定义创建环境工厂接口 + * Created by 吴天强 on 2022/1/14. + */ +public interface IConfigFactory { + IConfig createConfig(); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/fracory/simplefactory/SimpleFactory.java b/app/src/main/java/com/wss/amd/note/designpattern/fracory/simplefactory/SimpleFactory.java new file mode 100644 index 0000000..10a040e --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/fracory/simplefactory/SimpleFactory.java @@ -0,0 +1,30 @@ +package com.wss.amd.note.designpattern.fracory.simplefactory; + +import com.wss.amd.note.designpattern.fracory.simplefactory.config.DevConfig; +import com.wss.amd.note.designpattern.fracory.simplefactory.config.IConfig; +import com.wss.amd.note.designpattern.fracory.simplefactory.config.ProConfig; + +/** + * Describe:创建环境配置文件工厂 + * Created by 吴天强 on 2022/1/14. + */ +public class SimpleFactory { + + + /** + * 创建配置文件 + * + * @param type 环境类型 + * @return 已创建环境 + */ + public static IConfig createConfig(int type) { + switch (type) { + case 0: + return new ProConfig(); + case 1: + return new DevConfig(); + default: + } + return null; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/fracory/simplefactory/config/DevConfig.java b/app/src/main/java/com/wss/amd/note/designpattern/fracory/simplefactory/config/DevConfig.java new file mode 100644 index 0000000..44fa8e0 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/fracory/simplefactory/config/DevConfig.java @@ -0,0 +1,13 @@ +package com.wss.amd.note.designpattern.fracory.simplefactory.config; + +/** + * Describe:开发环境 + * Created by 吴天强 on 2022/1/14. + */ +public class DevConfig implements IConfig { + + @Override + public String getServiceUrl() { + return "http://192.168.0.1"; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/fracory/simplefactory/config/IConfig.java b/app/src/main/java/com/wss/amd/note/designpattern/fracory/simplefactory/config/IConfig.java new file mode 100644 index 0000000..948d25d --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/fracory/simplefactory/config/IConfig.java @@ -0,0 +1,10 @@ +package com.wss.amd.note.designpattern.fracory.simplefactory.config; + +/** + * Describe:定义环境配置相关方法 + * Created by 吴天强 on 2022/1/14. + */ +public interface IConfig { + + String getServiceUrl(); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/fracory/simplefactory/config/ProConfig.java b/app/src/main/java/com/wss/amd/note/designpattern/fracory/simplefactory/config/ProConfig.java new file mode 100644 index 0000000..a947a52 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/fracory/simplefactory/config/ProConfig.java @@ -0,0 +1,13 @@ +package com.wss.amd.note.designpattern.fracory.simplefactory.config; + +/** + * Describe:生产环境 + * Created by 吴天强 on 2022/1/14. + */ +public class ProConfig implements IConfig { + + @Override + public String getServiceUrl() { + return "http://pro.wss.com"; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/interpreter/AbstractExpression.java b/app/src/main/java/com/wss/amd/note/designpattern/interpreter/AbstractExpression.java new file mode 100644 index 0000000..b760dff --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/interpreter/AbstractExpression.java @@ -0,0 +1,9 @@ +package com.wss.amd.note.designpattern.interpreter; + +/** + * Describe: + * Created by 吴天强 on 2022/1/19. + */ +public interface AbstractExpression { + boolean interpret(GameUser gameUser); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/interpreter/CampTerminalExpression.java b/app/src/main/java/com/wss/amd/note/designpattern/interpreter/CampTerminalExpression.java new file mode 100644 index 0000000..f16a0fe --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/interpreter/CampTerminalExpression.java @@ -0,0 +1,26 @@ +package com.wss.amd.note.designpattern.interpreter; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Describe: + * Created by 吴天强 on 2022/1/19. + */ +public class CampTerminalExpression implements AbstractExpression { + + private List list = new ArrayList<>(); + + public CampTerminalExpression(String[] resource) { + this.list.addAll(Arrays.asList(resource)); + } + + @Override + public boolean interpret(@NotNull GameUser info) { + //对终结符表达式的处理 + return !list.contains(info.getCamp()); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wss/amd/note/designpattern/interpreter/GameEnvironment.java b/app/src/main/java/com/wss/amd/note/designpattern/interpreter/GameEnvironment.java new file mode 100644 index 0000000..23d8908 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/interpreter/GameEnvironment.java @@ -0,0 +1,26 @@ +package com.wss.amd.note.designpattern.interpreter; + +/** + * Describe: + * Created by 吴天强 on 2022/1/19. + */ +public class GameEnvironment { + private AbstractExpression exp; + + + public GameEnvironment() { + //数据初始化 + exp = new NonTerminalExpression(new CampTerminalExpression(new String[]{"齐国", "吴国"}), + new GradeTerminalExpression(50)); + } + + public void operation(GameUser user) { + //调用相关表达式类的解释方法 + boolean interpret = exp.interpret(user); + if (interpret) { + System.out.println("恭喜来自" + user.getCamp() + "的" + user.getName() + ",您可以参加高级副本《鹊桥仙》"); + } else { + System.out.println("恭喜来自" + user.getCamp() + "的" + user.getName() + ",您可以参加普通副本《捉泥鳅》"); + } + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/interpreter/GameUser.java b/app/src/main/java/com/wss/amd/note/designpattern/interpreter/GameUser.java new file mode 100644 index 0000000..a590a52 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/interpreter/GameUser.java @@ -0,0 +1,24 @@ +package com.wss.amd.note.designpattern.interpreter; + +import lombok.Getter; +import lombok.Setter; + +/** + * Describe: 游戏角色 + * Created by 吴天强 on 2022/1/19. + */ +@Getter +@Setter +public class GameUser { + private String name; + private String grader; + private int grade; + private String camp; + private boolean participated; + + public GameUser(String name, int grade, String camp) { + this.name = name; + this.grade = grade; + this.camp = camp; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/interpreter/GradeTerminalExpression.java b/app/src/main/java/com/wss/amd/note/designpattern/interpreter/GradeTerminalExpression.java new file mode 100644 index 0000000..070bb38 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/interpreter/GradeTerminalExpression.java @@ -0,0 +1,22 @@ +package com.wss.amd.note.designpattern.interpreter; + +import org.jetbrains.annotations.NotNull; + +/** + * Describe: + * Created by 吴天强 on 2022/1/19. + */ +public class GradeTerminalExpression implements AbstractExpression { + + private int grade; + + public GradeTerminalExpression(int grade) { + this.grade = grade; + } + + @Override + public boolean interpret(@NotNull GameUser info) { + //对终结符表达式的处理 + return info.getGrade() < grade; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wss/amd/note/designpattern/interpreter/NonTerminalExpression.java b/app/src/main/java/com/wss/amd/note/designpattern/interpreter/NonTerminalExpression.java new file mode 100644 index 0000000..2707162 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/interpreter/NonTerminalExpression.java @@ -0,0 +1,22 @@ +package com.wss.amd.note.designpattern.interpreter; + +/** + * Describe:非终结表达式 + * Created by 吴天强 on 2022/1/19. + */ +public class NonTerminalExpression implements AbstractExpression { + private AbstractExpression expression1; + private AbstractExpression expression2; + + public NonTerminalExpression(AbstractExpression expression1, AbstractExpression expression2) { + this.expression1 = expression1; + this.expression2 = expression2; + } + + @Override + public boolean interpret(GameUser info) { + //非对终结符表达式的处理 + return expression1.interpret(info) && expression2.interpret(info); + } +} + diff --git a/app/src/main/java/com/wss/amd/note/designpattern/iterator/Aggregate.java b/app/src/main/java/com/wss/amd/note/designpattern/iterator/Aggregate.java new file mode 100644 index 0000000..08c2560 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/iterator/Aggregate.java @@ -0,0 +1,12 @@ +package com.wss.amd.note.designpattern.iterator; + +/** + * Describe:聚合接口 + * Created by 吴天强 on 2022/1/19. + */ +public interface Aggregate { + + void add(E object); + + void remove(E object); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/iterator/Iterator.java b/app/src/main/java/com/wss/amd/note/designpattern/iterator/Iterator.java new file mode 100644 index 0000000..9066dd4 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/iterator/Iterator.java @@ -0,0 +1,14 @@ +package com.wss.amd.note.designpattern.iterator; + +/** + * Describe:迭代器接口 + * Created by 吴天强 on 2022/1/19. + */ +public interface Iterator { + + E first(); + + E next(); + + boolean hasNext(); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/iterator/UserAggregate.java b/app/src/main/java/com/wss/amd/note/designpattern/iterator/UserAggregate.java new file mode 100644 index 0000000..3a54589 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/iterator/UserAggregate.java @@ -0,0 +1,27 @@ +package com.wss.amd.note.designpattern.iterator; + +import java.util.ArrayList; +import java.util.List; + +/** + * Describe: + * Created by 吴天强 on 2022/1/19. + */ +public class UserAggregate implements Aggregate { + private List list = new ArrayList<>(); + + @Override + public void add(E object) { + list.add(object); + } + + @Override + public void remove(E object) { + list.remove(object); + } + + public Iterator iterator() { + return new UserIterator(list); + } + +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/iterator/UserIterator.java b/app/src/main/java/com/wss/amd/note/designpattern/iterator/UserIterator.java new file mode 100644 index 0000000..d8263bc --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/iterator/UserIterator.java @@ -0,0 +1,36 @@ +package com.wss.amd.note.designpattern.iterator; + +import java.util.List; + +/** + * Describe: + * Created by 吴天强 on 2022/1/19. + */ +public class UserIterator implements Iterator { + private List list = null; + private int index = -1; + + public UserIterator(List list) { + this.list = list; + } + + @Override + public E first() { + index = 0; + return list.get(index); + } + + @Override + public E next() { + E obj = null; + if (this.hasNext()) { + obj = list.get(++index); + } + return obj; + } + + @Override + public boolean hasNext() { + return index < list.size() - 1; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/mediator/Colleague.java b/app/src/main/java/com/wss/amd/note/designpattern/mediator/Colleague.java new file mode 100644 index 0000000..96578cd --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/mediator/Colleague.java @@ -0,0 +1,17 @@ +package com.wss.amd.note.designpattern.mediator; + +/** + * Describe: + * Created by 吴天强 on 2022/1/19. + */ +public abstract class Colleague { + protected Mediator mediator; + + public void setMedium(Mediator mediator) { + this.mediator = mediator; + } + + public abstract void receive(); + + public abstract void send(); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/mediator/ConcreteColleague1.java b/app/src/main/java/com/wss/amd/note/designpattern/mediator/ConcreteColleague1.java new file mode 100644 index 0000000..3c656ef --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/mediator/ConcreteColleague1.java @@ -0,0 +1,18 @@ +package com.wss.amd.note.designpattern.mediator; + +/** + * Describe: + * Created by 吴天强 on 2022/1/19. + */ +public class ConcreteColleague1 extends Colleague { + @Override + public void receive() { + System.out.println("具体同事类1收到请求。"); + } + + @Override + public void send() { + System.out.println("具体同事类1发出请求。"); + mediator.relay(this); //请中介者转发 + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/mediator/ConcreteColleague2.java b/app/src/main/java/com/wss/amd/note/designpattern/mediator/ConcreteColleague2.java new file mode 100644 index 0000000..e3a42c4 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/mediator/ConcreteColleague2.java @@ -0,0 +1,19 @@ +package com.wss.amd.note.designpattern.mediator; + +/** + * Describe: + * Created by 吴天强 on 2022/1/19. + */ +public class ConcreteColleague2 extends Colleague { + + @Override + public void receive() { + System.out.println("具体同事类2收到请求。"); + } + + @Override + public void send() { + System.out.println("具体同事类2发出请求。"); + mediator.relay(this); //请中介者转发 + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/mediator/ConcreteMediator.java b/app/src/main/java/com/wss/amd/note/designpattern/mediator/ConcreteMediator.java new file mode 100644 index 0000000..27416bc --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/mediator/ConcreteMediator.java @@ -0,0 +1,29 @@ +package com.wss.amd.note.designpattern.mediator; + +import java.util.ArrayList; +import java.util.List; + +/** + * Describe: + * Created by 吴天强 on 2022/1/19. + */ +public class ConcreteMediator implements Mediator { + private List colleagues = new ArrayList<>(); + + @Override + public void register(Colleague colleague) { + if (!colleagues.contains(colleague)) { + colleagues.add(colleague); + colleague.setMedium(this); + } + } + + @Override + public void relay(Colleague cl) { + for (Colleague ob : colleagues) { + if (!ob.equals(cl)) { + ob.receive(); + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wss/amd/note/designpattern/mediator/Mediator.java b/app/src/main/java/com/wss/amd/note/designpattern/mediator/Mediator.java new file mode 100644 index 0000000..a68f7ca --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/mediator/Mediator.java @@ -0,0 +1,11 @@ +package com.wss.amd.note.designpattern.mediator; + +/** + * Describe: + * Created by 吴天强 on 2022/1/19. + */ +public interface Mediator { + void register(Colleague colleague); + + void relay(Colleague cl); //转发 +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/memorandum/Caretaker.java b/app/src/main/java/com/wss/amd/note/designpattern/memorandum/Caretaker.java new file mode 100644 index 0000000..58d3cf2 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/memorandum/Caretaker.java @@ -0,0 +1,18 @@ +package com.wss.amd.note.designpattern.memorandum; + +/** + * Describe:管理者 + * Created by 吴天强 on 2022/1/19. + */ +public class Caretaker { + + private Memento memento; + + public Memento getMemento() { + return memento; + } + + public void setMemento(Memento memento) { + this.memento = memento; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/memorandum/Memento.java b/app/src/main/java/com/wss/amd/note/designpattern/memorandum/Memento.java new file mode 100644 index 0000000..dae773d --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/memorandum/Memento.java @@ -0,0 +1,23 @@ +package com.wss.amd.note.designpattern.memorandum; + +/** + * Describe:备忘录 + * Created by 吴天强 on 2022/1/19. + */ +public class Memento { + + private String state; + + public Memento(String state) { + this.state = state; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } +} + diff --git a/app/src/main/java/com/wss/amd/note/designpattern/memorandum/Originator.java b/app/src/main/java/com/wss/amd/note/designpattern/memorandum/Originator.java new file mode 100644 index 0000000..55d35b7 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/memorandum/Originator.java @@ -0,0 +1,27 @@ +package com.wss.amd.note.designpattern.memorandum; + +import org.jetbrains.annotations.NotNull; + +/** + * Describe:发起人 + * Created by 吴天强 on 2022/1/19. + */ +public class Originator { + private String state; + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public Memento createMemento() { + return new Memento(this.getState()); + } + + public void restoreMemento(@NotNull Memento m) { + this.setState(m.getState()); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/observer/BinaryObserver.java b/app/src/main/java/com/wss/amd/note/designpattern/observer/BinaryObserver.java new file mode 100644 index 0000000..56c948f --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/observer/BinaryObserver.java @@ -0,0 +1,19 @@ +package com.wss.amd.note.designpattern.observer; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public class BinaryObserver extends Observer { + public BinaryObserver(Subject subject) { + this.subject = subject; + // 通常在构造方法中将 this 发布出去的操作一定要小心 + this.subject.attach(this); + } + + @Override + public void update() { + String result = Integer.toBinaryString(subject.getState()); + System.out.println("订阅的数据发生变化,新的数据处理为二进制值为:" + result); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/observer/HexaObserver.java b/app/src/main/java/com/wss/amd/note/designpattern/observer/HexaObserver.java new file mode 100644 index 0000000..efae8dc --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/observer/HexaObserver.java @@ -0,0 +1,18 @@ +package com.wss.amd.note.designpattern.observer; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public class HexaObserver extends Observer { + public HexaObserver(Subject subject) { + this.subject = subject; + this.subject.attach(this); + } + + @Override + public void update() { + String result = Integer.toHexString(subject.getState()); + System.out.println("订阅的数据发生变化,新的数据处理为十六进制值为:" + result); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/observer/Observer.java b/app/src/main/java/com/wss/amd/note/designpattern/observer/Observer.java new file mode 100644 index 0000000..f865bda --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/observer/Observer.java @@ -0,0 +1,12 @@ +package com.wss.amd.note.designpattern.observer; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public abstract class Observer { + + protected Subject subject; + + public abstract void update(); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/observer/Subject.java b/app/src/main/java/com/wss/amd/note/designpattern/observer/Subject.java new file mode 100644 index 0000000..a979207 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/observer/Subject.java @@ -0,0 +1,37 @@ +package com.wss.amd.note.designpattern.observer; + +import java.util.ArrayList; +import java.util.List; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public class Subject { + private List observerList = new ArrayList<>(); + + private int state; + + public void attach(Observer observer) { + observerList.add(observer); + } + + public int getState() { + return state; + } + + public void setState(int state) { + this.state = state; + //数据已变更,通知更新 + notifyAllObservers(); + } + + + private void notifyAllObservers() { + for (Observer observer : observerList) { + observer.update(); + } + } +} + + diff --git a/app/src/main/java/com/wss/amd/note/designpattern/proxy/User.java b/app/src/main/java/com/wss/amd/note/designpattern/proxy/User.java new file mode 100644 index 0000000..65fc591 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/proxy/User.java @@ -0,0 +1,32 @@ +package com.wss.amd.note.designpattern.proxy; + +import lombok.Getter; +import lombok.Setter; + +/** + * Describe: + * Created by 吴天强 on 2022/1/17. + */ +@Getter +@Setter +public class User { + + private String name; + private String password; + private int age; + private String dec; + + public User(String name) { + this.name = name; + } + + public User() { + } + + @Override + public String toString() { + return "User{" + + "name='" + name + '\'' + + '}'; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/proxy/UserService.java b/app/src/main/java/com/wss/amd/note/designpattern/proxy/UserService.java new file mode 100644 index 0000000..34d8ba0 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/proxy/UserService.java @@ -0,0 +1,13 @@ +package com.wss.amd.note.designpattern.proxy; + +/** + * Describe: + * Created by 吴天强 on 2022/1/17. + */ +public interface UserService { + + User creteAdmin(); + + User createUser(); + +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/proxy/UserServiceImpl.java b/app/src/main/java/com/wss/amd/note/designpattern/proxy/UserServiceImpl.java new file mode 100644 index 0000000..15ec082 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/proxy/UserServiceImpl.java @@ -0,0 +1,24 @@ +package com.wss.amd.note.designpattern.proxy; + +/** + * Describe:创建用户实现 + * Created by 吴天强 on 2022/1/17. + */ +public class UserServiceImpl implements UserService { + @Override + public User creteAdmin() { + User user = new User(); + user.setName("admin"); + user.setPassword("000000"); + return user; + } + + @Override + public User createUser() { + User user = new User(); + user.setName("Tom"); + user.setPassword("000000"); + user.setAge(18); + return user; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/proxy/UserServiceProxy.java b/app/src/main/java/com/wss/amd/note/designpattern/proxy/UserServiceProxy.java new file mode 100644 index 0000000..099cd8f --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/proxy/UserServiceProxy.java @@ -0,0 +1,26 @@ +package com.wss.amd.note.designpattern.proxy; + +/** + * Describe:代理类 + * Created by 吴天强 on 2022/1/17. + */ +public class UserServiceProxy implements UserService { + + private UserService userService = new UserServiceImpl(); + + @Override + public User creteAdmin() { + System.out.println("代理类开始创建管理员"); + User user = userService.creteAdmin(); + user.setDec("由代理类创建的管理员"); + return user; + } + + @Override + public User createUser() { + System.out.println("代理类开始创建用户"); + User user = userService.creteAdmin(); + user.setDec("由代理类创建的用户"); + return user; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/responsibility/LocationRuleHandler.java b/app/src/main/java/com/wss/amd/note/designpattern/responsibility/LocationRuleHandler.java new file mode 100644 index 0000000..5f61eec --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/responsibility/LocationRuleHandler.java @@ -0,0 +1,22 @@ +package com.wss.amd.note.designpattern.responsibility; + +import org.jetbrains.annotations.NotNull; + +/** + * Describe:地理位置规则 + * Created by 吴天强 on 2022/1/18. + */ +public class LocationRuleHandler extends RuleHandler { + private static final String LOCATION = "上海"; + + @Override + public void apply(@NotNull People people) { + if (LOCATION.equalsIgnoreCase(people.getLocation())) { + if (this.getSuccessor() != null) { + this.getSuccessor().apply(people); + } + } else { + throw new RuntimeException("该活动仅限上海地区参与"); + } + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/responsibility/NewUserRuleHandler.java b/app/src/main/java/com/wss/amd/note/designpattern/responsibility/NewUserRuleHandler.java new file mode 100644 index 0000000..c14cf88 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/responsibility/NewUserRuleHandler.java @@ -0,0 +1,22 @@ +package com.wss.amd.note.designpattern.responsibility; + +import org.jetbrains.annotations.NotNull; + +/** + * Describe:新用户规则 + * Created by 吴天强 on 2022/1/18. + */ +public class NewUserRuleHandler extends RuleHandler { + + + @Override + public void apply(@NotNull People people) { + if (people.isNewUser()) { + if (this.getSuccessor() != null) { + this.getSuccessor().apply(people); + } + } else { + throw new RuntimeException("该活动仅限新用户参与"); + } + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/responsibility/ParticipantsRuleHandler.java b/app/src/main/java/com/wss/amd/note/designpattern/responsibility/ParticipantsRuleHandler.java new file mode 100644 index 0000000..c73e2db --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/responsibility/ParticipantsRuleHandler.java @@ -0,0 +1,22 @@ +package com.wss.amd.note.designpattern.responsibility; + +import org.jetbrains.annotations.NotNull; + +/** + * Describe:参与人数规则 + * Created by 吴天强 on 2022/1/18. + */ +public class ParticipantsRuleHandler extends RuleHandler { + private static final int MAX_PARTICIPANTS = 10; + + @Override + public void apply(@NotNull People people) { + if (people.getParticipants() <= MAX_PARTICIPANTS) { + if (this.getSuccessor() != null) { + this.getSuccessor().apply(people); + } + } else { + throw new RuntimeException("该活动单次仅限" + MAX_PARTICIPANTS + "人参数"); + } + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/responsibility/People.java b/app/src/main/java/com/wss/amd/note/designpattern/responsibility/People.java new file mode 100644 index 0000000..9c98bd4 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/responsibility/People.java @@ -0,0 +1,16 @@ +package com.wss.amd.note.designpattern.responsibility; + +import lombok.Getter; +import lombok.Setter; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +@Getter +@Setter +public class People { + private boolean newUser; + private String location; + private int participants; +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/responsibility/RuleHandler.java b/app/src/main/java/com/wss/amd/note/designpattern/responsibility/RuleHandler.java new file mode 100644 index 0000000..7f0f532 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/responsibility/RuleHandler.java @@ -0,0 +1,22 @@ +package com.wss.amd.note.designpattern.responsibility; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public abstract class RuleHandler { + + private RuleHandler successor; + + + public RuleHandler getSuccessor() { + return successor; + } + + public void setSuccessor(RuleHandler successor) { + this.successor = successor; + } + + + public abstract void apply(People people); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/singleton/FullMan.java b/app/src/main/java/com/wss/amd/note/designpattern/singleton/FullMan.java new file mode 100644 index 0000000..05c4281 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/singleton/FullMan.java @@ -0,0 +1,26 @@ +package com.wss.amd.note.designpattern.singleton; + +/** + * Describe:单例模式 - 饱汉模式 + * Created by 吴天强 on 2022/1/14. + */ +public class FullMan { + private FullMan() { + } + + // 和饿汉模式相比,这边不需要先实例化出来,注意这里的 volatile,它是必须的 + private static volatile FullMan instance = null; + + public static FullMan getInstance() { + if (instance == null) { + // 加锁 + synchronized (FullMan.class) { + // 这一次判断也是必须的,不然会有并发问题 + if (instance == null) { + instance = new FullMan(); + } + } + } + return instance; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wss/amd/note/designpattern/singleton/HungryMan.java b/app/src/main/java/com/wss/amd/note/designpattern/singleton/HungryMan.java new file mode 100644 index 0000000..fe49309 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/singleton/HungryMan.java @@ -0,0 +1,17 @@ +package com.wss.amd.note.designpattern.singleton; + +/** + * Describe:单例模式 - 饿汉模式 + * Created by 吴天强 on 2022/1/14. + */ +public class HungryMan { + private HungryMan() { + } + + // 创建私有静态实例,意味着这个类第一次使用的时候就会进行创建 + private static HungryMan instance = new HungryMan(); + + public static HungryMan getInstance() { + return instance; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/singleton/NestedClass.java b/app/src/main/java/com/wss/amd/note/designpattern/singleton/NestedClass.java new file mode 100644 index 0000000..4ebb988 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/singleton/NestedClass.java @@ -0,0 +1,19 @@ +package com.wss.amd.note.designpattern.singleton; + +/** + * Describe:单例模式 - 嵌套类 + * Created by 吴天强 on 2022/1/14. + */ +public class NestedClass { + private NestedClass() { + } + + //主要是使用了 嵌套类可以访问外部类的静态属性和静态方法 的特性 + private static class Holder { + private static NestedClass instance = new NestedClass(); + } + + public static NestedClass getInstance() { + return Holder.instance; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/state/InvisibleState.java b/app/src/main/java/com/wss/amd/note/designpattern/state/InvisibleState.java new file mode 100644 index 0000000..3f07bdd --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/state/InvisibleState.java @@ -0,0 +1,24 @@ +package com.wss.amd.note.designpattern.state; + +import org.jetbrains.annotations.NotNull; + +import androidx.annotation.NonNull; + +/** + * Describe:隐身 + * Created by 吴天强 on 2022/1/18. + */ +public class InvisibleState implements State { + @Override + public void doAction(@NotNull QQUser qqUser) { + System.out.println("此时QQ用户隐身啦~"); + qqUser.setState(this); + + } + + @NonNull + @Override + public String toString() { + return "InvisibleState"; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/state/OnLineState.java b/app/src/main/java/com/wss/amd/note/designpattern/state/OnLineState.java new file mode 100644 index 0000000..8ddf781 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/state/OnLineState.java @@ -0,0 +1,23 @@ +package com.wss.amd.note.designpattern.state; + +import org.jetbrains.annotations.NotNull; + +import androidx.annotation.NonNull; + +/** + * Describe:上线 + * Created by 吴天强 on 2022/1/18. + */ +public class OnLineState implements State { + @Override + public void doAction(@NotNull QQUser qqUser) { + System.out.println("此时QQ用户在线了~"); + qqUser.setState(this); + } + + @NonNull + @Override + public String toString() { + return "OnLineState"; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/state/QQUser.java b/app/src/main/java/com/wss/amd/note/designpattern/state/QQUser.java new file mode 100644 index 0000000..5451132 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/state/QQUser.java @@ -0,0 +1,19 @@ +package com.wss.amd.note.designpattern.state; + +import lombok.Getter; +import lombok.Setter; + +/** + * Describe:QQ用户 + * Created by 吴天强 on 2022/1/18. + */ +@Getter +@Setter +public class QQUser { + private State state; + private String name; + + public QQUser(String name) { + this.name = name; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/state/State.java b/app/src/main/java/com/wss/amd/note/designpattern/state/State.java new file mode 100644 index 0000000..e767bbd --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/state/State.java @@ -0,0 +1,10 @@ +package com.wss.amd.note.designpattern.state; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public interface State { + + void doAction(QQUser QQUser); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/strategy/NoodleStrategy.java b/app/src/main/java/com/wss/amd/note/designpattern/strategy/NoodleStrategy.java new file mode 100644 index 0000000..d7d5cd6 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/strategy/NoodleStrategy.java @@ -0,0 +1,12 @@ +package com.wss.amd.note.designpattern.strategy; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public class NoodleStrategy implements Strategy { + @Override + public void eat(int count) { + System.out.println("今天" + count + "人吃面条吧!!!"); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/strategy/NoonEatWhat.java b/app/src/main/java/com/wss/amd/note/designpattern/strategy/NoonEatWhat.java new file mode 100644 index 0000000..74515cb --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/strategy/NoonEatWhat.java @@ -0,0 +1,18 @@ +package com.wss.amd.note.designpattern.strategy; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public class NoonEatWhat { + + private Strategy strategy; + + public NoonEatWhat(Strategy strategy) { + this.strategy = strategy; + } + + public void executeEat(int cont) { + strategy.eat(cont); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/strategy/RiceStrategy.java b/app/src/main/java/com/wss/amd/note/designpattern/strategy/RiceStrategy.java new file mode 100644 index 0000000..07b529e --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/strategy/RiceStrategy.java @@ -0,0 +1,12 @@ +package com.wss.amd.note.designpattern.strategy; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public class RiceStrategy implements Strategy { + @Override + public void eat(int count) { + System.out.println("今天" + count + "人吃米饭呀~~"); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/strategy/Strategy.java b/app/src/main/java/com/wss/amd/note/designpattern/strategy/Strategy.java new file mode 100644 index 0000000..b9adcbc --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/strategy/Strategy.java @@ -0,0 +1,10 @@ +package com.wss.amd.note.designpattern.strategy; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public interface Strategy { + + void eat(int count); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/template/AbsTemplateMethod.java b/app/src/main/java/com/wss/amd/note/designpattern/template/AbsTemplateMethod.java new file mode 100644 index 0000000..fc15bd1 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/template/AbsTemplateMethod.java @@ -0,0 +1,26 @@ +package com.wss.amd.note.designpattern.template; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public abstract class AbsTemplateMethod { + + + public void templateMethod() { + init(); + ready(); + end(); + } + + protected void init() { + System.out.println("基类已经实现init"); + } + + protected abstract void ready(); + + protected void end() { + } + + +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/template/TemplateMethod.java b/app/src/main/java/com/wss/amd/note/designpattern/template/TemplateMethod.java new file mode 100644 index 0000000..5f0c6a5 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/template/TemplateMethod.java @@ -0,0 +1,18 @@ +package com.wss.amd.note.designpattern.template; + +/** + * Describe: + * Created by 吴天强 on 2022/1/18. + */ +public class TemplateMethod extends AbsTemplateMethod { + @Override + protected void ready() { + System.out.println("子类必须实现的抽象方法"); + } + + @Override + protected void end() { + super.end(); + System.out.println("子类可覆写的父类已经实现的方法"); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/visitor/SetMaterial.java b/app/src/main/java/com/wss/amd/note/designpattern/visitor/SetMaterial.java new file mode 100644 index 0000000..6ffd3c9 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/visitor/SetMaterial.java @@ -0,0 +1,33 @@ +package com.wss.amd.note.designpattern.visitor; + +import com.wss.amd.note.designpattern.visitor.company.Company; +import com.wss.amd.note.designpattern.visitor.material.Material; + +import java.util.ArrayList; +import java.util.List; + +/** + * Describe:对象结构角色:材料集 + * Created by 吴天强 on 2022/1/19. + */ +public class SetMaterial { + + private List list = new ArrayList<>(); + + + public String accept(Company visitor) { + StringBuilder sb = new StringBuilder(); + for (Material m : list) { + sb.append(m.accept(visitor)).append(","); + } + return sb.toString(); + } + + public void add(Material element) { + list.add(element); + } + + public void remove(Material element) { + list.remove(element); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/visitor/company/ArtCompany.java b/app/src/main/java/com/wss/amd/note/designpattern/visitor/company/ArtCompany.java new file mode 100644 index 0000000..5ce4e31 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/visitor/company/ArtCompany.java @@ -0,0 +1,20 @@ +package com.wss.amd.note.designpattern.visitor.company; + +import com.wss.amd.note.designpattern.visitor.material.Copper; +import com.wss.amd.note.designpattern.visitor.material.Paper; + +/** + * Describe:具体访问者:艺术公司 + * Created by 吴天强 on 2022/1/19. + */ +public class ArtCompany implements Company { + @Override + public String create(Copper copper) { + return "讲学图"; + } + + @Override + public String create(Paper paper) { + return "朱熹铜像"; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/visitor/company/Company.java b/app/src/main/java/com/wss/amd/note/designpattern/visitor/company/Company.java new file mode 100644 index 0000000..c6891ec --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/visitor/company/Company.java @@ -0,0 +1,16 @@ +package com.wss.amd.note.designpattern.visitor.company; + +import com.wss.amd.note.designpattern.visitor.material.Copper; +import com.wss.amd.note.designpattern.visitor.material.Paper; + +/** + * Describe:定义公司接口,分别可以制造纸制品 和铜制品 + * Created by 吴天强 on 2022/1/19. + */ +public interface Company { + + String create(Copper copper); + + String create(Paper paper); + +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/visitor/company/MintCompany.java b/app/src/main/java/com/wss/amd/note/designpattern/visitor/company/MintCompany.java new file mode 100644 index 0000000..856c9ac --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/visitor/company/MintCompany.java @@ -0,0 +1,20 @@ +package com.wss.amd.note.designpattern.visitor.company; + +import com.wss.amd.note.designpattern.visitor.material.Copper; +import com.wss.amd.note.designpattern.visitor.material.Paper; + +/** + * Describe:具体访问者:造币公司 + * Created by 吴天强 on 2022/1/19. + */ +public class MintCompany implements Company { + @Override + public String create(Copper copper) { + return "纸币"; + } + + @Override + public String create(Paper paper) { + return "铜币"; + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/visitor/material/Copper.java b/app/src/main/java/com/wss/amd/note/designpattern/visitor/material/Copper.java new file mode 100644 index 0000000..94b2851 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/visitor/material/Copper.java @@ -0,0 +1,16 @@ +package com.wss.amd.note.designpattern.visitor.material; + +import com.wss.amd.note.designpattern.visitor.company.Company; + +import org.jetbrains.annotations.NotNull; + +/** + * Describe:具体元素:铜 + * Created by 吴天强 on 2022/1/19. + */ +public class Copper implements Material { + @Override + public String accept(@NotNull Company visitor) { + return visitor.create(this); + } +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/visitor/material/Material.java b/app/src/main/java/com/wss/amd/note/designpattern/visitor/material/Material.java new file mode 100644 index 0000000..d5e8025 --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/visitor/material/Material.java @@ -0,0 +1,12 @@ +package com.wss.amd.note.designpattern.visitor.material; + +import com.wss.amd.note.designpattern.visitor.company.Company; + +/** + * Describe: + * Created by 吴天强 on 2022/1/19. + */ +public interface Material { + + String accept(Company visitor); +} diff --git a/app/src/main/java/com/wss/amd/note/designpattern/visitor/material/Paper.java b/app/src/main/java/com/wss/amd/note/designpattern/visitor/material/Paper.java new file mode 100644 index 0000000..86fb84a --- /dev/null +++ b/app/src/main/java/com/wss/amd/note/designpattern/visitor/material/Paper.java @@ -0,0 +1,16 @@ +package com.wss.amd.note.designpattern.visitor.material; + +import com.wss.amd.note.designpattern.visitor.company.Company; + +import org.jetbrains.annotations.NotNull; + +/** + * Describe:具体元素:纸 + * Created by 吴天强 on 2022/1/19. + */ +public class Paper implements Material { + @Override + public String accept(@NotNull Company visitor) { + return visitor.create(this); + } +} diff --git a/app/src/main/java/com/wss/amd/profile/DevProfile.java b/app/src/main/java/com/wss/amd/profile/DevProfile.java new file mode 100644 index 0000000..52a2ebf --- /dev/null +++ b/app/src/main/java/com/wss/amd/profile/DevProfile.java @@ -0,0 +1,31 @@ +package com.wss.amd.profile; + + +import com.wss.common.profile.IProfile; + +/** + * Describe:开发环境配置 + * Created by 吴天强 on 2020/4/13. + */ +public class DevProfile implements IProfile { + + /** + * Api服务器地址 + */ + private static final String SERVICE_BAR_URL = "https://www.wanandroid.com"; + + @Override + public String getServiceBase() { + return SERVICE_BAR_URL; + } + + @Override + public boolean isSecret() { + return false; + } + + @Override + public String getAesSecretKey() { + return "t96IBJiOSURJZOzdjQ36pw=="; + } +} diff --git a/app/src/main/java/com/wss/amd/profile/ProProfile.java b/app/src/main/java/com/wss/amd/profile/ProProfile.java new file mode 100644 index 0000000..71ac39e --- /dev/null +++ b/app/src/main/java/com/wss/amd/profile/ProProfile.java @@ -0,0 +1,31 @@ +package com.wss.amd.profile; + + +import com.wss.common.profile.IProfile; + +/** + * Describe:生产环境配置 + * Created by 吴天强 on 2021/11/15. + */ +public class ProProfile implements IProfile { + + /** + * Api服务器地址 + */ + private static final String SERVICE_BAR_URL = "https://www.wanandroid.com"; + + @Override + public String getServiceBase() { + return SERVICE_BAR_URL; + } + + @Override + public boolean isSecret() { + return true; + } + + @Override + public String getAesSecretKey() { + return "t96IBJiOSURJZOzdjQ36pw=="; + } +} diff --git a/app/src/main/java/com/wss/amd/profile/ProfileFactory.java b/app/src/main/java/com/wss/amd/profile/ProfileFactory.java new file mode 100644 index 0000000..6f9f9fb --- /dev/null +++ b/app/src/main/java/com/wss/amd/profile/ProfileFactory.java @@ -0,0 +1,32 @@ +package com.wss.amd.profile; + + +import com.wss.amd.BuildConfig; +import com.wss.common.profile.IProfile; +import com.wss.common.profile.IProfileFactory; + +/** + * Describe:创建环境工厂 + * Created by 吴天强 on 2020/4/13. + */ +public class ProfileFactory implements IProfileFactory { + /** + * 开发环境 + */ + private static final String DEV = "DEV"; + /** + * 生产环境 + */ + private static final String PRO = "PRO"; + + @Override + public IProfile createProfile() { + switch (BuildConfig.FLAVOR) { + case PRO: + return new ProProfile(); + case DEV: + default: + return new DevProfile(); + } + } +} diff --git a/app/src/main/res/layout/activity_loading.xml b/app/src/main/res/layout/activity_loading.xml new file mode 100644 index 0000000..0b6d228 --- /dev/null +++ b/app/src/main/res/layout/activity_loading.xml @@ -0,0 +1,30 @@ + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6862253..989abce 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,3 @@ - AMD + diff --git a/build.gradle b/build.gradle index a453a48..b95a8be 100644 --- a/build.gradle +++ b/build.gradle @@ -1,28 +1,39 @@ apply from: "config.gradle" buildscript { - repositories { - google() - jcenter() - mavenCentral() + maven { url 'https://maven.aliyun.com/repository/google' } + maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter' } + maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } +// google() +// jcenter( } dependencies { - classpath "com.android.tools.build:gradle:3.0.1" - //黄油刀 - classpath "com.jakewharton:butterknife-gradle-plugin:8.4.0" + classpath 'com.android.tools.build:gradle:3.6.2' + classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.1' //数据库GreenDAO classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' + //听云 } +} - allprojects { - repositories { - google() - jcenter() - maven { url 'https://jitpack.io' } - } +allprojects { + repositories { + maven { url "https://dl.bintray.com/tencentqcloudterminal/maven" } + maven { url 'https://maven.aliyun.com/repository/google' } + maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter' } + maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } + maven { url 'https://jitpack.io' } + maven { url "http://mvn.gt.igexin.com/nexus/content/repositories/releases/" } + maven { url 'https://gitee.com/liuchaoya/libcommon/raw/master/repository/' } +// google() +// jcenter() } } + task clean(type: Delete) { delete rootProject.buildDir } + + + diff --git a/common_base/build.gradle b/common_base/build.gradle index 170748c..a364a2f 100644 --- a/common_base/build.gradle +++ b/common_base/build.gradle @@ -1,5 +1,6 @@ apply plugin: 'com.android.library' apply plugin: 'com.jakewharton.butterknife' +apply plugin: 'org.greenrobot.greendao' android { compileSdkVersion rootProject.ext.versions.compileSdkVersion @@ -34,51 +35,61 @@ android { dexOptions { javaMaxHeapSize "4g" } - - + compileOptions { + targetCompatibility rootProject.ext.versions.javaSDKVersion + sourceCompatibility rootProject.ext.versions.javaSDKVersion + } } dependencies { // 在项目中的libs中的所有的.jar结尾的文件,都是依赖 - compile fileTree(include: ['*.jar'], dir: 'libs') - //把implementation 用api代替,它是对外部公开的, 所有其他的module就不需要添加该依赖 - api rootProject.ext.dependencies["appcompat_v7"] - api rootProject.ext.dependencies["constraint_layout"] - api rootProject.ext.dependencies["cardview-v7"] - api rootProject.ext.dependencies["recyclerview-v7"] - api rootProject.ext.dependencies["support-v4"] - api rootProject.ext.dependencies["design"] - api rootProject.ext.dependencies["support_annotations"] - //MultiDex分包方法 + api fileTree(include: ['*.jar'], dir: 'libs') + //android基础库 + api rootProject.ext.dependencies["appcompat"] + api rootProject.ext.dependencies["recyclerview"] + api rootProject.ext.dependencies["cardview"] + api rootProject.ext.dependencies["annotation"] + api rootProject.ext.dependencies["material"] api rootProject.ext.dependencies["multidex"] - //黄油刀 - annotationProcessor rootProject.ext.dependencies["butterknife_compiler"] + api rootProject.ext.dependencies["junit"] + api rootProject.ext.dependencies["swiperefreshlayout"] + api rootProject.ext.dependencies["localbroadcastmanager"] + //Lombok + api rootProject.ext.dependencies["lombok"] + api rootProject.ext.dependencies["javaxAnnotation"] + //阿里ARouter + api rootProject.ext.dependencies["arouter"] + annotationProcessor rootProject.ext.dependencies["arouterCompiler"] + //Butterknife api rootProject.ext.dependencies["butterknife"] - //Arouter路由 - annotationProcessor rootProject.ext.dependencies["arouter_compiler"] - api rootProject.ext.dependencies["arouter_api"] - api rootProject.ext.dependencies["arouter_annotation"] - //eventbus 发布/订阅事件总线 - api rootProject.ext.dependencies["eventbus"] - //网络 - api rootProject.ext.dependencies["novate"] - //日志 + annotationProcessor rootProject.ext.dependencies["butterknifeCompiler"] + //EventBus + api rootProject.ext.dependencies["eventBus"] + //Logger api rootProject.ext.dependencies["logger"] - //fastJson - api rootProject.ext.dependencies["fastjson"] //沉浸栏 - api rootProject.ext.dependencies["barlibrary"] - //banner + api rootProject.ext.dependencies["immersionbar"] + //Bnnaer api rootProject.ext.dependencies["banner"] - //图片加载 - api rootProject.ext.dependencies["picasso"] - //lombok - api rootProject.ext.dependencies["lombok"] - api rootProject.ext.dependencies["lombokJavax"] - //时间 日期 地址 条件选中器 + //Glude + api rootProject.ext.dependencies["glide"] + //弹窗选择器 api rootProject.ext.dependencies["pickerView"] - //万能Adapter + //危险权限申请 + api rootProject.ext.dependencies["xxpermissions"] + //网络 + api rootProject.ext.dependencies["rxHttp"] + api rootProject.ext.dependencies["rxLife"] + api rootProject.ext.dependencies["rxAndroid"] + annotationProcessor rootProject.ext.dependencies["rxCompiler"] + //万能适配器 api rootProject.ext.dependencies["superAdapter"] - - + //图片预览 + api rootProject.ext.dependencies["scaleImageView"] + //二维码扫描 + api rootProject.ext.dependencies["zxing"] + //刷新库 + api rootProject.ext.dependencies["refreshLayout"] + //GreenDao + api rootProject.ext.dependencies["greenDao"] } diff --git a/common_base/doc.txt b/common_base/doc.txt index ee0c71a..17a85e5 100644 --- a/common_base/doc.txt +++ b/common_base/doc.txt @@ -1,3 +1,8 @@ 说明:该module为所有业务Module必须依赖的lib - 其中包含了第三方库的加载、网络请求初始化、日志、公共资源文件、公共方法的抽取等。 \ No newline at end of file + 其中包含了第三方库的加载、网络请求初始化、日志、公共资源文件、公共方法的抽取等。 + + +Jar包说明 + +bcprov-1.3.3.jar Aes加密接用到该包 \ No newline at end of file diff --git a/common_base/libs/bcprov-1.3.3.jar b/common_base/libs/bcprov-1.3.3.jar new file mode 100644 index 0000000..4554ce5 Binary files /dev/null and b/common_base/libs/bcprov-1.3.3.jar differ diff --git a/common_base/src/main/AndroidManifest.xml b/common_base/src/main/AndroidManifest.xml index a4ea8b7..146ce8a 100644 --- a/common_base/src/main/AndroidManifest.xml +++ b/common_base/src/main/AndroidManifest.xml @@ -9,14 +9,17 @@ + + - + + diff --git a/common_base/src/main/assets/iconfont/iconfont.ttf b/common_base/src/main/assets/iconfont/iconfont.ttf new file mode 100644 index 0000000..e24ea22 Binary files /dev/null and b/common_base/src/main/assets/iconfont/iconfont.ttf differ diff --git a/common_base/src/main/java/com/wss/common/activity/WebViewActivity.java b/common_base/src/main/java/com/wss/common/activity/WebViewActivity.java deleted file mode 100644 index 5d252fb..0000000 --- a/common_base/src/main/java/com/wss/common/activity/WebViewActivity.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.wss.common.activity; - -import android.content.Context; -import android.content.Intent; -import android.net.http.SslError; -import android.view.KeyEvent; -import android.webkit.SslErrorHandler; -import android.webkit.WebView; -import android.webkit.WebViewClient; - -import com.wss.common.base.ActionBarActivity; -import com.wss.common.base.R; -import com.wss.common.base.R2; -import com.wss.common.base.mvp.BasePresenter; -import com.wss.common.widget.ProgressWebView; - -import butterknife.BindView; - -/** - * Describe:WevView Activity - * Created by 吴天强 on 2018/10/19. - */ - -public class WebViewActivity extends ActionBarActivity { - - @BindView(R2.id.pw_view) - ProgressWebView webView; - - - public static void actionStart(Context context, String url) { - Intent intent = new Intent(context, WebViewActivity.class); - intent.putExtra("URL", url); - context.startActivity(intent); - } - - @Override - protected BasePresenter createPresenter() { - return null; - } - - @Override - protected int getLayoutId() { - return R.layout.activity_webview; - } - - @Override - protected void initView() { - webView.loadUrl(getIntent().getStringExtra("URL")); - webView.setWebViewClient(new MyWebClient()); - } - - - private class MyWebClient extends WebViewClient { - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - webView.loadUrl(url); - return true; - } - - @Override - public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { - handler.proceed(); // 接受所有网站的证书 - } - - @Override - public void onPageFinished(WebView view, String url) { - super.onPageFinished(view, url); - setTitleText(view.getTitle()); - } - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK) { - showFinish(); - return true; - } else { - return super.onKeyDown(keyCode, event); - } - } - - - private void showFinish() { - if (webView.canGoBack()) { - webView.goBack(); - } else { - finish(); - } - } - -} diff --git a/common_base/src/main/java/com/wss/common/adapter/BannerImgAdapter.java b/common_base/src/main/java/com/wss/common/adapter/BannerImgAdapter.java new file mode 100644 index 0000000..1a0a6bc --- /dev/null +++ b/common_base/src/main/java/com/wss/common/adapter/BannerImgAdapter.java @@ -0,0 +1,72 @@ +package com.wss.common.adapter; + +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; + +import com.bigkoo.convenientbanner.holder.CBViewHolderCreator; +import com.bigkoo.convenientbanner.holder.Holder; +import com.wss.common.base.R; +import com.wss.common.bean.Banner; +import com.wss.common.utils.ImageUtils; + +/** + * Describe:轮播图默认适配器 适合只加载一张图的Banner + * Created by 吴天强 on 2018/11/1. + */ +public class BannerImgAdapter implements CBViewHolderCreator { + /** + * 是否加载圆角的 + */ + private boolean circle = false; + + public BannerImgAdapter() { + } + + public BannerImgAdapter(boolean circle) { + this.circle = circle; + } + + @Override + public Holder createHolder(View itemView) { + return new Holder(itemView) { + LinearLayout layout; + ImageView imageView; + + @Override + protected void initView(View itemView) { + layout = itemView.findViewById(R.id.ll_img_parent); + imageView = getImageView(); + if (imageView != null) { + layout.removeAllViews(); + layout.addView(imageView); + } else { + imageView = itemView.findViewById(R.id.image); + } + } + + @Override + public void updateUI(Banner data) { + if (circle) { + ImageUtils.loadImageCircleBead(imageView, data.getImageUrl(), 4); + } else { + ImageUtils.loadImage(imageView, data.getImageUrl()); + } + } + }; + } + + @Override + public int getLayoutId() { + return R.layout.layout_banner_img; + } + + /** + * 可以传入自定义的ImageView + * + * @return ImageView + */ + public ImageView getImageView() { + return null; + } +} diff --git a/common_base/src/main/java/com/wss/common/adapter/FragmentPagerAdapter.java b/common_base/src/main/java/com/wss/common/adapter/FragmentPagerAdapter.java new file mode 100644 index 0000000..3c5af2d --- /dev/null +++ b/common_base/src/main/java/com/wss/common/adapter/FragmentPagerAdapter.java @@ -0,0 +1,94 @@ +package com.wss.common.adapter; + +import android.util.Log; + +import com.wss.common.base.BaseFragment; +import com.wss.common.bean.HorizontalTabTitle; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentStatePagerAdapter; + + +/** + * Describe:滑动Fragment适配器 + * 如果滑动Fragment是同一个 使用双参构造方法 覆写 getTabFragment 返回Fragment + * 如果滑动Fragment是不同的Fragment 使用第三参构造方法 传入对应的Fragment集合 + *

+ * Created by 吴天强 on 2018/10/22. + */ +public class FragmentPagerAdapter extends FragmentStatePagerAdapter { + private List titles; + private List fragments; + + /** + * 使用该构造方法 必须重写 getTabFragment 返回对应的Fragment + * + * @param fm FragmentManager + * @param titles Tab + */ + public FragmentPagerAdapter(FragmentManager fm, List titles) { + this(fm, titles, null); + } + + /** + * @param fm FragmentManager + * @param titles Tab + * @param fragments 滑动的Fragment集合 + */ + public FragmentPagerAdapter(FragmentManager fm, List titles, List fragments) { + super(fm); + this.titles = titles; + this.fragments = fragments; + } + + + @Override + public int getCount() { + return titles != null ? titles.size() : 0; + } + + + @NotNull + @Override + public Fragment getItem(int position) { + Log.e("调用了Fragment", "postion:" + position); + BaseFragment fragment = null; + if (fragments == null || fragments.isEmpty()) { + fragment = getTabFragment(); + } else { + fragment = fragments.get(position); + } + if (fragment == null) { + throw new NullPointerException("Switch Fragment can not be empty!"); + } + fragment.setTabTitle(titles.get(position)); + return fragment; + } + + @Override + public int getItemPosition(@NonNull Object object) { + return POSITION_NONE; + } + + + @Override + public CharSequence getPageTitle(int position) { + return titles.get(position).getTitle(); + } + + /** + * 使用两参构造方法 必须重写 该方法 返回对应的Fragment + * + * @return BaseFragment + */ + public BaseFragment getTabFragment() { + return null; + } + +} diff --git a/common_base/src/main/java/com/wss/common/base/ActionBarActivity.java b/common_base/src/main/java/com/wss/common/base/ActionBarActivity.java deleted file mode 100644 index 1c291db..0000000 --- a/common_base/src/main/java/com/wss/common/base/ActionBarActivity.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.wss.common.base; - -import android.os.Bundle; -import android.support.annotation.Nullable; - -import com.wss.common.base.mvp.BasePresenter; -import com.wss.common.widget.ActionBar; - -/** - * Describe:所有带actionBar的Activity基类 - * Created by 吴天强 on 2018/10/18. - */ - -public abstract class ActionBarActivity

extends BaseMvpActivity

{ - - // @BindView(R2.id.actionbar) - protected ActionBar actionBar; - - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - actionBar = findViewById(R.id.actionbar); - } - - protected void setTitleText(String title) { - if (actionBar != null) { - actionBar.setCenterText(title); - } - } - - protected void setTitleText(int title) { - if (actionBar != null) { - actionBar.setCenterText(getString(title)); - } - } - - @Override - protected boolean isActionBar() { - return true; - } -} diff --git a/common_base/src/main/java/com/wss/common/base/BaseActionBarActivity.java b/common_base/src/main/java/com/wss/common/base/BaseActionBarActivity.java new file mode 100644 index 0000000..5f2e01f --- /dev/null +++ b/common_base/src/main/java/com/wss/common/base/BaseActionBarActivity.java @@ -0,0 +1,240 @@ +package com.wss.common.base; + +import android.os.Bundle; +import android.view.View; + +import com.wss.common.base.mvp.BasePresenter; +import com.wss.common.widget.ActionBar; + +import androidx.annotation.Nullable; +import butterknife.BindView; + +/** + * Describe:所有带actionBar的Activity基类 + * Created by 吴天强 on 2018/10/18. + */ +public abstract class BaseActionBarActivity

extends BaseMvpActivity

{ + + @BindView(R2.id.actionbar) + ActionBar actionBar; + + @BindView(R2.id.action_bar_line) + View actionBarLine; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + actionBar.setVisibility(View.VISIBLE); + //沉浸式状态栏 + setImmersionBarColor(R.color.white); + } + + + /** + * 设置左边文字 + * + * @param text 文字 + */ + protected void setLeftText(String text) { + setLeftText(text, null); + } + + /** + * @param text 文字 + * @param listener 事件监听 + */ + protected void setLeftText(String text, View.OnClickListener listener) { + setLeftText(text, R.color.color_333333, listener); + } + + /** + * 设置左边文字 + * + * @param text 文字 + * @param textColor 文字颜色 + * @param listener 事件监听 + */ + protected void setLeftText(String text, int textColor, View.OnClickListener listener) { + if (actionBar != null) { + if (textColor != 0) { + actionBar.setLeftTextColor(getResources().getColor(textColor)); + } + actionBar.setLeftText(text, listener); + } + } + + /** + * 设置左边icon + * + * @param drawable icon resId + * @param listener 事件监听 + */ + protected void setLeftIcon(int drawable, View.OnClickListener listener) { + if (actionBar != null) { + actionBar.setLeftIcon(drawable, listener); + } + } + + /** + * 设置左边icon + * + * @param listener 事件监听 + */ + protected void setLeftIcon(View.OnClickListener listener) { + setLeftIcon(R.drawable.ic_back_black, listener); + } + + /** + * 设置左边View + * + * @param view view + */ + protected void setLeftView(View view) { + if (actionBar != null) { + actionBar.setLeftView(view); + } + } + + /** + * 设置中间文字 + * + * @param text text + */ + protected void setCenterText(int text) { + setCenterText(getString(text)); + } + + /** + * 设置中间文字 + * + * @param text text + */ + protected void setCenterText(String text) { + setCenterText(text, R.color.color_333333); + } + + /** + * 设置中间文字 + * + * @param text text + * @param textColor 文字颜色 + */ + protected void setCenterText(String text, int textColor) { + setCenterText(text, textColor, null); + } + + /** + * 设置中间文字 + * + * @param text text + * @param textColor 文字颜色 + * @param listener 事件监听 + */ + protected void setCenterText(String text, int textColor, View.OnClickListener listener) { + if (actionBar != null) { + if (textColor != 0) { + actionBar.setCenterTextColor(textColor); + } + //加粗 + actionBar.setCenterTextBold(true); + actionBar.setCenterText(text, listener); + } + } + + /** + * 设置中间View + * + * @param view view + */ + protected void setCenterView(View view) { + if (actionBar != null) { + actionBar.setCenterView(view); + } + } + + /** + * 设置右边文字 + * + * @param resId 文字 + * @param listener 事件监听 + */ + protected void setRightText(int resId, View.OnClickListener listener) { + setRightText(getString(resId), listener); + } + + /** + * 设置右边文字 + * + * @param text 文字 + * @param listener 事件监听 + */ + protected void setRightText(String text, View.OnClickListener listener) { + setRightText(text, R.color.color_333333, listener); + } + + /** + * 设置右边文字 + * + * @param text 文字 + * @param textColor 文字颜色 + * @param listener 事件监听 + */ + protected void setRightText(String text, int textColor, View.OnClickListener listener) { + if (actionBar != null) { + if (textColor != 0) { + actionBar.setRightTextColor(textColor); + } + actionBar.setRightText(text, listener); + } + } + + /** + * 设置右边icon + * + * @param drawable 图片 + * @param listener 事件监听 + */ + protected void setRightIcon(int drawable, View.OnClickListener listener) { + if (actionBar != null) { + actionBar.setRightIcon(drawable, listener); + } + } + + /** + * 设置右边View + * + * @param view view + */ + protected void setRightView(View view) { + if (actionBar != null) { + actionBar.setRightView(view); + } + } + + /** + * 返回顶部TitleBar + * + * @return ActionBar + */ + protected ActionBar getTitleBar() { + return actionBar; + } + + /** + * 设置ActionBar下面的横线 + * + * @param show 是否显示 + */ + protected void showActionBarLine(boolean show) { + actionBarLine.setVisibility(show ? View.VISIBLE : View.GONE); + } + + /** + * 设置左边返回图标是否显示 + * + * @param show 是否显示 + */ + public void showBackImg(boolean show) { + actionBar.showBackImg(show); + } +} diff --git a/common_base/src/main/java/com/wss/common/base/BaseActivity.java b/common_base/src/main/java/com/wss/common/base/BaseActivity.java index bc54385..6a3a911 100644 --- a/common_base/src/main/java/com/wss/common/base/BaseActivity.java +++ b/common_base/src/main/java/com/wss/common/base/BaseActivity.java @@ -1,29 +1,24 @@ package com.wss.common.base; import android.content.Context; -import android.content.Intent; -import android.net.Uri; import android.os.Bundle; -import android.provider.Settings; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.FragmentActivity; import android.view.View; import android.view.ViewGroup; import android.view.ViewStub; +import android.view.WindowManager; import android.widget.TextView; -import com.gyf.barlibrary.ImmersionBar; -import com.orhanobut.logger.Logger; +import com.gyf.immersionbar.BarHide; +import com.gyf.immersionbar.ImmersionBar; +import com.scwang.smartrefresh.layout.SmartRefreshLayout; import com.wss.common.bean.Event; import com.wss.common.utils.EventBusUtils; -import com.wss.common.widget.dialog.LoadingDialog; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; -import java.util.Arrays; - +import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentActivity; import butterknife.ButterKnife; import butterknife.Unbinder; @@ -31,44 +26,72 @@ * Describe:所有Activity的基类 * Created by 吴天强 on 2018/10/15. */ - public abstract class BaseActivity extends FragmentActivity { - private Unbinder unbinder; private ViewStub emptyView; - protected Context mContext; - protected ImmersionBar mImmersionBar; - protected LoadingDialog loadingDialog; - + protected Context context; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (isActionBar()) { + //加入Activity管理器 + BaseApplication.i().getActivityManage().addActivity(this); + context = this; + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); + if (!isFullScreenLayout()) { setContentView(R.layout.activity_base); ((ViewGroup) findViewById(R.id.fl_content)).addView(getLayoutInflater().inflate(getLayoutId(), null)); - } else { - setContentView(getLayoutId()); + unbinder = ButterKnife.bind(this); } - unbinder = ButterKnife.bind(this); - mContext = this; - //初始化ButterKnife - //沉浸式状态栏 - initImmersionBar(R.color.blue); - //加入Activity管理器 - BaseApplication.getApplication().getActivityManage().addActivity(this); - if (regEvent()) { + if (registerEventBus()) { EventBusUtils.register(this); } - loadingDialog = new LoadingDialog(mContext); + } + /** + * 设置沉浸栏颜色 + * + * @param statusBarColor 颜色 + */ + protected void setImmersionBarColor(int statusBarColor) { + setImmersionBarColor(statusBarColor, false); } - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - initView(); + /** + * 设置沉浸栏颜色 + * + * @param statusBarColor 沉浸栏颜色 + */ + protected void setImmersionBarColor(int statusBarColor, boolean fitsSystemWindows) { + setImmersionBarColor(statusBarColor, R.color.black, fitsSystemWindows); + } + + + /** + * 设置沉浸栏颜色 + * + * @param statusBarColor 沉浸栏颜色 + * @param navigationBarColor 沉浸栏图标颜色 + */ + protected void setImmersionBarColor(int statusBarColor, int navigationBarColor, boolean fitsSystemWindows) { + ImmersionBar.with(this) + .statusBarColor(statusBarColor) + .navigationBarColor(navigationBarColor) + .fitsSystemWindows(fitsSystemWindows) + .autoDarkModeEnable(true) + .statusBarDarkFont(true) + .init(); + } + + /** + * 隐藏状态栏、导航栏 + */ + protected void setImmersionBarHide() { + ImmersionBar.with(this) + .fitsSystemWindows(false) + .hideBar(BarHide.FLAG_HIDE_BAR) + .init(); } @Override @@ -77,43 +100,78 @@ protected void onDestroy() { if (unbinder != null) { unbinder.unbind(); } - if (regEvent()) { + if (registerEventBus()) { EventBusUtils.unregister(this); } - //必须调用该方法,防止内存泄漏 - if (mImmersionBar != null) { - mImmersionBar.destroy(); - } //将Activity从管理器移除 - BaseApplication.getApplication().getActivityManage().removeActivity(this); + BaseApplication.i().getActivityManage().removeActivity(this); + } + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + initView(); } - //***************************************空页面方法************************************* + //***************************************空页面方法 start************************************* + + /** + * 数据为空页面 + */ protected void showEmptyView() { - showEmptyOrErrorView(getString(R.string.no_data), R.drawable.bg_no_data); + showEmptyView(getString(R.string.no_data)); + } + + /** + * 数据为空页面 + * + * @param text 显示文案 + */ + protected void showEmptyView(String text) { + showEmptyOrErrorView(text, R.drawable.bg_no_data, false); } + /** + * 请求数据报错页面 + */ protected void showErrorView() { - showEmptyOrErrorView(getString(R.string.error_data), R.drawable.bg_no_net); + showErrorView(getString(R.string.network_error_server_error)); + } + + /** + * 请求数据报错页面 + * + * @param text 显示文案 + */ + protected void showErrorView(String text) { + showEmptyOrErrorView(text, R.drawable.bg_no_net , true); } - public void showEmptyOrErrorView(String text, int img) { - emptyView = findViewById(R.id.vs_empty); - if (emptyView != null) { - emptyView.setVisibility(View.VISIBLE); - findViewById(R.id.iv_empty).setBackgroundResource(img); - ((TextView) findViewById(R.id.tv_empty)).setText(text); - findViewById(R.id.ll_empty).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - onEmptyViewClick(); - } - }); + /** + * 请求数据报错或者为空页面 + * + * @param text 提示文案 + * @param iconResId 显示的icon + */ + public void showEmptyOrErrorView(String text, int iconResId, boolean showRefreshButton) { + if (emptyView == null) { + emptyView = findViewById(R.id.vs_empty); + } + emptyView.setVisibility(View.VISIBLE); + findViewById(R.id.iv_empty).setBackgroundResource(iconResId); + ((TextView) findViewById(R.id.tv_empty)).setText(text); + if (showRefreshButton) { + View refreshButton = findViewById(R.id.tv_try_again); + refreshButton.setVisibility(View.VISIBLE); + refreshButton.setOnClickListener(v -> onRefreshRetry()); } } + /** + * 隐藏空页面 + */ protected void hideEmptyView() { if (emptyView != null) { emptyView.setVisibility(View.GONE); @@ -121,84 +179,64 @@ protected void hideEmptyView() { } /** - * 空页面被点击 + * 刷新重试 */ - protected void onEmptyViewClick() { - + protected void onRefreshRetry() { } - //***************************************空页面方法********************************* - + //***************************************空页面方法 end********************************* - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - Logger.e("permissions:" + Arrays.toString(permissions) + " grantResults:" + Arrays.toString(grantResults)); - - //如果有未授权权限则跳转设置页面 - if (!requestPermissionsResult(grantResults)) { - Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); - intent.setData(Uri.parse("package:" + getPackageName())); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); + /** + * 停止刷新和加载更多 + * + * @param layout 刷新layout + */ + protected void stopRefresh(SmartRefreshLayout layout) { + if (layout == null) { + return; } - - + layout.finishLoadMore(); + layout.finishRefresh(); } /** - * 判断授权结果 + * 是否布局伸入状态栏类型Activity + * + * @return boolean */ - private boolean requestPermissionsResult(int[] grantResults) { - for (int code : grantResults) { - if (code == -1) { - return false; - } - } - return true; + protected boolean isFullScreenLayout() { + return false; } /** - * 沉浸栏颜色 + * 需要接收事件 重写该方法 并返回true + * + * @return boolean */ - protected void initImmersionBar(int color) { - if (mImmersionBar == null) { - mImmersionBar = ImmersionBar.with(this); - if (color != 0) { - mImmersionBar.statusBarColor(color); - } - mImmersionBar.init(); - } + protected boolean registerEventBus() { + return false; } - /** * 子类接受事件 重写该方法 + * + * @param event 事件 */ @Subscribe(threadMode = ThreadMode.MAIN) public void onEventBus(Event event) { - } - /** - * 是否需要ActionBar - * TODO 暂时用此方法 后续优化 + * 返回页面layout + * + * @return layout */ - protected boolean isActionBar() { - return false; - } + protected abstract int getLayoutId(); /** - * 需要接收事件 重写该方法 并返回true + * 初始化View */ - protected boolean regEvent() { - return false; - } - - protected abstract int getLayoutId(); - protected abstract void initView(); } diff --git a/common_base/src/main/java/com/wss/common/base/BaseApplication.java b/common_base/src/main/java/com/wss/common/base/BaseApplication.java index c6992a3..2e64d20 100644 --- a/common_base/src/main/java/com/wss/common/base/BaseApplication.java +++ b/common_base/src/main/java/com/wss/common/base/BaseApplication.java @@ -2,7 +2,7 @@ import android.app.Application; import android.content.Context; -import android.support.multidex.MultiDex; +import android.widget.Toast; import com.alibaba.android.arouter.launcher.ARouter; import com.orhanobut.logger.AndroidLogAdapter; @@ -10,26 +10,67 @@ import com.orhanobut.logger.LogcatLogStrategy; import com.orhanobut.logger.Logger; import com.orhanobut.logger.PrettyFormatStrategy; +import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.scwang.smartrefresh.layout.footer.ClassicsFooter; +import com.scwang.smartrefresh.layout.header.ClassicsHeader; +import com.wss.common.bean.User; +import com.wss.common.constants.Constants; +import com.wss.common.constants.Dic; import com.wss.common.manage.ActivityManage; -import com.wss.common.manage.CrashHandlerManage; +import com.wss.common.utils.CacheUtils; +import com.wss.common.utils.ToastUtils; +import com.wss.common.utils.ValidUtils; +import com.wss.common.utils.toast.ToastInterceptor; +import com.wss.common.utils.toast.style.ToastBlackStyle; + +import org.jetbrains.annotations.Contract; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509TrustManager; + +import androidx.multidex.MultiDex; +import io.reactivex.disposables.Disposable; +import io.reactivex.plugins.RxJavaPlugins; +import okhttp3.ConnectionSpec; +import okhttp3.OkHttpClient; +import rxhttp.HttpSender; +import rxhttp.wrapper.ssl.SSLSocketFactoryImpl; +import rxhttp.wrapper.ssl.X509TrustManagerImpl; /** * Describe:基础Application所有需要模块化开发的module都需要继承自BaseApplication * Created by 吴天强 on 2018/10/12. */ public class BaseApplication extends Application { - - //全局唯一的context + /** + * 全局上下文 + */ private static BaseApplication application; - - //Activity管理器 + /** + * Activity管理器 + */ private ActivityManage activityManage; + /** + * 保存所有网络请求 + */ + private Map netDisposable; + + /** + * 登录用户 + */ + private User user; + @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); application = this; - //MultiDex分包方法 必须最先初始化 + //MultiDex分包方法 初始化 MultiDex.install(this); } @@ -37,37 +78,98 @@ protected void attachBaseContext(Context base) { public void onCreate() { super.onCreate(); activityManage = new ActivityManage(); - initARouter(); + //初始化日志框架 initLogger(); - initCrashManage(); + //初始化ARouter + initARouter(); + //初始化Toast + initToast(); + //初始化网络框架 + initRXHttp(); + //刷新框架 + initRefreshLayout(); } /** - * 初始化崩溃管理器 + * 初始化刷新框架 */ - private void initCrashManage() { - if (!BuildConfig.DEBUG) { - CrashHandlerManage.getInstance() - .init(getApplicationContext()); - } + private void initRefreshLayout() { + //设置全局的Header构建器 + SmartRefreshLayout.setDefaultRefreshHeaderCreator((context, layout) -> { + //指定为经典Footer,默认是 BezierRadarHeader + return new ClassicsHeader(context); + }); + //设置全局的Footer构建器 + SmartRefreshLayout.setDefaultRefreshFooterCreator((context, layout) -> { + //指定为经典Footer,默认是 BallPulseFooter + return new ClassicsFooter(context).setDrawableSize(20); + }); } + /** + * B + * 初始化Toast + */ + private void initToast() { + ToastUtils.setToastInterceptor(new ToastInterceptor() { + @Override + public boolean intercept(Toast toast, CharSequence text) { + boolean intercept = super.intercept(toast, text); + if (intercept) { + Logger.e("空 Toast"); + } else { + Logger.d(text.toString()); + } + return intercept; + } + }); + // 初始化吐司工具类 + ToastUtils.init(this, new ToastBlackStyle(this)); + } + + /** + * 初始化网络请求 + */ + private void initRXHttp() { + X509TrustManager trustManager = new X509TrustManagerImpl(); + SSLSocketFactory sslSocketFactory = new SSLSocketFactoryImpl(trustManager); + HttpSender.setDebug(BuildConfig.DEBUG); + HttpSender.init(new OkHttpClient.Builder() + .connectTimeout(Constants.Net.TIME_OUT, TimeUnit.SECONDS) + .readTimeout(Constants.Net.TIME_OUT, TimeUnit.SECONDS) + .writeTimeout(Constants.Net.TIME_OUT, TimeUnit.SECONDS) + .connectionSpecs(Arrays.asList(ConnectionSpec.COMPATIBLE_TLS, ConnectionSpec.CLEARTEXT)) + //添加信任证书 + .sslSocketFactory(sslSocketFactory, trustManager) + //忽略Host验证 + .hostnameVerifier(((hostname, session) -> true)) + .build()); + RxJavaPlugins.setErrorHandler(throwable -> { + //Rx全局异常处理 + }); + } /** * 初始化日志打印框架 */ private void initLogger() { FormatStrategy formatStrategy = PrettyFormatStrategy.newBuilder() - .showThreadInfo(false) //(可选)是否显示线程信息。 默认值为true - .methodCount(2) //(可选)要显示的方法行数。 默认2 - .methodOffset(7) //(可选)设置调用堆栈的函数偏移值,0的话则从打印该Log的函数开始输出堆栈信息,默认是0 - .logStrategy(new LogcatLogStrategy()) //(可选)更改要打印的日志策略。 默认LogCat - .tag("AMD") //(可选)每个日志的全局标记。 默认PRETTY_LOGGER + //(可选)是否显示线程信息。 默认值为true + .showThreadInfo(false) + //(可选)要显示的方法行数。 默认2 + .methodCount(0) + //(可选)设置调用堆栈的函数偏移值,0的话则从打印该Log的函数开始输出堆栈信息,默认是0 + .methodOffset(7) + //(可选)更改要打印的日志策略。 默认LogCat + .logStrategy(new LogcatLogStrategy()) + //(可选)每个日志的全局标记。 默认PRETTY_LOGGER + .tag("APP_LOG") .build(); + Logger.addLogAdapter(new AndroidLogAdapter(formatStrategy) { @Override public boolean isLoggable(int priority, String tag) { - //DEBUG模式下不打印LOG + //非DEBUG模式下不打印LOG return BuildConfig.DEBUG; } }); @@ -78,10 +180,12 @@ public boolean isLoggable(int priority, String tag) { */ private void initARouter() { if (BuildConfig.DEBUG) { - ARouter.openLog(); // 打印日志 - ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险) + // 打印日志 + ARouter.openLog(); + // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险) + ARouter.openDebug(); } - ARouter.init(application);// 尽可能早,推荐在Application中初始化 + ARouter.init(application); } /** @@ -98,7 +202,8 @@ public void onTerminate() { * * @return BaseApplication */ - public static BaseApplication getApplication() { + @Contract(pure = true) + public static BaseApplication i() { return application; } @@ -113,7 +218,9 @@ public void exitApp() { } /** - * 返回Activity管理器 + * 获取Activity管理器 + * + * @return ActivityManage */ public ActivityManage getActivityManage() { if (activityManage == null) { @@ -122,5 +229,90 @@ public ActivityManage getActivityManage() { return activityManage; } + /** + * 获取存放的请求对象 + * + * @return Map + */ + public Map getNetDisposables() { + if (netDisposable == null) { + return new HashMap<>(16); + } + return netDisposable; + } + + /** + * 退出登录清除换群 + */ + public void loginOutClean() { + + } + + /** + * 添加请求对象 + * + * @param requestId 请求ID + * @param disposable 对象 + */ + public void addNetDisposable(String requestId, Disposable disposable) { + if (netDisposable == null) { + netDisposable = new HashMap<>(16); + } + netDisposable.put(requestId, disposable); + } + + /** + * 根据请求ID移除该对象 + * + * @param requestId 请求ID + */ + public void removeNetDisposable(String requestId) { + netDisposable.remove(requestId); + } + + /** + * 保存用户信息 + * + * @param user 登录用户 + */ + public void setUser(User user) { + CacheUtils.get(this).put(Dic.LOGIN_USER_INFO, user); + this.user = user; + } + + /** + * 获取用户信息 + * + * @return User + */ + public User getUser() { + if (!ValidUtils.isValid(user) || !ValidUtils.isValid(user.getId())) { + //如果保存的user为空,则去本地缓存中取 + user = (User) CacheUtils.get(this).getAsObject(Dic.LOGIN_USER_INFO); + } + if (!ValidUtils.isValid(user)) { + //防止异常情况下getUser对象为null问题 + user = new User(); + } + return user; + } + /** + * 是否登录 + * + * @return boolean + */ + public boolean isLogged() { + User user = getUser(); + return ValidUtils.isValid(user) && ValidUtils.isValid(user.getId()); + } + + + public String getDeviceId() { + return ""; + } + + public String getLoginToken() { + return ""; + } } \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/base/BaseFragment.java b/common_base/src/main/java/com/wss/common/base/BaseFragment.java index 7e962f9..e463b3a 100644 --- a/common_base/src/main/java/com/wss/common/base/BaseFragment.java +++ b/common_base/src/main/java/com/wss/common/base/BaseFragment.java @@ -2,21 +2,24 @@ import android.content.Context; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewStub; import android.widget.TextView; -import com.wss.common.widget.dialog.LoadingDialog; +import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.wss.common.bean.Event; +import com.wss.common.bean.HorizontalTabTitle; +import com.wss.common.utils.ValidUtils; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; import butterknife.ButterKnife; import butterknife.Unbinder; @@ -24,35 +27,36 @@ * Describe:所有Fragment的基类 * Created by 吴天强 on 2018/10/17. */ - public abstract class BaseFragment extends Fragment { + protected Context context; private ViewStub emptyView; private View rootView; private Unbinder unBinder; - protected Context mContext; - protected LoadingDialog loadingDialog; - + private HorizontalTabTitle tabTitle; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mContext = getActivity(); - loadingDialog = new LoadingDialog(mContext); + context = getActivity(); } + @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - rootView = inflater.inflate(R.layout.fragment_base, container, false); + rootView = inflater.inflate(R.layout.layout_base, container, false); ((ViewGroup) rootView.findViewById(R.id.fl_content)).addView(getLayoutInflater().inflate(getLayoutId(), null)); unBinder = ButterKnife.bind(this, rootView); - if (regEvent()) { - EventBus.getDefault().register(this); + if (registerEventBus()) { + if (!EventBus.getDefault().isRegistered(this)) { + EventBus.getDefault().register(this); + } } return rootView; } + @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); initView(); @@ -65,38 +69,73 @@ public void onDestroy() { if (unBinder != null) { unBinder.unbind(); } - if (regEvent()) { + if (registerEventBus()) { EventBus.getDefault().unregister(this); } } - //***************************************空页面方法************************************* + //***************************************空页面方法 start ************************************* + + /** + * 数据为空页面 + */ protected void showEmptyView() { - showEmptyOrErrorView(getString(R.string.no_data), R.drawable.bg_no_data); + showEmptyView(getString(R.string.no_data)); + } + + /** + * 数据为空页面 + * + * @param text 显示文案 + */ + protected void showEmptyView(String text) { + showEmptyOrErrorView(text, R.drawable.bg_no_data, false); } + /** + * 请求数据报错页面 + */ protected void showErrorView() { - showEmptyOrErrorView(getString(R.string.error_data), R.drawable.bg_no_net); + showErrorView(getString(R.string.network_error_server_error)); + } + + /** + * 请求数据报错页面 + * + * @param text 显示文案 + */ + protected void showErrorView(String text) { + showEmptyOrErrorView(text, R.drawable.bg_no_net, true); } - public void showEmptyOrErrorView(String text, int img) { - emptyView = rootView.findViewById(R.id.vs_empty); - if (emptyView != null) { - emptyView.setVisibility(View.VISIBLE); - rootView.findViewById(R.id.iv_empty).setBackgroundResource(img); - ((TextView) rootView.findViewById(R.id.tv_empty)).setText(text); - rootView.findViewById(R.id.ll_empty).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - onEmptyViewClick(); - } - }); + /** + * 请求数据报错或者为空页面 + * + * @param text 提示文案 + * @param iconResId 显示的icon + */ + public void showEmptyOrErrorView(String text, int iconResId, boolean showRefreshButton) { + if (emptyView == null) { + emptyView = rootView.findViewById(R.id.vs_empty); + } + emptyView.setVisibility(View.VISIBLE); + rootView.findViewById(R.id.iv_empty).setBackgroundResource(iconResId); + ((TextView) rootView.findViewById(R.id.tv_empty)).setText(text); + View refreshButton = rootView.findViewById(R.id.tv_try_again); + if (showRefreshButton) { + refreshButton.setVisibility(View.VISIBLE); + refreshButton.setOnClickListener(v -> onRefreshRetry()); + } else { + refreshButton.setVisibility(View.GONE); } } + /** + * 隐藏空页面 + */ protected void hideEmptyView() { if (emptyView != null) { emptyView.setVisibility(View.GONE); @@ -104,39 +143,78 @@ protected void hideEmptyView() { } /** - * 空页面被点击 + * 刷新重试 */ - protected void onEmptyViewClick() { + protected void onRefreshRetry() { + } + + //***************************************空页面方法 end********************************* + /** + * 停止刷新和加载更多 + * + * @param layout 刷新layout + */ + protected void stopRefresh(SmartRefreshLayout layout) { + if (layout == null) { + return; + } + layout.finishLoadMore(); + layout.finishRefresh(); } - //***************************************空页面方法********************************* + /** + * 返回Fragment的根布局 + * + * @return View + */ + protected View getRootView() { + return rootView; + } /** - * 给Fragment设置数据 + * 给Fragment设置TabTile数据 + * + * @param data tabtitle */ - public void setFragmentData(Object data) { + public void setTabTitle(HorizontalTabTitle data) { + this.tabTitle = data; + } + /** + * 返回ViewPager+Fragment滑动 设置给子Fragment的Tab数据 + * + * @return HorizontalTabTitle + */ + public HorizontalTabTitle getTabTitle() { + return ValidUtils.isValid(tabTitle) ? tabTitle : new HorizontalTabTitle(""); } /** * 子类接受事件 重写该方法 */ @Subscribe(threadMode = ThreadMode.MAIN) - public void onEventBus(Object event) { + public void onEventBus(Event event) { } /** * 需要接收事件 重新该方法 并返回true */ - protected boolean regEvent() { + protected boolean registerEventBus() { return false; } - + /** + * 返回页面layout + * + * @return layout + */ protected abstract int getLayoutId(); + /** + * 初始化View + */ protected abstract void initView(); } diff --git a/common_base/src/main/java/com/wss/common/base/BaseFullScreenActivity.java b/common_base/src/main/java/com/wss/common/base/BaseFullScreenActivity.java new file mode 100644 index 0000000..b6df48f --- /dev/null +++ b/common_base/src/main/java/com/wss/common/base/BaseFullScreenActivity.java @@ -0,0 +1,34 @@ +package com.wss.common.base; + + +import com.gyf.immersionbar.ImmersionBar; +import com.wss.common.base.mvp.BasePresenter; + +/** + * Describe:布局需要伸入状态栏下面的Activity继承该类, + * 并通过setContentView来设置layout,重新注册ButterKnife + * Created by 吴天强 on 2020/4/28. + */ +public abstract class BaseFullScreenActivity

extends BaseMvpActivity

{ + + @Override + protected boolean isFullScreenLayout() { + return true; + } + + @Override + protected int getLayoutId() { + //该方法仅仅是为了实现父类的抽象方法,无任何意义,忽略即可 + return R.layout.layout_nothing; + } + + @Override + protected void initView() { + //布局可伸入状态栏 + ImmersionBar.with(this) + .statusBarColor(R.color.transparent) + .statusBarDarkFont(true) + .fullScreen(true) + .init(); + } +} diff --git a/common_base/src/main/java/com/wss/common/base/HorizontalTabActivity.java b/common_base/src/main/java/com/wss/common/base/BaseHorizontalTabActivity.java similarity index 67% rename from common_base/src/main/java/com/wss/common/base/HorizontalTabActivity.java rename to common_base/src/main/java/com/wss/common/base/BaseHorizontalTabActivity.java index 958205e..a1ab86f 100644 --- a/common_base/src/main/java/com/wss/common/base/HorizontalTabActivity.java +++ b/common_base/src/main/java/com/wss/common/base/BaseHorizontalTabActivity.java @@ -1,24 +1,22 @@ package com.wss.common.base; -import android.support.annotation.CallSuper; -import android.support.v4.view.ViewPager; -import com.wss.common.base.adapter.FragmentPagerAdapter; +import com.wss.common.adapter.FragmentPagerAdapter; import com.wss.common.base.mvp.BasePresenter; import com.wss.common.bean.HorizontalTabTitle; import com.wss.common.widget.PagerSlidingTabStrip; import java.util.List; +import androidx.annotation.CallSuper; +import androidx.viewpager.widget.ViewPager; import butterknife.BindView; /** * Describe:带水平选项卡的Activity * Created by 吴天强 on 2018/10/22. */ - -public abstract class HorizontalTabActivity

extends ActionBarActivity

{ - +public abstract class BaseHorizontalTabActivity

extends BaseActionBarActivity

{ @BindView(R2.id.pst_tab) PagerSlidingTabStrip tabStrip; @@ -38,15 +36,24 @@ protected void initView() { viewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager(), getTabTitles()) { @Override public BaseFragment getTabFragment() { - return HorizontalTabActivity.this.getTabFragment(); + return BaseHorizontalTabActivity.this.getTabFragment(); } }); tabStrip.setViewPager(viewPager); } + /** + * 选项卡List + * + * @return List + */ protected abstract List getTabTitles(); + /** + * 滑动Fragment + * + * @return BaseFragment + */ protected abstract BaseFragment getTabFragment(); - } diff --git a/common_base/src/main/java/com/wss/common/base/BaseMvpActivity.java b/common_base/src/main/java/com/wss/common/base/BaseMvpActivity.java index c523d75..df294f2 100644 --- a/common_base/src/main/java/com/wss/common/base/BaseMvpActivity.java +++ b/common_base/src/main/java/com/wss/common/base/BaseMvpActivity.java @@ -2,25 +2,30 @@ import android.content.Context; import android.os.Bundle; -import android.support.annotation.Nullable; +import android.text.TextUtils; import com.wss.common.base.mvp.BasePresenter; import com.wss.common.base.mvp.IBaseView; +import com.wss.common.widget.dialog.LoadingDialog; + +import androidx.annotation.Nullable; +import androidx.lifecycle.LifecycleOwner; /** * Describe:所有需要Mvp开发的Activity的基类 * Created by 吴天强 on 2018/10/15. */ - +@SuppressWarnings("unchecked") public abstract class BaseMvpActivity

extends BaseActivity implements IBaseView { - protected P presenter; + private P presenter; + private LoadingDialog loadingDialog; - - @SuppressWarnings("unchecked") @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + loadingDialog = new LoadingDialog(context); + loadingDialog.setCancelable(loadingCancelable()); //创建present presenter = createPresenter(); if (presenter != null) { @@ -28,22 +33,45 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { } } - @Override protected void onDestroy() { super.onDestroy(); if (presenter != null) { presenter.detachView(); + presenter = null; } + } + /** + * 返回Presenter + * + * @return P + */ + protected P getPresenter() { + return presenter; } - //***************************************IBaseView方法实现************************************* + + //***************************************IBaseView方法实现 start************************************* + @Override public void showLoading() { + showLoading(""); + } + + /** + * 显示加载框 + * + * @param msg 加载框文字 + */ + public void showLoading(String msg) { if (loadingDialog != null && !loadingDialog.isShowing()) { + if (!TextUtils.isEmpty(msg)) { + loadingDialog.setTitleText(msg); + } loadingDialog.show(); } + hideEmptyView(); } @Override @@ -53,21 +81,41 @@ public void dismissLoading() { } } + /** + * 加载框是否可以取消 + * + * @return boolean + */ + protected boolean loadingCancelable() { + return true; + } + + @Override public void onEmpty(Object tag) { - + dismissLoading(); } @Override public void onError(Object tag, String errorMsg) { + dismissLoading(); + } + @Override + public LifecycleOwner getLifecycleOwner() { + return this; } @Override public Context getContext() { - return mContext; + return context; } - //***************************************IBaseView方法实现************************************* + //********************************x*******IBaseView方法实现 end************************************* + /** + * 创建Presenter + * + * @return Presenter + */ protected abstract P createPresenter(); } diff --git a/common_base/src/main/java/com/wss/common/base/BaseMvpFragment.java b/common_base/src/main/java/com/wss/common/base/BaseMvpFragment.java index c04ef53..dc4ec42 100644 --- a/common_base/src/main/java/com/wss/common/base/BaseMvpFragment.java +++ b/common_base/src/main/java/com/wss/common/base/BaseMvpFragment.java @@ -1,31 +1,35 @@ package com.wss.common.base; + import android.os.Bundle; -import android.support.annotation.Nullable; import com.wss.common.base.mvp.BasePresenter; import com.wss.common.base.mvp.IBaseView; +import com.wss.common.widget.dialog.LoadingDialog; + +import androidx.annotation.Nullable; +import androidx.lifecycle.LifecycleOwner; /** * Describe:所有需要Mvp开发的Fragment的基类 * Created by 吴天强 on 2018/10/17. */ - +@SuppressWarnings("unchecked") public abstract class BaseMvpFragment

extends BaseFragment implements IBaseView { - protected P presenter; - + private P presenter; + private LoadingDialog loadingDialog; - @SuppressWarnings("unchecked") @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - - if (presenter == null) { - presenter = createPresenter(); + loadingDialog = new LoadingDialog(context); + loadingDialog.setCancelable(loadingCancelable()); + //创建present + presenter = createPresenter(); + if (presenter != null) { presenter.attachView(this); } - } @Override @@ -34,16 +38,26 @@ public void onDestroy() { if (presenter != null) { presenter.detachView(); } + } + /** + * 返回presenter + * + * @return P + */ + protected P getPresenter() { + return presenter; } + //***************************************IBaseView方法实现 start ************************************* + - //***************************************IBaseView方法实现************************************* @Override public void showLoading() { if (loadingDialog != null && !loadingDialog.isShowing()) { loadingDialog.show(); } + hideEmptyView(); } @Override @@ -53,16 +67,36 @@ public void dismissLoading() { } } + /** + * 加载框是否可以取消 + * + * @return boolean + */ + protected boolean loadingCancelable() { + return true; + } + @Override public void onEmpty(Object tag) { - + dismissLoading(); } @Override public void onError(Object tag, String errorMsg) { + dismissLoading(); + } + @Override + public LifecycleOwner getLifecycleOwner() { + return this; } - //***************************************IBaseView方法实现************************************* + //***************************************IBaseView方法实现 end************************************* + + /** + * 创建Presenter + * + * @return Presenter + */ protected abstract P createPresenter(); } diff --git a/common_base/src/main/java/com/wss/common/base/BaseRefreshListActivity.java b/common_base/src/main/java/com/wss/common/base/BaseRefreshListActivity.java new file mode 100644 index 0000000..0769b17 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/base/BaseRefreshListActivity.java @@ -0,0 +1,114 @@ +package com.wss.common.base; + +import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.scwang.smartrefresh.layout.listener.OnRefreshLoadMoreListener; +import com.wss.common.base.mvp.BasePresenter; + +import androidx.annotation.CallSuper; +import androidx.recyclerview.widget.RecyclerView; +import butterknife.BindView; + +/** + * Describe:带下拉刷新 上拉加载更多的Activity + * 内部实现为刷新控件 SmartRefreshLayout + 列表控件 RecyclerView + * Created by 吴天强 on 2018/10/23. + */ +public abstract class BaseRefreshListActivity

extends BaseActionBarActivity

{ + + @BindView(R2.id.ptrl_list) + SmartRefreshLayout refreshLayout; + + @BindView(R2.id.recycle_view) + RecyclerView recyclerView; + + /** + * 适配器 + */ + private RecyclerView.Adapter adapter; + + @Override + protected int getLayoutId() { + return R.layout.layout_refresh; + } + + @CallSuper + @Override + protected void initView() { + adapter = createAdapter(); + refreshLayout.setOnRefreshLoadMoreListener(createRefreshListener()); + recyclerView.setLayoutManager(getLayoutManager()); + recyclerView.setAdapter(adapter); + + } + + @CallSuper + @Override + public void dismissLoading() { + super.dismissLoading(); + stopRefresh(); + } + + + @CallSuper + @Override + public void showLoading() { + super.showLoading(); + hideEmptyView(); + } + + /** + * 停止刷新 + */ + protected void stopRefresh() { + refreshLayout.finishRefresh(); + refreshLayout.finishLoadMore(); + } + + /** + * 获取刷新控件 + * + * @return PullToRefreshLayout + */ + protected SmartRefreshLayout getRefreshLayout() { + return refreshLayout; + } + + /** + * 获取加载数据列表控件 + * + * @return RecyclerView + */ + protected RecyclerView getRecyclerView() { + return recyclerView; + } + + /** + * 返回适配器 + * + * @return RecyclerView.Adapter + */ + protected RecyclerView.Adapter getAdapter() { + return adapter; + } + + /** + * 创建 RecyclerView LayoutManager + * + * @return RecyclerView.LayoutManager + */ + protected abstract RecyclerView.LayoutManager getLayoutManager(); + + /** + * 创建刷新监听器 + * + * @return OnPullRefreshListener + */ + protected abstract OnRefreshLoadMoreListener createRefreshListener(); + + /** + * 创建列表适配器 + * + * @return RecyclerView.Adapter + */ + protected abstract RecyclerView.Adapter createAdapter(); +} diff --git a/common_base/src/main/java/com/wss/common/base/BaseRefreshListFragment.java b/common_base/src/main/java/com/wss/common/base/BaseRefreshListFragment.java new file mode 100644 index 0000000..a7d45c0 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/base/BaseRefreshListFragment.java @@ -0,0 +1,112 @@ +package com.wss.common.base; + + +import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.scwang.smartrefresh.layout.listener.OnRefreshLoadMoreListener; +import com.wss.common.base.mvp.BasePresenter; + +import androidx.annotation.CallSuper; +import androidx.recyclerview.widget.RecyclerView; +import butterknife.BindView; + +/** + * Describe:带下拉刷新 上拉加载更多的Fragment + * 内部实现为刷新控件 SmartRefreshLayout + 列表控件 RecyclerView + * Created by 吴天强 on 2018/10/23. + */ +public abstract class BaseRefreshListFragment

extends BaseMvpFragment

{ + + @BindView(R2.id.ptrl_list) + SmartRefreshLayout refreshLayout; + + @BindView(R2.id.recycle_view) + RecyclerView recyclerView; + /** + * 适配器 + */ + private RecyclerView.Adapter adapter; + + @Override + protected int getLayoutId() { + return R.layout.layout_refresh; + } + + @CallSuper + @Override + protected void initView() { + adapter = createAdapter(); + refreshLayout.setOnRefreshLoadMoreListener(createRefreshListener()); + recyclerView.setLayoutManager(getLayoutManager()); + recyclerView.setAdapter(adapter); + } + + @CallSuper + @Override + public void dismissLoading() { + super.dismissLoading(); + stopRefresh(); + } + + @CallSuper + @Override + public void showLoading() { + super.showLoading(); + hideEmptyView(); + } + + /** + * 停止刷新 + */ + protected void stopRefresh() { + refreshLayout.finishRefresh(); + refreshLayout.finishLoadMore(); + } + + /** + * 获取刷新控件 + * + * @return PullToRefreshLayout + */ + protected SmartRefreshLayout getRefreshLayout() { + return refreshLayout; + } + + /** + * 获取加载数据列表控件 + * + * @return RecyclerView + */ + protected RecyclerView getRecyclerView() { + return recyclerView; + } + + /** + * 返回适配器 + * + * @return RecyclerView.Adapter + */ + protected RecyclerView.Adapter getAdapter() { + return adapter; + } + + /** + * 创建 RecyclerView LayoutManager + * + * @return RecyclerView.LayoutManager + */ + protected abstract RecyclerView.LayoutManager getLayoutManager(); + + /** + * 创建刷新监听器 + * + * @return OnPullRefreshListener + */ + protected abstract OnRefreshLoadMoreListener createRefreshListener(); + + /** + * 创建列表适配器 + * + * @return RecyclerView.Adapter + */ + protected abstract RecyclerView.Adapter createAdapter(); +} diff --git a/common_base/src/main/java/com/wss/common/base/RefreshListActivity.java b/common_base/src/main/java/com/wss/common/base/RefreshListActivity.java deleted file mode 100644 index 552ecc4..0000000 --- a/common_base/src/main/java/com/wss/common/base/RefreshListActivity.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.wss.common.base; - -import android.support.annotation.CallSuper; -import android.support.v7.widget.RecyclerView; - -import com.wss.common.listener.OnListItemClickListener; -import com.wss.common.base.mvp.BasePresenter; -import com.wss.common.widget.pulltorefresh.OnPullRefreshListener; -import com.wss.common.widget.pulltorefresh.PullToRefreshLayout; - -import butterknife.BindView; - -/** - * Describe:带下拉刷新 上拉加载更多的Activity - * 内部实现为刷新控件 PullToRefreshLayout + 列表控件 RecyclerView - * Created by 吴天强 on 2018/10/23. - */ -public abstract class RefreshListActivity

extends ActionBarActivity

implements OnPullRefreshListener, OnListItemClickListener { - - @BindView(R2.id.ptrl_list) - PullToRefreshLayout refreshLayout; - - @BindView(R2.id.recycle_view) - RecyclerView recyclerView; - - protected RecyclerView.Adapter adapter; - - - @Override - protected int getLayoutId() { - return R.layout.layout_refresh; - } - - @CallSuper - @Override - protected void initView() { - refreshLayout.setOnPullRefreshListener(this); - recyclerView.setLayoutManager(getLayoutManager()); - adapter = createAdapter(); - recyclerView.setAdapter(adapter); - } - - @CallSuper - @Override - public void dismissLoading() { - super.dismissLoading(); - stopRefresh(); - hideEmptyView(); - } - - /** - * 停止刷新 - */ - protected void stopRefresh() { - refreshLayout.finishRefresh(); - refreshLayout.finishLoadMore(); - - } - - protected abstract RecyclerView.LayoutManager getLayoutManager(); - - protected abstract RecyclerView.Adapter createAdapter(); -} diff --git a/common_base/src/main/java/com/wss/common/base/RefreshListFragment.java b/common_base/src/main/java/com/wss/common/base/RefreshListFragment.java deleted file mode 100644 index 75874d9..0000000 --- a/common_base/src/main/java/com/wss/common/base/RefreshListFragment.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.wss.common.base; - -import android.support.annotation.CallSuper; -import android.support.v7.widget.RecyclerView; - -import com.wss.common.listener.OnListItemClickListener; -import com.wss.common.base.mvp.BasePresenter; -import com.wss.common.widget.pulltorefresh.OnPullRefreshListener; -import com.wss.common.widget.pulltorefresh.PullToRefreshLayout; - -import butterknife.BindView; - -/** - * Describe:带下拉刷新 上拉加载更多的Fragment - * 内部实现为刷新控件 PullToRefreshLayout + 列表控件 RecyclerView - * Created by 吴天强 on 2018/10/23. - */ -public abstract class RefreshListFragment

extends BaseMvpFragment

implements OnPullRefreshListener, OnListItemClickListener { - - - @BindView(R2.id.ptrl_list) - PullToRefreshLayout refreshLayout; - - @BindView(R2.id.recycle_view) - RecyclerView recyclerView; - - protected RecyclerView.Adapter adapter; - - @Override - protected int getLayoutId() { - return R.layout.layout_refresh; - } - - @CallSuper - @Override - protected void initView() { - adapter = createAdapter(); - refreshLayout.setOnPullRefreshListener(this); - recyclerView.setLayoutManager(getLayoutManager()); - recyclerView.setAdapter(adapter); - } - - @CallSuper - @Override - public void dismissLoading() { - super.dismissLoading(); - stopRefresh(); - hideEmptyView(); - } - - /** - * 停止刷新 - */ - protected void stopRefresh() { - refreshLayout.finishRefresh(); - refreshLayout.finishLoadMore(); - - } - - protected abstract RecyclerView.LayoutManager getLayoutManager(); - - protected abstract RecyclerView.Adapter createAdapter(); -} diff --git a/common_base/src/main/java/com/wss/common/base/adapter/BaseListAdapter.java b/common_base/src/main/java/com/wss/common/base/adapter/BaseListAdapter.java index 53b2747..77cce29 100644 --- a/common_base/src/main/java/com/wss/common/base/adapter/BaseListAdapter.java +++ b/common_base/src/main/java/com/wss/common/base/adapter/BaseListAdapter.java @@ -2,34 +2,70 @@ import android.content.Context; -import com.wss.common.listener.OnListItemClickListener; +import com.wss.common.base.adapter.listener.OnListItemClickListener; +import org.byteam.superadapter.IMulItemViewType; import org.byteam.superadapter.SuperAdapter; +import org.byteam.superadapter.SuperViewHolder; +import org.jetbrains.annotations.NotNull; import java.util.List; /** * Describe:万能适配器基类 适用于RecycleView ListView GridView等 - * 注意点:Item的最外层高度不能设置为 match_parent 否则滑动会出现混乱 目前还不知道原因、 + * 注意点:Item的最外层高度不能设置为 match_parent 否则滑动会出现混乱[TODO] * Created by 吴天强 on 2018/10/30. */ - public abstract class BaseListAdapter extends SuperAdapter { /** * Item点击监听 */ - protected OnListItemClickListener listener; + private OnListItemClickListener listener; - public BaseListAdapter(Context context, List items, int layoutResId, OnListItemClickListener listener) { - super(context, items, layoutResId); + /** + * 常规列表重写该方法 + * + * @param context context + * @param mData 数据源 + * @param layoutResId 布局文件 + * @param listener Item点击回调 + */ + public BaseListAdapter(Context context, List mData, int layoutResId, OnListItemClickListener listener) { + super(context, mData, layoutResId); this.listener = listener; } /** - * 添加点击事件监听 + * 多布局列表重写该方法 + * + * @param context context + * @param mData 数据源 + * @param multiItemViewType 多布局类型 */ - public void setOnListItemClickListener(OnListItemClickListener listener) { + public BaseListAdapter(Context context, List mData, IMulItemViewType multiItemViewType) { + super(context, mData, multiItemViewType); + } + + /** + * Item点击事件 + * + * @param listener listener + */ + public void setOnListItemClickListener(OnListItemClickListener listener) { this.listener = listener; } + + @Override + public void onBind(@NotNull final SuperViewHolder viewHolder, int viewType, final int layoutPosition, T data) { + viewHolder.itemView.setOnClickListener(v -> { + if (listener != null) { + listener.onItemClick(data, layoutPosition); + } + }); + onBindData(viewHolder, viewType, layoutPosition, data); + } + + public abstract void onBindData(@NotNull SuperViewHolder holder, int viewType, int layoutPosition, @NotNull T data); + } diff --git a/common_base/src/main/java/com/wss/common/base/adapter/FragmentPagerAdapter.java b/common_base/src/main/java/com/wss/common/base/adapter/FragmentPagerAdapter.java deleted file mode 100644 index e099116..0000000 --- a/common_base/src/main/java/com/wss/common/base/adapter/FragmentPagerAdapter.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.wss.common.base.adapter; - -import android.support.annotation.NonNull; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentStatePagerAdapter; -import android.view.ViewGroup; - -import com.wss.common.base.BaseFragment; -import com.wss.common.bean.HorizontalTabTitle; - -import java.util.List; - -/** - * Describe:FragmentPagerAdapter - * Created by 吴天强 on 2018/10/22. - */ - -public abstract class FragmentPagerAdapter extends FragmentStatePagerAdapter { - private BaseFragment[] fragments; - private List titles; - - public FragmentPagerAdapter(FragmentManager fm, List titles) { - super(fm); - this.titles = titles; - } - - @Override - public int getCount() { - return titles != null ? titles.size() : 0; - } - - - @Override - public Fragment getItem(int position) { - if (fragments == null) { - fragments = new BaseFragment[titles.size()]; - } - BaseFragment fragment = getTabFragment(); - fragment.setFragmentData(titles.get(position)); - fragments[position] = fragment; - return fragment; - } - - @Override - public int getItemPosition(@NonNull Object object) { - return POSITION_NONE; - } - - @Override - public void startUpdate(ViewGroup container) { - super.startUpdate(container); - } - - @Override - public CharSequence getPageTitle(int position) { - return titles.get(position).getTitle(); - } - - public abstract BaseFragment getTabFragment(); -} diff --git a/common_base/src/main/java/com/wss/common/base/adapter/listener/OnListItemClickListener.java b/common_base/src/main/java/com/wss/common/base/adapter/listener/OnListItemClickListener.java new file mode 100644 index 0000000..6e5aa16 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/base/adapter/listener/OnListItemClickListener.java @@ -0,0 +1,16 @@ +package com.wss.common.base.adapter.listener; + +/** + * Describe:RecycleView 点击事件监听 + * Created by 吴天强 on 2018/10/18. + */ +public interface OnListItemClickListener { + + /** + * Item点击事件 + * + * @param data Data + * @param position position + */ + void onItemClick(T data, int position); +} diff --git a/common_base/src/main/java/com/wss/common/base/bean/BaseBean.java b/common_base/src/main/java/com/wss/common/base/bean/BaseBean.java index 5336724..2a1f161 100644 --- a/common_base/src/main/java/com/wss/common/base/bean/BaseBean.java +++ b/common_base/src/main/java/com/wss/common/base/bean/BaseBean.java @@ -2,10 +2,11 @@ import java.io.Serializable; + /** * Describe:Bean基类 * Created by 吴天强 on 2018/10/17. */ - public class BaseBean implements Serializable { + } diff --git a/common_base/src/main/java/com/wss/common/base/mvp/BaseModel.java b/common_base/src/main/java/com/wss/common/base/mvp/BaseModel.java new file mode 100644 index 0000000..3ca9b7e --- /dev/null +++ b/common_base/src/main/java/com/wss/common/base/mvp/BaseModel.java @@ -0,0 +1,27 @@ +package com.wss.common.base.mvp; + + +import androidx.lifecycle.LifecycleOwner; + +/** + * Describe:网络请求基类 + * Created by 吴天强 on 2019/7/11. + */ +public class BaseModel { + + private LifecycleOwner lifecycleOwner; + + public BaseModel(LifecycleOwner lifecycleOwner) { + this.lifecycleOwner = lifecycleOwner; + } + + /** + * 返回生命周期所有者 + * + * @return LifecycleOwner + */ + protected LifecycleOwner getLifecycleOwner() { + return lifecycleOwner; + } + +} diff --git a/common_base/src/main/java/com/wss/common/base/mvp/BasePresenter.java b/common_base/src/main/java/com/wss/common/base/mvp/BasePresenter.java index 06f79fa..ed3fccc 100644 --- a/common_base/src/main/java/com/wss/common/base/mvp/BasePresenter.java +++ b/common_base/src/main/java/com/wss/common/base/mvp/BasePresenter.java @@ -2,24 +2,37 @@ import android.content.Context; +import java.lang.ref.WeakReference; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import androidx.lifecycle.LifecycleOwner; + /** * Describe:Presenter基类 * Created by 吴天强 on 2018/10/17. */ +@SuppressWarnings("unchecked") +public abstract class BasePresenter { -public abstract class BasePresenter { - - private V view; - private M module; - + private V mProxyView; + private M model; + private WeakReference weakReference; /** * 绑定View + * + * @param view view */ public void attachView(V view) { - this.view = view; - if (this.module == null) { - this.module = createModule(); + weakReference = new WeakReference<>(view); + mProxyView = (V) Proxy.newProxyInstance( + view.getClass().getClassLoader(), + view.getClass().getInterfaces(), + new MvpViewHandler(weakReference.get())); + if (this.model == null) { + this.model = createModule(); } } @@ -27,40 +40,99 @@ public void attachView(V view) { * 解绑View */ public void detachView() { - this.view = null; - this.module = null; + this.model = null; + if (!isViewDetached()) { + weakReference.clear(); + weakReference = null; + } } /** - * 是否与View建立连接 + * 是否与View断开连接 */ - protected boolean isViewAttached() { - return view != null; + protected boolean isViewDetached() { + return weakReference == null || weakReference.get() == null; } + /** + * 返回View + * + * @return view + */ protected V getView() { - return view; + return mProxyView; } - protected M getModule() { - return module; + /** + * 返回Model + * + * @return model + */ + protected M getModel() { + return model; } + /** + * 返回Context + * + * @return context + */ protected Context getContext() { return getView().getContext(); } + /** + * 返回持有View的生命周期所有者 + * + * @return LifecycleOwner + */ + protected LifecycleOwner getLifecycleOwner() { + return getView().getLifecycleOwner(); + } + + /** + * 显示加载框 + */ protected void showLoading() { - view.showLoading(); + getView().showLoading(); } + /** + * 隐藏加载框 + */ protected void dismissLoading() { - view.dismissLoading(); + getView().dismissLoading(); } - /** * 通过该方法创建Module */ protected abstract M createModule(); -} + + /** + * 初始化方法 + */ + public abstract void start(); + + + /** + * View代理类 防止 页面关闭P异步操作调用V 方法 空指针问题 + */ + private class MvpViewHandler implements InvocationHandler { + + private IBaseView mvpView; + + MvpViewHandler(IBaseView mvpView) { + this.mvpView = mvpView; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + //如果V层没被销毁, 执行V层的方法. + if (!isViewDetached()) { + return method.invoke(mvpView, args); + } //P层不需要关注V层的返回值 + return null; + } + } +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/base/mvp/IBaseModule.java b/common_base/src/main/java/com/wss/common/base/mvp/IBaseModule.java deleted file mode 100644 index e2a85b2..0000000 --- a/common_base/src/main/java/com/wss/common/base/mvp/IBaseModule.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.wss.common.base.mvp; - -/** - * Describe:所有Module的基类 - * Created by 吴天强 on 2018/10/17. - */ - -public interface IBaseModule { - -} diff --git a/common_base/src/main/java/com/wss/common/base/mvp/IBaseView.java b/common_base/src/main/java/com/wss/common/base/mvp/IBaseView.java index 6672132..5b6f526 100644 --- a/common_base/src/main/java/com/wss/common/base/mvp/IBaseView.java +++ b/common_base/src/main/java/com/wss/common/base/mvp/IBaseView.java @@ -2,20 +2,51 @@ import android.content.Context; +import androidx.lifecycle.LifecycleOwner; + /** * Describe:所有View基类 * Created by 吴天强 on 2018/10/17. */ - public interface IBaseView { + /** + * 显示加载框 + */ void showLoading(); + /** + * 隐藏加载框 + */ void dismissLoading(); + /** + * 空数据 + * + * @param tag TAG + */ void onEmpty(Object tag); + /** + * 错误数据 + * + * @param tag TAG + * @param errorMsg 错误信息 + */ void onError(Object tag, String errorMsg); + /** + * 上下文 + * + * @return context + */ Context getContext(); + + /** + * 返回页面生命周期 + * + * @return LifecycleOwner + */ + LifecycleOwner getLifecycleOwner(); + } diff --git a/common_base/src/main/java/com/wss/common/bean/Banner.java b/common_base/src/main/java/com/wss/common/bean/Banner.java new file mode 100644 index 0000000..265a5a0 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/bean/Banner.java @@ -0,0 +1,35 @@ +package com.wss.common.bean; + + +import com.wss.common.base.bean.BaseBean; + +import lombok.Getter; +import lombok.Setter; + +/** + * description: 房源首页Banner实体 + * + * @author 杨伟-tony + * create by 2020/5/21 20:40 + */ +@Getter +@Setter +public class Banner extends BaseBean { + /** + * 图片链接 + */ + private String imageUrl; + /** + * 跳转链接 + */ + private String redirectUrl; + + public Banner(String imageUrl) { + this.imageUrl = imageUrl; + } + + public Banner(String imageUrl, String redirectUrl) { + this.imageUrl = imageUrl; + this.redirectUrl = redirectUrl; + } +} diff --git a/common_base/src/main/java/com/wss/common/bean/Event.java b/common_base/src/main/java/com/wss/common/bean/Event.java index c5d1b36..554e1cb 100644 --- a/common_base/src/main/java/com/wss/common/bean/Event.java +++ b/common_base/src/main/java/com/wss/common/bean/Event.java @@ -1,5 +1,8 @@ package com.wss.common.bean; + +import com.wss.common.base.bean.BaseBean; + import lombok.Getter; import lombok.Setter; @@ -7,15 +10,19 @@ * Describe:EventBus事件类 * Created by 吴天强 on 2018/10/22. */ - @Getter @Setter -public class Event { +public class Event extends BaseBean { + /** + * 事件 + */ private String action; + /** + * 事件附带数据 + */ private T data; - public Event(String action) { this.action = action; } @@ -25,5 +32,4 @@ public Event(String action, T data) { this.data = data; } - } diff --git a/common_base/src/main/java/com/wss/common/bean/HorizontalTabTitle.java b/common_base/src/main/java/com/wss/common/bean/HorizontalTabTitle.java index e6215a6..2892986 100644 --- a/common_base/src/main/java/com/wss/common/bean/HorizontalTabTitle.java +++ b/common_base/src/main/java/com/wss/common/bean/HorizontalTabTitle.java @@ -1,18 +1,25 @@ package com.wss.common.bean; + import com.wss.common.base.bean.BaseBean; import lombok.Getter; import lombok.Setter; /** - * Describe:水平选项卡Title + * Describe:水平选项卡标题 * Created by 吴天强 on 2018/10/22. */ @Getter @Setter public class HorizontalTabTitle extends BaseBean { + /** + * 显示标题文字 + */ private String title; + /** + * 标题其他附带数据 + */ private T data; public HorizontalTabTitle(String title) { @@ -22,5 +29,7 @@ public HorizontalTabTitle(String title) { public HorizontalTabTitle(String title, T data) { this.title = title; this.data = data; + } + } diff --git a/common_base/src/main/java/com/wss/common/bean/SelectorData.java b/common_base/src/main/java/com/wss/common/bean/SelectorData.java new file mode 100644 index 0000000..85ec54c --- /dev/null +++ b/common_base/src/main/java/com/wss/common/bean/SelectorData.java @@ -0,0 +1,29 @@ +package com.wss.common.bean; + +import com.contrarywind.interfaces.IPickerViewData; +import com.wss.common.base.bean.BaseBean; +import com.wss.common.utils.ValidUtils; + +import lombok.Getter; +import lombok.Setter; + +/** + * Describe:弹窗选择器数据Bean + * Created by 吴天强 on 2020/4/21. + */ +@Getter +@Setter +public class SelectorData extends BaseBean implements IPickerViewData { + private String name; + private T data; + + public SelectorData(String name, T data) { + this.name = name; + this.data = data; + } + + @Override + public String getPickerViewText() { + return ValidUtils.isValid(name) ? name : ""; + } +} diff --git a/common_base/src/main/java/com/wss/common/bean/Template.java b/common_base/src/main/java/com/wss/common/bean/Template.java new file mode 100644 index 0000000..ecfaff6 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/bean/Template.java @@ -0,0 +1,55 @@ +package com.wss.common.bean; + +import com.wss.common.base.bean.BaseBean; +import com.wss.common.constants.Constants; + +import java.util.Map; + +import lombok.Getter; +import lombok.Setter; + +/** + * Describe:可跳转的模板 + * Created by 吴天强 on 2018/11/13. + */ +@Getter +@Setter +public class Template extends BaseBean { + + private String title; + private int res; + private String describe; + private Class clazz; + /** + * 模块外跳转链接 + */ + private String url; + /** + * 页面跳转类型 + */ + private int type; + /** + * 其他附加参数 + */ + private Map params; + + public Template(String title, String url, int type, String describe) { + this.title = title; + this.url = url; + this.type = type; + this.describe = describe; + } + + public Template(String title, Class clazz, String describe) { + this.title = title; + this.clazz = clazz; + this.type = Constants.TemplateType.ACTIVITY; + this.describe = describe; + } + + public Template(String title, int res, Class clazz) { + this.title = title; + this.res = res; + this.clazz = clazz; + } +} diff --git a/common_base/src/main/java/com/wss/common/bean/User.java b/common_base/src/main/java/com/wss/common/bean/User.java new file mode 100644 index 0000000..7f058ba --- /dev/null +++ b/common_base/src/main/java/com/wss/common/bean/User.java @@ -0,0 +1,42 @@ +package com.wss.common.bean; + +import com.wss.common.base.bean.BaseBean; + +import lombok.Getter; +import lombok.Setter; + +/** + * Describe:置业顾问信息 + * Created by 吴天强 on 2020/5/6. + */ +@Getter +@Setter +public class User extends BaseBean { + + /** + * email : banshengcy@163.com + * icon : + * id : 1260 + * password : ******* + * token : + * type : 0 + * username : 于慢慢家的吴蜀黍 + */ + + private Integer id; + private String username; + private String email; + private String icon; + private String password; + private String token; + private int type; + + public User() { + } + + public User(String username, String password) { + this.username = username; + this.password = password; + } + +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/constants/ARouterConfig.java b/common_base/src/main/java/com/wss/common/constants/ARouterConfig.java index 969f851..11e1eb7 100644 --- a/common_base/src/main/java/com/wss/common/constants/ARouterConfig.java +++ b/common_base/src/main/java/com/wss/common/constants/ARouterConfig.java @@ -4,21 +4,41 @@ * Describe:路由页面常量配置 注意:路径至少需要两级 {/xx/xx} * Created by 吴天强 on 2018/10/16. */ - public interface ARouterConfig { - //************************************Main模块***************************************** + //************************************Main模块*****************************************/ + /** + * 订单列表 + */ + String MAIN_ORDER_LIST = "/main/OrderListActivity"; //*************************************玩安卓模块*****************************************/ /** * 玩安卓首页 */ - String WAN_MAIN_ACTIVITY = "/wan/WanMainActivity"; + String WAN_MAIN_FRAGMENT = "/wan/HomeFragment"; + /** + * 我的收藏 + */ + String WAN_COLLECTION = "/wan/CollectionActivity"; - //*************************************商城模块*****************************************/ + //*************************************商城模块******************************************/ /** * 商场首页 */ String MARKET_MAIN_ACTIVITY = "/market/MarketMainActivity"; + + + //*************************************User模块*****************************************/ + /** + * 用户模块 + */ + String USER_MAIN_FRAGMENT = "/user/UserFragment"; + /** + * 登录 + */ + String USER_LOGIN = "/user/LoginActivity"; + + } diff --git a/common_base/src/main/java/com/wss/common/constants/Constant.java b/common_base/src/main/java/com/wss/common/constants/Constant.java deleted file mode 100644 index deca4c9..0000000 --- a/common_base/src/main/java/com/wss/common/constants/Constant.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.wss.common.constants; - -/** - * Describe:常量存放 - * Created by 吴天强 on 2017/10/9. - */ - -public class Constant { - - /** - * 服务器类型 - * SERVER_DEVELOP 开发环境 - * SERVER_TEST 测试环境 - * SERVER_PRODUCTION 生产环境 - */ - public static final int SERVER_TYPE = ServerType.SERVER_DEVELOP; - - /** - * 服务器类型 - */ - public class ServerType { - public static final int SERVER_DEVELOP = 0;//开发环境 - public static final int SERVER_TEST = 1;//测试环境 - public static final int SERVER_PRODUCTION = 2;//生产环境 - } - - - /** - * 模块类型 - */ - public static final int MODULE_TYPE_SHUTTLE = 0; - public static final int MODULE_TYPE_CHART = 1; - public static final int MODULE_TYPE_DEALS = 2; - - - /** - * 默认日期格式 - */ - public static final String DATE_FORMAT_SLASH = "yyyy/MM/dd"; - public static final String DATE_FORMAT_LINE = "yyyy-MM-dd"; - public static final String DATE_FORMAT_DEFAULT = DATE_FORMAT_SLASH + " HH:mm:ss"; - - public static final String ERROR_MESSAGE = "网络连接失败"; - - -} diff --git a/common_base/src/main/java/com/wss/common/constants/Constants.java b/common_base/src/main/java/com/wss/common/constants/Constants.java new file mode 100644 index 0000000..10883d1 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/constants/Constants.java @@ -0,0 +1,159 @@ +package com.wss.common.constants; + +import android.content.Context; + +import com.wss.common.base.BaseApplication; + +/** + * Describe:常量存放 + * Created by 吴天强 on 2017/10/9. + */ +public interface Constants { + /** + * 无法分类的散装常量 + */ + interface Common { + /** + * android + */ + int PLATFORM_ANDROID = 1; + + /** + * 渠道名称 + */ + String CHANEL = "android"; + /** + * API 版本 + */ + String API_VERSION = "1.0"; + } + + + /** + * WebView相关配置 + */ + interface WebView { + String CACHE_DIR = BaseApplication.i().getDir("cache", Context.MODE_PRIVATE).getPath(); + } + + + /** + * 网络请求相关 + */ + interface Net { + //response + String STATE = "errorCode"; + String MESSAGE = "errorMsg"; + String DATA = "data"; + + //request + String HEADER_CHANNEL = "channel"; + String HEADER_API_VERSION = "apiVersion"; + String HEADER_DEVICE_ID = "deviceid"; + String HEADER_TOKEN = "token"; + String HEADER_APP_VERSION = "appVersion"; + String HEADER_IP = "ip"; + String REQUEST_ID = "requestId"; + String REQUEST_DATA = "requestData"; + /** + * 超时时间 单位:秒 + */ + int TIME_OUT = 30; + + /** + * 接口响应码 + */ + interface Status { + /** + * 处理成功 + */ + String CODE_SUCCESS = "0"; + /** + * 该用户已经被禁用 + */ + String CODE_ACCOUNT_DISABLE = "1111"; + /** + * 该用户已经离职 + */ + String CODE_ACCOUNT_QUIT = "1112"; + /** + * 账号被锁定 + */ + String CODE_ACCOUNT_LOCKED = "1113"; + /** + * 此账户没有操作权限 + */ + String CODE_ACCOUNT_NO_AUTHORITY = "1114"; + /** + * 此账户没有归属团队 + */ + String CODE_ACCOUNT_NO_TEAM = "1118"; + /** + * 此账户同一项目归属多个团队 + */ + String CODE_ACCOUNT_MULTIPLE_TEAM = "1119"; + /** + * Token过期 + */ + String CODE_TOKEN_EXPIRED = "1120"; + /** + * 该用户的岗位信息有误 + */ + String CODE_ACCOUNT_POSITION_ERROR = "1128"; + + /** + * 参数异常 + */ + String INVALID_PARAMETER_ERROR = "403"; + } + } + + /** + * 选择文件的文件类型 + */ + interface FileType { + /** + * 图片 + */ + int IMAGE = 0; + /** + * 视频 + */ + int VIDEO = 1; + /** + * 音频 + */ + int AUDIO = 2; + /** + * 文档 + */ + int DOC = 3; + /** + * 压缩包 + */ + int PACKAGE = 4; + /** + * 其他 + */ + int OTHER = 5; + } + + /** + * 数据库配置 + */ + interface DBConfig { + String DB_NAME = "AMD.db"; + } + + /** + * 页面跳转类型 + */ + interface TemplateType { + + int ACTIVITY = 0; + int AROUTER = 1; + int WEB_VIEW = 2; + int SYS_BROWSER = 3; + + } +} diff --git a/common_base/src/main/java/com/wss/common/constants/Dic.java b/common_base/src/main/java/com/wss/common/constants/Dic.java new file mode 100644 index 0000000..9259e1f --- /dev/null +++ b/common_base/src/main/java/com/wss/common/constants/Dic.java @@ -0,0 +1,84 @@ +package com.wss.common.constants; + +/** + * Describe:字典 存放Activity跳转传参key、缓存数据到SP中key等。 + * Created by 吴天强 on 2018/11/13. + */ +public interface Dic { + + /** + * 跳转画廊参数-图片集合 + */ + String IMAGE_LIST = "image_list"; + /** + * 跳转画廊参数-图片起始位置 + */ + String IMAGE_POSITION = "image_position"; + /** + * 跳转画廊参数-是否本地图片 + */ + String IMAGE_LOCAL = "image_local"; + /** + * 跳转画廊参数-是否全屏显示 + */ + String FULL_SCREEN = "full_screen"; + + /** + * 通过路由跳转的Fragment的路径 + */ + String TARGET_FRAGMENT_PATH = "target_fragment_path"; + + /** + * 进入类型 + */ + String FROM_TYPE = "from_type"; + + /** + * 链接 + */ + String URL = "url"; + + /** + * 需要actionBar + */ + String IS_ACTION_BAR = "is_action_bar"; + /** + * actionBar 文字 + */ + String TITLE_TEXT = "title_text"; + /** + * bundle带入的数据 + */ + String BUNDLE_DATA = "bundle_data"; + + /** + * 可选文件的最大值 + */ + String MAX_SELECT_FILE = "max_select_file"; + /** + * 选择的文件路径列表 + */ + String SELECT_FILE_PATHS = "select_file_paths"; + /** + * 商品信息 + */ + String GOODS_INFO = "goods_info"; + /** + * 登录用户 + */ + String LOGIN_USER_INFO = "login_user_info"; + /** + * 系列子类 + */ + String CLASSIFICATION_CHILD = "classification_child"; + /** + * HouseFragment中的ViewPager + */ + String HOUSE_FRAGMENT_VIEWPAGER = "viewpager"; + /** + * 供应商 商品 信息 + */ + String VENDOR_GOODS_INFO = "vendor_goods_info"; + + +} diff --git a/common_base/src/main/java/com/wss/common/constants/EventAction.java b/common_base/src/main/java/com/wss/common/constants/EventAction.java new file mode 100644 index 0000000..267af44 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/constants/EventAction.java @@ -0,0 +1,41 @@ +package com.wss.common.constants; + +/** + * Describe:EventBus事件 + * Created by 吴天强 on 2018/10/19. + */ +public interface EventAction { + + //************************************Common模块****************************************/ + /** + * 删除画廊的图片, Action Data = 删除图片的position + */ + String IMAGE_GALLERY_DELETE = "image_gallery_delete"; + /** + * 二维码扫描结果 Action Data = String + */ + String SCAN_QR_CODE_RESULT = "scan_qr_code_result"; + + + + //************************************Market模块*****************************************/ + /** + * 购物车有变化 + */ + String EVENT_SHOPPING_CART_CHANGED = "event_shopping_cart_changed"; + /** + * 刷新购物车 重新获取数据 + */ + String EVENT_SHOPPING_CART_REFRESH = "event_shopping_cart_refresh"; + + //************************************User模块*****************************************/ + /** + * 登录成功 + */ + String EVENT_LOGIN_SUCCESS = "event_login_success"; + + /** + * 注册成功 + */ + String EVENT_REGISTER_SUCCESS = "event_register_success"; +} diff --git a/common_base/src/main/java/com/wss/common/constants/EventConstant.java b/common_base/src/main/java/com/wss/common/constants/EventConstant.java deleted file mode 100644 index 82925c3..0000000 --- a/common_base/src/main/java/com/wss/common/constants/EventConstant.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.wss.common.constants; - -/** - * Describe:EventBus事件 - * Created by 吴天强 on 2018/10/19. - */ - -public interface EventConstant { - - /** - * 商城商品被点击 - */ - String EVENT_MARKET_CLICK = "event_market_click"; -} diff --git a/common_base/src/main/java/com/wss/common/dao/BaseDBManager.java b/common_base/src/main/java/com/wss/common/dao/BaseDBManager.java new file mode 100644 index 0000000..5dfc8cb --- /dev/null +++ b/common_base/src/main/java/com/wss/common/dao/BaseDBManager.java @@ -0,0 +1,111 @@ +package com.wss.common.dao; + +import org.greenrobot.greendao.AbstractDao; +import org.greenrobot.greendao.query.QueryBuilder; + +import java.util.List; + +/** + * Describe:所有的数据库操作类都继承自此 + * Created by 吴天强 on 2018/11/5. + */ +public class BaseDBManager { + + private AbstractDao mDao; + + public BaseDBManager(AbstractDao dao) { + this.mDao = dao; + } + + public void insert(T item) { + mDao.insert(item); + } + + public void insert(T... items) { + mDao.insertInTx(items); + } + + public void insert(List items) { + mDao.insertInTx(items); + } + + public void insertOrUpdate(T item) { + mDao.insertOrReplace(item); + } + + public void insertOrUpdate(T... items) { + mDao.insertOrReplaceInTx(items); + } + + public void insertOrUpdate(List items) { + mDao.insertOrReplaceInTx(items); + } + + public void deleteByKey(K key) { + mDao.deleteByKey(key); + } + + public void delete(T item) { + mDao.delete(item); + } + + public void delete(T... items) { + mDao.deleteInTx(items); + } + + public void delete(List items) { + mDao.deleteInTx(items); + } + + public void deleteAll() { + mDao.deleteAll(); + } + + public void update(T item) { + mDao.update(item); + } + + public void update(T... items) { + mDao.updateInTx(items); + } + + public void update(List items) { + mDao.updateInTx(items); + } + + /** + * 根据主键查询数据 如果没有涉及主键 则会报错 + * + * @param key 主键 + * @return T + */ + public T query(K key) { + return mDao.load(key); + } + + public List queryAll() { + return mDao.loadAll(); + } + + public List query(String where, String... params) { + return mDao.queryRaw(where, params); + } + + public QueryBuilder getQueryBuilder() { + return mDao.queryBuilder(); + } + + public long count() { + return mDao.count(); + } + + public void refresh(T item) { + mDao.refresh(item); + } + + public void detach(T item) { + mDao.detach(item); + } + +} + diff --git a/common_base/src/main/java/com/wss/common/dao/DBMigrationHelper.java b/common_base/src/main/java/com/wss/common/dao/DBMigrationHelper.java new file mode 100644 index 0000000..b46cac6 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/dao/DBMigrationHelper.java @@ -0,0 +1,240 @@ +package com.wss.common.dao; + + +import android.database.Cursor; +import android.database.SQLException; +import android.database.sqlite.SQLiteDatabase; +import android.text.TextUtils; + +import com.orhanobut.logger.Logger; + +import org.greenrobot.greendao.AbstractDao; +import org.greenrobot.greendao.database.Database; +import org.greenrobot.greendao.database.StandardDatabase; +import org.greenrobot.greendao.internal.DaoConfig; +import org.jetbrains.annotations.Contract; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + + +/** + * Describe:数据库升级操作类 + * Created by 吴天强 on 2018/11/5. + */ +public class DBMigrationHelper { + + private static final String SQLITE_MASTER = "sqlite_master"; + private static final String SQLITE_TEMP_MASTER = "sqlite_temp_master"; + + static void migrate(SQLiteDatabase db, Class>... daoClasses) { + Logger.e("【The Old Database Version】" + db.getVersion()); + Database database = new StandardDatabase(db); + migrate(database, daoClasses); + } + + + @SafeVarargs + static void migrate(Database database, Class>... daoClasses) { + Logger.e("【Generate temp table】start"); + generateTempTables(database, daoClasses); + Logger.e("【Generate temp table】complete"); + + Logger.e("【Drop all table and recreate all table】"); + for (Class> daoClass : daoClasses) { + DaoConfig daoConfig = new DaoConfig(database, daoClass); + dropTable(database, true, daoConfig); + createTable(database, false, daoConfig); + } + + Logger.e("【Restore data】start"); + restoreData(database, daoClasses); + Logger.e("【Restore data】complete"); + } + + private static void dropTable(Database database, boolean ifExists, DaoConfig daoConfig) { + String sql = String.format("DROP TABLE %s\"%s\"", ifExists ? "IF EXISTS " : "", daoConfig.tablename); + database.execSQL(sql); + } + + @SafeVarargs + private static void generateTempTables(Database db, Class>... daoClasses) { + for (Class> daoClass : daoClasses) { + String tempTableName = null; + + DaoConfig daoConfig = new DaoConfig(db, daoClass); + String tableName = daoConfig.tablename; + if (!isTableExists(db, false, tableName)) { + Logger.e("【New Table】" + tableName); + continue; + } + try { + tempTableName = daoConfig.tablename.concat("_TEMP"); + StringBuilder dropTableStringBuilder = new StringBuilder(); + dropTableStringBuilder.append("DROP TABLE IF EXISTS ").append(tempTableName).append(";"); + Logger.e("【Generate temp table】 dropTableStringBuilder:" + dropTableStringBuilder); + db.execSQL(dropTableStringBuilder.toString()); + + StringBuilder insertTableStringBuilder = new StringBuilder(); + insertTableStringBuilder.append("CREATE TEMPORARY TABLE ").append(tempTableName); + insertTableStringBuilder.append(" AS SELECT * FROM ").append(tableName).append(";"); + Logger.e("【Generate temp table】 insertTableStringBuilder:" + insertTableStringBuilder); + db.execSQL(insertTableStringBuilder.toString()); + Logger.e("【Table】" + tableName + "\n ---Columns-->" + getColumnsStr(daoConfig)); + Logger.e("【Generate temp table】" + tempTableName); + } catch (SQLException e) { + Logger.e("【Failed to generate temp table】" + tempTableName, e); + } + } + } + + @Contract("null, _, _ -> false") + private static boolean isTableExists(Database db, boolean isTemp, String tableName) { + if (db == null || TextUtils.isEmpty(tableName)) { + return false; + } + String dbName = isTemp ? SQLITE_TEMP_MASTER : SQLITE_MASTER; + String sql = "SELECT COUNT(*) FROM " + dbName + " WHERE type = ? AND name = ?"; + Cursor cursor = null; + int count = 0; + try { + cursor = db.rawQuery(sql, new String[]{"table", tableName}); + if (cursor == null || !cursor.moveToFirst()) { + return false; + } + count = cursor.getInt(0); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (cursor != null) { + cursor.close(); + } + } + return count > 0; + } + + + private static String getColumnsStr(DaoConfig daoConfig) { + if (daoConfig == null) { + return "no columns"; + } + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < daoConfig.allColumns.length; i++) { + builder.append(daoConfig.allColumns[i]); + builder.append(","); + } + if (builder.length() > 0) { + builder.deleteCharAt(builder.length() - 1); + } + return builder.toString(); + } + + + @SafeVarargs + private static void restoreData(Database db, Class>... daoClasses) { + for (Class> daoClass : daoClasses) { + DaoConfig daoConfig = new DaoConfig(db, daoClass); + String tableName = daoConfig.tablename; + String tempTableName = daoConfig.tablename.concat("_TEMP"); + isTableExists(db, true, tempTableName); + try { + // get all columns from tempTable, take careful to use the columns list + List columns = getColumns(db, tempTableName); + ArrayList properties = new ArrayList<>(columns.size()); + for (int j = 0; j < daoConfig.properties.length; j++) { + String columnName = daoConfig.properties[j].columnName; + if (columns.contains(columnName)) { + properties.add(columnName); + } + } + if (properties.size() > 0) { + final String columnSQL = TextUtils.join(",", properties); + + StringBuilder insertTableStringBuilder = new StringBuilder(); + insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" ("); + insertTableStringBuilder.append(columnSQL); + insertTableStringBuilder.append(") SELECT "); + insertTableStringBuilder.append(columnSQL); + insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";"); + Logger.e("【Restore data】 db sql: " + insertTableStringBuilder); + db.execSQL(insertTableStringBuilder.toString()); + Logger.e("【Restore data】 to " + tableName); + } + StringBuilder dropTableStringBuilder = new StringBuilder(); + dropTableStringBuilder.append("DROP TABLE ").append(tempTableName); + db.execSQL(dropTableStringBuilder.toString()); + Logger.e("【Drop temp table】" + tempTableName); + } catch (SQLException e) { + Logger.e("【Failed to restore data from temp table 】" + tempTableName, e); + } + } + } + + private static List getColumns(Database db, String tableName) { + List columns = null; + try (Cursor cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 0", null)) { + if (null != cursor && cursor.getColumnCount() > 0) { + columns = Arrays.asList(cursor.getColumnNames()); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (null == columns) { + columns = new ArrayList<>(); + } + } + return columns; + } + + private static void createTable(Database db, boolean ifNotExists, DaoConfig daoConfig) { + String tableName = daoConfig.tablename; + StringBuilder builder = new StringBuilder(); + builder.append("CREATE TABLE "); + builder.append(ifNotExists ? "IF NOT EXISTS " : ""); + builder.append(tableName); + builder.append(getColumnsSql(daoConfig)); + Logger.e("【createTable】 sql:" + builder.toString()); + db.execSQL(builder.toString()); // 6: Description + } + + private static String getColumnsSql(DaoConfig daoConfig) { + if (daoConfig == null) { + return ""; + } + StringBuilder builder = new StringBuilder(" ("); + for (int i = 0; i < daoConfig.properties.length; i++) { + builder.append(String.format("\"%s\" %s,", daoConfig.properties[i].columnName, + getPropertyType(daoConfig.properties[i].type))); + } + if (daoConfig.properties.length > 0 && builder.length() > 0) { + builder.deleteCharAt(builder.length() - 1); + } + builder.append("); "); + return builder.toString(); + } + + /** + * 根据字段类型返回对应的数据库字段语句 + * + * @param type + * @return + */ + private static String getPropertyType(Class type) { + if (type.equals(byte[].class)) { + return "BLOB"; + } else if (type.equals(String.class)) { + return "TEXT DEFAULT ''"; + } else if (type.equals(boolean.class) || type.equals(Boolean.class) + || type.equals(int.class) || type.equals(Integer.class) + || type.equals(long.class) || type.equals(Long.class) + || type.equals(Date.class) || type.equals(Byte.class)) { + return "INTEGER DEFAULT (0)"; + } else if (type.equals(float.class) || type.equals(Float.class) + || type.equals(double.class) || type.equals(Double.class)) { + return "REAL DEFAULT (0)"; + } + return "TEXT DEFAULT ''"; + } +} diff --git a/common_base/src/main/java/com/wss/common/exception/NetErrorException.java b/common_base/src/main/java/com/wss/common/exception/NetErrorException.java new file mode 100644 index 0000000..deb20d6 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/exception/NetErrorException.java @@ -0,0 +1,53 @@ +package com.wss.common.exception; + +/** + * Describe:网络请求异常类 + * Created by 吴天强 on 2019/6/15. + */ +public class NetErrorException extends Exception { + + /** + * 网络请求错误码 + */ + private String errorCode; + /** + * 响应的Data + */ + private String responseData; + + public NetErrorException() { + } + + public NetErrorException(String message) { + this("", message); + } + + public NetErrorException(String errorCode, String message) { + this(errorCode, message, ""); + + } + + public NetErrorException(String errorCode, String message, String responseData) { + super(message); + this.errorCode = errorCode; + this.responseData = responseData; + } + + /** + * 返回网络请求错误码 + * + * @return errorCode + */ + public String getNetErrorCode() { + return errorCode; + } + + /** + * 返回网路响应的DATA + * + * @return responseData + */ + public String getNetResponseData() { + return responseData; + } +} diff --git a/common_base/src/main/java/com/wss/common/listener/OnListItemClickListener.java b/common_base/src/main/java/com/wss/common/listener/OnListItemClickListener.java deleted file mode 100644 index 958532e..0000000 --- a/common_base/src/main/java/com/wss/common/listener/OnListItemClickListener.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.wss.common.listener; - -/** - * Describe:RecycleView 点击事件监听 - * Created by 吴天强 on 2018/10/18. - */ - -public interface OnListItemClickListener { - - void onItemClick(int position); -} diff --git a/common_base/src/main/java/com/wss/common/manage/ActivityManage.java b/common_base/src/main/java/com/wss/common/manage/ActivityManage.java index 80b29d0..992ab1d 100644 --- a/common_base/src/main/java/com/wss/common/manage/ActivityManage.java +++ b/common_base/src/main/java/com/wss/common/manage/ActivityManage.java @@ -9,10 +9,11 @@ * Describe:管理所有的Activity * Created by 吴天强 on 2018/10/15. */ - public class ActivityManage { - //保存所有创建的Activity + /** + * 保存所有创建的Activity + */ private Set allActivities = new HashSet<>(); /** @@ -45,7 +46,5 @@ public void finishAll() { for (Activity activity : allActivities) { activity.finish(); } - } - } diff --git a/common_base/src/main/java/com/wss/common/manage/ActivityToActivity.java b/common_base/src/main/java/com/wss/common/manage/ActivityToActivity.java new file mode 100644 index 0000000..8d0a0af --- /dev/null +++ b/common_base/src/main/java/com/wss/common/manage/ActivityToActivity.java @@ -0,0 +1,301 @@ +package com.wss.common.manage; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.text.TextUtils; + +import com.alibaba.android.arouter.core.LogisticsCenter; +import com.alibaba.android.arouter.facade.Postcard; +import com.alibaba.android.arouter.launcher.ARouter; +import com.wss.common.base.bean.BaseBean; +import com.wss.common.bean.Template; +import com.wss.common.constants.Constants; +import com.wss.common.constants.Dic; +import com.wss.common.utils.ToastUtils; +import com.wss.common.utils.Utils; +import com.wss.common.view.browser.BrowserActivity; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import androidx.fragment.app.Fragment; + +/** + * Describe:Activity跳转 + * Created by 吴天强 on 2018/10/22. + */ +public class ActivityToActivity { + + + ////////////////////////////////////////普通Activity跳转//////////////////////////////////////// + + /** + * 普通Activity之间跳转 + * + * @param activity activity + * @param clazz 目标activity + */ + public static void toActivity(Context activity, Class clazz) { + toActivityForResult(activity, clazz, null, 0); + } + + /** + * 普通Activity之间跳转 + * + * @param activity activity + * @param clazz 目标activity + * @param params 携带参数 + */ + public static void toActivity(Context activity, Class clazz, Map params) { + toActivityForResult(activity, clazz, params, 0); + } + + /** + * 普通Activity之间跳转 + * + * @param activity activity + * @param clazz 目标activity + * @param requestCode 请求码 需大于0 + */ + public static void toActivityForResult(Context activity, Class clazz, int requestCode) { + toActivityForResult(activity, clazz, null, requestCode); + } + + /** + * 普通Activity之间跳转 + * ps:兼容从Fragment中使用ActivityForResult。传统方式Fragment中的onActivityResult中无法收到回调 + * + * @param fragment 当前fragment + * @param activity activity + * @param clazz 目标activity + * @param requestCode 请求码 需大于0 + */ + public static void toActivityForResult(Fragment fragment, Context activity, Class clazz, int requestCode) { + toActivityForResult(fragment, activity, clazz, null, requestCode); + } + + /** + * 普通Activity之间跳转 + * + * @param activity activity + * @param clazz 目标activity + * @param params 参数 + * @param requestCode 请求码 需大于0 + */ + public static void toActivityForResult(Context activity, Class clazz, Map params, int requestCode) { + Intent intent = new Intent(); + intent.setClass(activity, clazz); + assembleParams(intent, params); + if (requestCode > 0) { + ((Activity) activity).startActivityForResult(intent, requestCode); + } else { + activity.startActivity(intent); + } + } + + /** + * 在Fragment中调用startActivityForResult + * + * @param fragment 跳转所在的Fragment + * @param activity activity + * @param clazz 目标activity + * @param params 参数 + * @param requestCode 请求码 需大于0 + */ + public static void toActivityForResult(@NotNull Fragment fragment, Context activity, Class clazz, Map params, int requestCode) { + Intent intent = new Intent(); + intent.setClass(activity, clazz); + assembleParams(intent, params); + fragment.startActivityForResult(intent, requestCode); + } + + private static void assembleParams(Intent intent, Map params) { + if (params != null) { + for (Map.Entry entry : params.entrySet()) { + String key = entry.getKey(); + Object value = params.get(key); + if (value instanceof String) { + intent.putExtra(key, (String) value); + } else if (value instanceof Boolean) { + intent.putExtra(key, (boolean) value); + } else if (value instanceof Integer) { + intent.putExtra(key, (int) value); + } else if (value instanceof Float) { + intent.putExtra(key, (float) value); + } else if (value instanceof Double) { + intent.putExtra(key, (double) value); + } else if (value instanceof Long) { + intent.putExtra(key, (long) value); + } else if (value instanceof Short) { + intent.putExtra(key, (short) value); + } else if (value instanceof Bundle) { + intent.putExtra(key, (Bundle) value); + } else if (value instanceof BaseBean) { + intent.putExtra(key, (BaseBean) value); + } else if (value instanceof ArrayList) { + intent.putExtra(key, (ArrayList) value); + } else if (value instanceof HashMap) { + intent.putExtra(key, (HashMap) value); + } + } + } + } + //////////////////////////////////////WebView跳转////////////////////////////////////// + + /** + * 跳转WebView url 不可为空 + * + * @param context context + * @param url 链接 + * @param title 标题 + */ + public static void toWebView(Context context, String url, String title) { +// if (!NetworkUtil.isLink(url)) { +// return; +// } + Map param = new HashMap<>(); + param.put(Dic.URL, url); + param.put(Dic.TITLE_TEXT, title); + toActivity(context, BrowserActivity.class, param); + } + + /** + * 跳转WebView url 不可为空 + * + * @param context 链接 + * @param url 标题 + */ + public static void toWebView(Context context, String url) { + toWebView(context, url, ""); + } + + //////////////////////////////////////ARouter跳转////////////////////////////////////// + + /** + * ARouter跳转Activity + * + * @param activity Activity + * @param url 目标Activity Url + */ + public static void toActivity(Activity activity, String url) { + toActivityForResult(activity, url, null, 0); + } + + /** + * ARouter跳转Activity + * + * @param activity Activity + * @param url 目标Activity Url + */ + public static void toActivity(Activity activity, String url, Map params) { + toActivityForResult(activity, url, params, 0); + } + + /** + * ARouter跳转Activity + * + * @param activity Activity + * @param url 目标Activity Url + * @param requestCode 请求码 需大于0 + */ + public static void toActivityForResult(Activity activity, String url, int requestCode) { + toActivityForResult(activity, url, null, requestCode); + } + + /** + * ARouter跳转Activity + * + * @param activity Activity + * @param url 目标Activity Url + * @param params 参数 + * @param requestCode 请求码 需大于0 + */ + public static void toActivityForResult(Activity activity, String url, Map params, int requestCode) { + if (TextUtils.isEmpty(url)) { + return; + } + Postcard postcard = ARouter.getInstance() + .build(url); +// .withOptionsCompat(ActivityOptionsCompat.makeCustomAnimation(activity, R.anim.anim_right_in, R.anim.anim_right_out)); + if (params != null) { + for (Map.Entry entry : params.entrySet()) { + String key = entry.getKey(); + Object value = params.get(key); + if (value instanceof String) { + postcard.withString(key, (String) value); + } else if (value instanceof Boolean) { + postcard.withBoolean(key, (boolean) value); + } else if (value instanceof Integer) { + postcard.withInt(key, (int) value); + } else if (value instanceof Float) { + postcard.withFloat(key, (float) value); + } else if (value instanceof Double) { + postcard.withDouble(key, (double) value); + } else if (value instanceof Long) { + postcard.withLong(key, (long) value); + } else if (value instanceof Short) { + postcard.withShort(key, (short) value); + } else if (value instanceof Bundle) { + postcard.withBundle(key, (Bundle) value); + } else if (value instanceof BaseBean) { + postcard.withSerializable(key, (BaseBean) value); + } else if (value instanceof ArrayList) { + postcard.withSerializable(key, (ArrayList) value); + } else if (value instanceof HashMap) { + postcard.withSerializable(key, (HashMap) value); + } + } + } + if (requestCode > 0) { + LogisticsCenter.completion(postcard); + activity.startActivityForResult(new Intent(activity, postcard.getDestination()), requestCode); + } else { + postcard.navigation(); + } + } + + + /** + * 通过模板跳转Activity + * + * @param activity activity + * @param template 模板信息 + */ + @SuppressWarnings("unchecked") + public static void toActivity(Activity activity, Template template) { + switch (template.getType()) { + case Constants.TemplateType.ACTIVITY: + //跳转Activity + toActivity(activity, template.getClazz(), template.getParams()); + break; + case Constants.TemplateType.AROUTER: + //跳转Arouter + toActivity(activity, template.getUrl(), template.getParams()); + break; + case Constants.TemplateType.WEB_VIEW: + //跳转WebView + toWebView(activity, template.getUrl(), template.getDescribe()); + break; + case Constants.TemplateType.SYS_BROWSER: + //跳转手机浏览器 + Utils.toSystemBrowser(activity, template.getUrl()); + break; + default: + + } + } + + /** + * 跳转登录页 + * + * @param context ctx + */ + public static void toLoginActivity(Context context) { + ToastUtils.show("跳转登录页"); + } +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/manage/BlurTransformation.java b/common_base/src/main/java/com/wss/common/manage/BlurTransformation.java new file mode 100644 index 0000000..027edee --- /dev/null +++ b/common_base/src/main/java/com/wss/common/manage/BlurTransformation.java @@ -0,0 +1,88 @@ +package com.wss.common.manage; + +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Bitmap; +import android.os.Build; +import android.renderscript.Allocation; +import android.renderscript.Element; +import android.renderscript.RenderScript; +import android.renderscript.ScriptIntrinsicBlur; + +import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; +import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; + +import java.security.MessageDigest; + +import androidx.annotation.NonNull; + +/** + * Describe:加载图片转换器之模糊转换器 + * Created by 吴天强 on 2018/11/8. + */ +public class BlurTransformation extends BitmapTransformation { + private Context context; + /** + * 模糊度 0=< blurRadius<=25 + */ + private float blurRadius = 15; + + public BlurTransformation(Context context) { + this.context = context; + } + + /** + * @param context context + * @param blurRadius 模糊度 最大25 + */ + public BlurTransformation(Context context, float blurRadius) { + this.context = context; + this.blurRadius = blurRadius; + } + + @Override + protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) { + return blurBitmap(context, toTransform, outWidth, outHeight); + } + + @Override + public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) { + } + + + /** + * @param context 上下文对象 + * @param image 需要模糊的图片 + * @param outWidth 输入出的宽度 + * @param outHeight 输出的高度 + * @return 模糊处理后的Bitmap + */ + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + private Bitmap blurBitmap(Context context, Bitmap image, int outWidth, int outHeight) { + // 将缩小后的图片做为预渲染的图片 + Bitmap inputBitmap = Bitmap.createScaledBitmap(image, outWidth, outHeight, false); + // 创建一张渲染后的输出图片 + Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap); + // 创建RenderScript内核对象 + RenderScript rs = RenderScript.create(context); + // 创建一个模糊效果的RenderScript的工具对象 + ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); + // 由于RenderScript并没有使用VM来分配内存,所以需要使用Allocation类来创建和分配内存空间 + // 创建Allocation对象的时候其实内存是空的,需要使用copyTo()将数据填充进去 + Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap); + Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap); + // 设置渲染的模糊程度, 25f是最大模糊度 + //模糊度最大到25 + if (blurRadius < 0 || blurRadius > 25) { + blurRadius = 20; + } + blurScript.setRadius(blurRadius); + // 设置blurScript对象的输入内存 + blurScript.setInput(tmpIn); + // 将输出数据保存到输出内存中 + blurScript.forEach(tmpOut); + // 将数据填充到Allocation中 + tmpOut.copyTo(outputBitmap); + return outputBitmap; + } +} diff --git a/common_base/src/main/java/com/wss/common/manage/CrashHandlerManage.java b/common_base/src/main/java/com/wss/common/manage/CrashHandlerManage.java deleted file mode 100644 index f14f11f..0000000 --- a/common_base/src/main/java/com/wss/common/manage/CrashHandlerManage.java +++ /dev/null @@ -1,187 +0,0 @@ -package com.wss.common.manage; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.os.Build; -import android.os.Environment; -import android.support.annotation.Nullable; - -import com.orhanobut.logger.Logger; -import com.wss.common.base.BaseApplication; -import com.wss.common.utils.DateUtils; -import com.wss.common.utils.FileUtils; -import com.wss.common.utils.ZipUtils; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.Writer; -import java.lang.Thread.UncaughtExceptionHandler; -import java.lang.reflect.Field; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Map; - -/** - * UncaughtException处理类,当程序发生Uncaught异常的时候,由该类来接管程序,并记录发送错误报告. - */ -@SuppressLint("StaticFieldLeak") -public class CrashHandlerManage implements UncaughtExceptionHandler { - private static final String TAG = "CrashHandlerManage"; - private UncaughtExceptionHandler mDefaultHandler;// 系统默认的UncaughtException处理类 - private static CrashHandlerManage INSTANCE; - private Context mContext;// 程序的Context对象 - private Map info = new HashMap<>();// 用来存储设备信息和异常信息 - - /** - * 保证只有一个CrashHandler实例 - */ - private CrashHandlerManage() { - } - - public synchronized static CrashHandlerManage getInstance() { - if (INSTANCE == null) { - INSTANCE = new CrashHandlerManage(); - } - return INSTANCE; - } - - - /** - * 初始化 - */ - public void init(Context context) { - mContext = context; - mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();// 获取系统默认的UncaughtException处理器 - Thread.setDefaultUncaughtExceptionHandler(this);// 设置该CrashHandler为程序的默认处理器 - } - - /** - * 当UncaughtException发生时会转入该重写的方法来处理 - */ - public void uncaughtException(Thread thread, Throwable ex) { - if (!handleException(ex) && mDefaultHandler != null) { - // 如果自定义的没有处理则让系统默认的异常处理器来处理 - mDefaultHandler.uncaughtException(thread, ex); - } else { - try { - Thread.sleep(3000);// 如果处理了,让程序继续运行3秒再退出,保证文件保存并上传到服务器 - } catch (InterruptedException e) { - e.printStackTrace(); - } - ((BaseApplication) mContext.getApplicationContext()).exitApp(); - // 退出程序 - android.os.Process.killProcess(android.os.Process.myPid()); - System.exit(1); - } - } - - /** - * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. - * - * @param ex 异常信息 - * @return true 如果处理了该异常信息;否则返回false. - */ - public boolean handleException(Throwable ex) { - if (ex == null) - return false; - // 收集设备参数信息 - collectDeviceInfo(mContext); - // 保存日志文件 - saveCrashInfo2File(ex); - return true; - } - - /** - * 收集设备参数信息 - */ - public void collectDeviceInfo(Context context) { - try { - PackageManager pm = context.getPackageManager();// 获得包管理器 - PackageInfo pi = pm.getPackageInfo(context.getPackageName(), - PackageManager.GET_ACTIVITIES);// 得到该应用的信息,即主Activity - if (pi != null) { - String versionName = pi.versionName == null ? "null" - : pi.versionName; - String versionCode = pi.versionCode + ""; - info.put("versionName", versionName); - info.put("versionCode", versionCode); - } - } catch (NameNotFoundException e) { - e.printStackTrace(); - Logger.e("获取设置信息失败"); - } - - Field[] fields = Build.class.getDeclaredFields();// 反射机制 - for (Field field : fields) { - try { - field.setAccessible(true); - info.put(field.getName(), field.get("").toString()); - Logger.e(field.getName() + ":" + field.get("")); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - } - - @Nullable - private String saveCrashInfo2File(Throwable ex) { - StringBuilder sb = new StringBuilder(); - for (Map.Entry entry : info.entrySet()) { - String key = entry.getKey(); - String value = entry.getValue(); - sb.append(key).append("=").append(value).append("\r\n"); - } - Writer writer = new StringWriter(); - PrintWriter pw = new PrintWriter(writer); - ex.printStackTrace(pw); - Throwable cause = ex.getCause(); - // 循环着把所有的异常信息写入writer中 - while (cause != null) { - cause.printStackTrace(pw); - cause = cause.getCause(); - } - pw.close();// 记得关闭 - String result = writer.toString(); - sb.append(result); - // 保存文件 - String fileName = "crash-" + DateUtils.getCurrentDateStr() + "-" + DateUtils.getCurrentTimeStamp() + ".log"; - if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - try { - File file = new File(FileUtils.getAppCrashPath(), fileName); - FileOutputStream fos = new FileOutputStream(file); - fos.write(sb.toString().getBytes()); - fos.close(); - return fileName; - } catch (IOException e) { - e.printStackTrace(); - } - } - return null; - } - - /** - * 上传日志文件 - */ - public static void uploadCrashFiles() { - final File outFile = FileUtils.getAppCrashPath(); - LinkedList files = FileUtils.listLinkedFiles(FileUtils.getAppCrashPath().getPath()); - if (files == null || files.size() == 0) { - return; - } - try { - ZipUtils.zipFiles(files, outFile); - } catch (IOException e) { - e.printStackTrace(); - } - if (!outFile.exists()) {//如果这个zip文件不存在的话,则不执行如下的操作 - return; - } - //TODO 做上传操作 - } -} diff --git a/common_base/src/main/java/com/wss/common/manage/GlideRoundTransform.java b/common_base/src/main/java/com/wss/common/manage/GlideRoundTransform.java new file mode 100644 index 0000000..dcd825c --- /dev/null +++ b/common_base/src/main/java/com/wss/common/manage/GlideRoundTransform.java @@ -0,0 +1,60 @@ +package com.wss.common.manage; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; + +import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; +import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; +import com.bumptech.glide.load.resource.bitmap.TransformationUtils; +import com.wss.common.utils.PxUtils; + +import org.jetbrains.annotations.Contract; + +import java.security.MessageDigest; + +import androidx.annotation.NonNull; + +/** + * Describe:加载图片转换器之圆角转换器 + * Created by 吴天强 on 2019/7/2. + */ +public class GlideRoundTransform extends BitmapTransformation { + + private float radius = 0f; + + public GlideRoundTransform(Context context, int dp) { + super(); + radius = PxUtils.dp2px(dp); + } + + @Override + protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) { + Bitmap bitmap = TransformationUtils.centerCrop(pool, toTransform, outWidth, outHeight); + return roundCrop(pool, bitmap); + } + + @Contract("_, null -> null") + private Bitmap roundCrop(BitmapPool pool, Bitmap source) { + if (source == null){ return null;} + Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(result); + Paint paint = new Paint(); + paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP)); + paint.setAntiAlias(true); + RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight()); + canvas.drawRoundRect(rectF, radius, radius, paint); + return result; + } + + public String getId() { + return getClass().getName() + Math.round(radius); + } + + @Override + public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) { + } +} diff --git a/common_base/src/main/java/com/wss/common/manage/MediaScannerManage.java b/common_base/src/main/java/com/wss/common/manage/MediaScannerManage.java new file mode 100644 index 0000000..c09c792 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/manage/MediaScannerManage.java @@ -0,0 +1,74 @@ +package com.wss.common.manage; + +import android.content.Context; +import android.media.MediaScannerConnection; +import android.net.Uri; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.io.File; + +/** + * Describe:扫描媒体文件帮助类(主要用户保存文件、图片之后刷新到相册 操作) + * Created by 吴天强 on 2021/5/10. + */ +public class MediaScannerManage { + + private MediaScannerConnection mConn = null; + private File mFile = null; + + private MediaScannerManage(Context context) { + ScannerClient mClient = new ScannerClient(); + if (mConn == null) { + mConn = new MediaScannerConnection(context, mClient); + } + } + + @NotNull + @Contract("_ -> new") + public static synchronized MediaScannerManage getInstance(Context context) { + return new MediaScannerManage(context); + } + + + public void scanFile(File file) { + mFile = file; + mConn.connect(); + } + + private class ScannerClient implements MediaScannerConnection.MediaScannerConnectionClient { + + @Override + public void onMediaScannerConnected() { + if (mFile == null) { + return; + } + scan(mFile); + } + + @Override + public void onScanCompleted(String path, Uri uri) { + mConn.disconnect(); + } + + private void scan(@NotNull File file) { + + if (file.isFile()) { + mConn.scanFile(file.getAbsolutePath(), null); + return; + } + File[] files = file.listFiles(); + if (files == null) { + return; + } + File[] files1 = file.listFiles(); + if (files1 == null) { + return; + } + for (File f : files1) { + scan(f); + } + } + } +} diff --git a/common_base/src/main/java/com/wss/common/manage/UpdateManager.java b/common_base/src/main/java/com/wss/common/manage/UpdateManager.java deleted file mode 100644 index dcf0cd5..0000000 --- a/common_base/src/main/java/com/wss/common/manage/UpdateManager.java +++ /dev/null @@ -1,158 +0,0 @@ -package com.wss.common.manage; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Handler; -import android.os.Message; -import android.text.TextUtils; -import android.view.View; -import android.widget.ProgressBar; - -import com.tamic.novate.Throwable; -import com.tamic.novate.callback.RxFileCallBack; -import com.wss.common.base.BaseApplication; -import com.wss.common.base.R; -import com.wss.common.net.HttpUtils; -import com.wss.common.utils.FileUtils; -import com.wss.common.utils.ToastUtils; -import com.wss.common.widget.dialog.AppDialog; - -import java.io.File; - - -/** - * 更新下载APK - * Created by wtq on 2016/7/22. - */ -@SuppressLint("StaticFieldLeak") -public class UpdateManager { - - private static final int DOWNLOADING = 0x00; - private static final int DOWNLOAD_SUCCESS = 0x02; - private static final int DOWNLOAD_FAILED = 0x03; - private static final int DOWNLOAD_CANCEL = 0x04; - private static String DOWNLOAD_PATH = "/updateApkFile/"; - private static String APK_NAME = BaseApplication.getApplication().getPackageName() + "Temp.apk"; - /** - * 下载之后存放路径 - */ - private String apkPath = FileUtils.getTempPath() + DOWNLOAD_PATH + APK_NAME; - - private Context mContext; - private ProgressBar progressBar; - private AppDialog dialog; - private static UpdateManager manager; - private String downloadUrl; - - private UpdateManager(Context context) { - this.mContext = context; - //每次启动先删除本地原有APK - FileUtils.deleteFile(apkPath); - } - - public static synchronized UpdateManager getInstance(Context context) { - if (manager == null) { - manager = new UpdateManager(context); - } - return manager; - } - - /** - * 设置下载地址 - * - * @param url url - * @return UpdateManager - */ - public UpdateManager setDownloadUrl(String url) { - this.downloadUrl = url; - return this; - } - - /** - * 弹出下载框 - */ - public void download() { - if (TextUtils.isEmpty(downloadUrl)) { - ToastUtils.showToast(mContext, "请设置下载Url"); - return; - } - - View progressBarView = View.inflate(mContext, R.layout.laytou_update_progress, null); - progressBar = progressBarView.findViewById(R.id.pb_update_progress); - dialog = new AppDialog(mContext); - dialog.setTitle("版本更新中···") - .addContentView(progressBarView) - .setSingleButton("后台下载", new AppDialog.OnButtonClickListener() { - @Override - public void onClick(String val) { - ToastUtils.showToast(mContext, "已切换到后台下载···"); - } - }) - .show(); - HttpUtils.getInstance(mContext) - .downloadFile(downloadUrl, new RxFileCallBack(apkPath, "Temp.apk") { - - @Override - public void onNext(Object tag, File file) { - updateHandler.sendEmptyMessage(DOWNLOAD_SUCCESS); - } - - @Override - public void onProgress(Object tag, float progress, long downloaded, long total) { - Message message = updateHandler.obtainMessage(); - message.what = DOWNLOADING; - message.obj = (int) progress; - updateHandler.sendMessage(message); - } - - @Override - public void onError(Object tag, Throwable e) { - updateHandler.sendEmptyMessage(DOWNLOAD_FAILED); - } - - @Override - public void onCancel(Object tag, Throwable e) { - updateHandler.sendEmptyMessage(DOWNLOAD_CANCEL); - } - }); - } - - - @SuppressLint("HandlerLeak") - private Handler updateHandler = new Handler() { - public void handleMessage(Message msg) { - switch (msg.what) { - case DOWNLOADING: - progressBar.setProgress((Integer) msg.obj); - break; - case DOWNLOAD_SUCCESS: - dialog.dismiss(); - installApk(); - break; - case DOWNLOAD_FAILED: - dialog.dismiss(); - ToastUtils.showToast(mContext, "下载失败"); - break; - case DOWNLOAD_CANCEL: - dialog.dismiss(); - ToastUtils.showToast(mContext, "已取消下载"); - break; - default: - break; - } - } - - }; - - /** - * 安装apk - */ - private void installApk() { - // 安装,如果签名不一致,可能出现程序未安装提示 - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(Uri.fromFile(new File(apkPath)), "application/vnd.android.package-archive"); - mContext.startActivity(intent); - } -} diff --git a/common_base/src/main/java/com/wss/common/net/Api.java b/common_base/src/main/java/com/wss/common/net/Api.java index a3d0583..7eff724 100644 --- a/common_base/src/main/java/com/wss/common/net/Api.java +++ b/common_base/src/main/java/com/wss/common/net/Api.java @@ -1,12 +1,22 @@ package com.wss.common.net; + +import com.wss.common.profile.ProfileManager; + +import rxhttp.wrapper.annotation.DefaultDomain; + /** - * Describe:接口 + * Describe:接口名称 * Created by 吴天强 on 2018/10/17. */ - public interface Api { + /** + * 网络请求BaseUrl + */ + @DefaultDomain + String BASE_URL = ProfileManager.profile().getServiceBase(); + //**********************************************鸿洋开放接口************************************** /** * 获取Banner */ @@ -17,8 +27,71 @@ public interface Api { */ String GET_ARTICLE_LIST = "/article/list/%s/json"; + + /** + * 登录 + */ + String LOGIN = "/user/login"; + /** + * 注册 + */ + String REGISTER = "/user/register"; + + /** + * 体系 + */ + String TREE = "/tree/json"; + /** + * 体系下文章 + */ + String TREE_ARTICLE = "/article/list/%s/json?cid=%s"; + + /** + * 项目分类 + */ + String PROJECT = "/project/tree/json"; + + /** + * 项目列表 + */ + String PROJECT_LIST = "/project/list/%s/json?cid=%s"; + + /** + * 查询公众号列表 + */ + String WX_NUMBER = "/wxarticle/chapters/json"; + /** + * 查询公众号文章 + */ + String WX_ARTICLE_LIST = "/wxarticle/list/%s/%s/json"; + + /** + * 搜索 + */ + String SEARCH_LIST = "/article/query/%s/json?k=%s"; + + /** + * 收藏列表 + */ + String COLLECTION_LIST = "/lg/collect/list/%s/json"; + + + //**********************************************鸿洋开放接口************************************** + + + //**********************************************其他服务接口************************************ + /** - * 下载文件 + * 上传文件(替换成自己的服务接口地址) */ - String DOWNLOAD_FILE = "http://wap.dl.pinyin.sogou.com/wapdl/hole/201512/03/SogouInput_android_v7.11_sweb.apk"; + String UPLOAD_FILE = "/app/upload/uploadFile.json"; + + /** + * 下载文件(替换成自己的服务接口地址) + */ + String DOWNLOAD_FILE = "/app/download/downloadFile.json"; + + //**********************************************其他服务接口************************************ + + } diff --git a/common_base/src/main/java/com/wss/common/net/HttpUtils.java b/common_base/src/main/java/com/wss/common/net/HttpUtils.java deleted file mode 100644 index 4aa4899..0000000 --- a/common_base/src/main/java/com/wss/common/net/HttpUtils.java +++ /dev/null @@ -1,289 +0,0 @@ -package com.wss.common.net; - -import android.content.Context; - -import com.orhanobut.logger.Logger; -import com.tamic.novate.Novate; -import com.tamic.novate.callback.ResponseCallback; -import com.tamic.novate.callback.RxFileCallBack; - -import java.io.File; -import java.util.List; - -/** - * Describe:网络请求帮助类 - * Created by 吴天强 on 2017/9/19. - */ - -public class HttpUtils { - - private static final int REQUEST_GET = 0; - private static final int REQUEST_POST = 1; - private static final int REQUEST_JSON = 2; - private Novate.Builder builder; - private static HttpUtils httpUtils; - - private HttpUtils(Context context) { - builder = new Novate.Builder(context); - builder.baseUrl(NetConfig.Url.getBaseUrl()); - - //https配置 xxx.cer放在asset目录下 -// builder.skipSSLSocketFactory(true);//信任所有证书 -// builder.addSSLSocketFactory(NovateHttpsFactroy.creatSSLSocketFactory( -// BaseApplication.getApplication().getBaseContext(), "xxx.cer")); - -// builder.addHeader(headers); //添加公共请求头 -// builder.addParameters(parameters);//公共参数 -// builder.connectTimeout(10); //连接时间 可以忽略 -// builder.addCookie(false); //是否同步cooike 默认不同步 -// builder.addCache(true); //是否缓存 默认缓存 -// builder.addCache(cache, cacheTime); //自定义缓存 -// builder.addLog(true);//是否开启log -// builder.cookieManager(new NovateCookieManager()); // 自定义cooike,可以忽略 -// builder.addInterceptor(); // 自定义Interceptor -// builder.addNetworkInterceptor(); // 自定义NetworkInterceptor -// builder.proxy(proxy); //代理 -// builder.client(client); //clent 默认不需要 - } - - public static synchronized HttpUtils getInstance(Context context) { - if (httpUtils == null) { - httpUtils = new HttpUtils(context); - } - return httpUtils; - } - - - ////////////////////////////////////////// GET请求 ///////////////////////////////////////////// - - /** - * Get无参无Tag - * - * @param url 请求地址 - * @param callback 回调 - */ - public void getRequest(String url, ResponseCallback callback) { - request(REQUEST_GET, url, new RequestParam(), null, callback); - } - - /** - * Get 无参有Tag - * - * @param url 请求地址 - * @param tag 标签 - * @param callback 回调 - */ - public void getRequest(String url, String tag, ResponseCallback callback) { - request(REQUEST_GET, url, new RequestParam(), tag, callback); - } - - /** - * Get有参无Tag - * - * @param url 请求地址 - * @param params 请求参数 - * @param callback 回调 - */ - public void getRequest(String url, RequestParam params, ResponseCallback callback) { - request(REQUEST_GET, url, params, null, callback); - } - - /** - * Get有参有Tag - * - * @param url 请求地址 - * @param params 请求参数 - * @param tag 标签 - * @param callback 回调 - */ - public void getRequest(String url, RequestParam params, String tag, ResponseCallback callback) { - request(REQUEST_GET, url, params, tag, callback); - } - - - ////////////////////////////////////////// POST请求 ///////////////////////////////////////////// - - /** - * Post 无参无TAG - * - * @param url 请求地址 - * @param callback 回调 - */ - public void postRequest(String url, ResponseCallback callback) { - request(REQUEST_POST, url, new RequestParam(), null, callback); - } - - /** - * Post 无参有TAG - * - * @param url 请求地址 - * @param tag 标签 - * @param callback 回调 - */ - public void postRequest(String url, String tag, ResponseCallback callback) { - request(REQUEST_POST, url, new RequestParam(), tag, callback); - } - - /** - * Post 有参无TAG - * - * @param url 请求地址 - * @param params 请求参数 - * @param callback 回调 - */ - public void postRequest(String url, RequestParam params, ResponseCallback callback) { - request(REQUEST_POST, url, params, null, callback); - } - - /** - * Post 有参有TAG - * - * @param url 请求地址 - * @param params 请求参数 - * @param tag 标签 - * @param callback 回调 - */ - public void postRequest(String url, RequestParam params, String tag, ResponseCallback callback) { - request(REQUEST_POST, url, params, tag, callback); - } - - ////////////////////////////////////////// JSON格式请求 ///////////////////////////////////////////// - - /** - * Post 无参无TAG - * - * @param url 请求地址 - * @param callback 回调 - */ - public void jsonRequest(String url, ResponseCallback callback) { - request(REQUEST_JSON, url, new RequestParam(), null, callback); - } - - /** - * Post 无参有TAG - * - * @param url 请求地址 - * @param tag 标签 - * @param callback 回调 - */ - public void jsonRequest(String url, String tag, ResponseCallback callback) { - request(REQUEST_JSON, url, new RequestParam(), tag, callback); - } - - /** - * Post 有参无TAG - * - * @param url 请求地址 - * @param params 请求参数 - * @param callback 回调 - */ - public void jsonRequest(String url, RequestParam params, ResponseCallback callback) { - request(REQUEST_JSON, url, params, null, callback); - } - - /** - * Post 有参有TAG - * - * @param url 请求地址 - * @param params 请求参数 - * @param tag 标签 - * @param callback 回调 - */ - public void jsonRequest(String url, RequestParam params, String tag, ResponseCallback callback) { - request(REQUEST_JSON, url, params, tag, callback); - } - - - /** - * 最终请求 - * - * @param methodType 请求类型 get post - * @param url 接口 - * @param params 参数 - * @param tag tag - * @param callback 请求回调 - */ - private void request(int methodType, String url, RequestParam params, String tag, ResponseCallback callback) { - Logger.e(NetConfig.Url.getBaseUrl() + url + params.toJson()); -// builder.header(""); - switch (methodType) { - case REQUEST_POST: - builder.build().rxPost(tag, url, params.getParameter(), callback); - break; - case REQUEST_GET: - builder.build().rxGet(tag, url, params.getParameter(), callback); - break; - case REQUEST_JSON: - builder.build().rxJson(tag, url, params.toJson(), callback); - break; - default: - builder.build().rxGet(tag, url, params.getParameter(), callback); - break; - } - } - - /** - * 上传单个文件 - * - * @param url 接口 - * @param file 文件 - * @param callback 回调 - */ - public void upLoadFile(String url, File file, ResponseCallback callback) { - upLoadFile(url, file, null, callback); - } - - /** - * 上传单个文件 - * - * @param url 接口 - * @param file 文件 - * @param tag 标签 - * @param callback 回调 - */ - public void upLoadFile(String url, File file, String tag, ResponseCallback callback) { - //使用Part 方式上传文件 - Logger.e(NetConfig.Url.getBaseUrl() + url + file.getAbsolutePath()); - builder.build().rxUploadWithPart(url, file, callback); - } - - /** - * 上传多个文件 - * - * @param url 接口 - * @param files 文件 - * @param callback 回调 - */ - public void upLoadFile(String url, List files, ResponseCallback callback) { - upLoadFile(url, files, null, callback); - } - - - /** - * 上传多个文件 - * - * @param url 接口 - * @param files 文件 - * @param tag 标签 - * @param callback 回调 - */ - public void upLoadFile(String url, List files, String tag, ResponseCallback callback) { - builder.build().rxUploadWithPartListByFile(tag, url, files, callback); - } - - /** - * 文件下载 - * - * @param url 文件路径 - * @param callBack 回调 - */ - public void downloadFile(String url, RxFileCallBack callBack) { - builder.build().rxDownload(url, callBack); - } - - - private boolean judgeUrl(String url) { - return url.startsWith("http://") || url.startsWith("https://"); - } - -} diff --git a/common_base/src/main/java/com/wss/common/net/NetConfig.java b/common_base/src/main/java/com/wss/common/net/NetConfig.java deleted file mode 100644 index 8d8f360..0000000 --- a/common_base/src/main/java/com/wss/common/net/NetConfig.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.wss.common.net; - -import com.wss.common.constants.Constant; - - -/** - * Describe:网络请求URL - * Created by 吴天强 on 2017/9/26. - */ - -public class NetConfig { - - - /** - * 响应的返回key - */ - public class Code { - public static final String SUCCESS = "success"; - public static final String MSG = "errorMsg"; - public static final String CODE = "errorCode"; - public static final String MODEL = "data"; - } - - /** - * H5界面 - */ - public class Html { - - } - - /** - * 网络请求Url - */ - public static class Url { - - - //服务器地址 - interface BaseUrl { - String SERVER_DEVELOP = "http://www.wanandroid.com"; - String SERVER_TEST = ""; - String SERVER_PRODUCTION = ""; - } - - /** - * 返回服务器基础地址 - */ - static String getBaseUrl() { - switch (Constant.SERVER_TYPE) { - case Constant.ServerType.SERVER_DEVELOP: - return BaseUrl.SERVER_DEVELOP; - case Constant.ServerType.SERVER_TEST: - return BaseUrl.SERVER_TEST; - case Constant.ServerType.SERVER_PRODUCTION: - return BaseUrl.SERVER_PRODUCTION; - } - return BaseUrl.SERVER_PRODUCTION; - } - - - } - -} diff --git a/common_base/src/main/java/com/wss/common/net/NetworkManage.java b/common_base/src/main/java/com/wss/common/net/NetworkManage.java new file mode 100644 index 0000000..c8ff90c --- /dev/null +++ b/common_base/src/main/java/com/wss/common/net/NetworkManage.java @@ -0,0 +1,617 @@ +package com.wss.common.net; + +import android.os.Handler; +import android.text.TextUtils; + +import com.orhanobut.logger.Logger; +import com.rxjava.rxlife.RxLife; +import com.wss.common.base.BaseApplication; +import com.wss.common.base.R; +import com.wss.common.constants.Constants; +import com.wss.common.exception.NetErrorException; +import com.wss.common.manage.ActivityToActivity; +import com.wss.common.net.request.RequestParam; +import com.wss.common.net.response.BaseResponse; +import com.wss.common.net.response.DownloadResponse; +import com.wss.common.profile.ProfileManager; +import com.wss.common.secret.AesUtils; +import com.wss.common.utils.JsonUtils; +import com.wss.common.utils.NetworkUtil; +import com.wss.common.utils.ToastUtils; +import com.wss.common.utils.Utils; +import com.wss.common.utils.ValidUtils; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.net.SocketTimeoutException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import androidx.annotation.NonNull; +import androidx.lifecycle.LifecycleOwner; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; +import rxhttp.wrapper.exception.HttpStatusCodeException; +import rxhttp.wrapper.param.RxHttp; +import rxhttp.wrapper.param.RxHttpFormParam; +import rxhttp.wrapper.param.RxHttpJsonParam; +import rxhttp.wrapper.param.RxHttpNoBodyParam; + + +/** + * Describe:网络请求帮助类 + * Created by 吴天强 on 2020/1/3. + */ +@SuppressWarnings("unchecked") +public class NetworkManage { + /** + * 取第一个进来的跳转到登录页 + */ + public volatile static int count = 0; + private static final String GET = "GET"; + private static final String POST_FORM = "POST_FORM"; + private static final String POST_JSON = "POST_JSON"; + private static final String PUT_FORM = "PUT_FORM"; + private static final String PUT_JSON = "PUT_JSON"; + private static final String DELETE_FORM = "DELETE_FORM"; + private static final String DELETE_JSON = "DELETE_JSON"; + /** + * 账户相关的报错信息 + */ + private static final List ACCOUNT_ERROR_CODE = new ArrayList<>(); + + /** + * 无须加密白名单 + */ + private static final List NO_DECRYPTION_LIST = new ArrayList<>(); + + static { + ACCOUNT_ERROR_CODE.add(Constants.Net.Status.CODE_ACCOUNT_DISABLE); + ACCOUNT_ERROR_CODE.add(Constants.Net.Status.CODE_ACCOUNT_QUIT); + ACCOUNT_ERROR_CODE.add(Constants.Net.Status.CODE_ACCOUNT_LOCKED); + ACCOUNT_ERROR_CODE.add(Constants.Net.Status.CODE_ACCOUNT_NO_AUTHORITY); + ACCOUNT_ERROR_CODE.add(Constants.Net.Status.CODE_ACCOUNT_NO_TEAM); + ACCOUNT_ERROR_CODE.add(Constants.Net.Status.CODE_ACCOUNT_MULTIPLE_TEAM); + ACCOUNT_ERROR_CODE.add(Constants.Net.Status.CODE_TOKEN_EXPIRED); + ACCOUNT_ERROR_CODE.add(Constants.Net.Status.CODE_ACCOUNT_POSITION_ERROR); + + NO_DECRYPTION_LIST.add(Api.UPLOAD_FILE); + NO_DECRYPTION_LIST.add(Api.DOWNLOAD_FILE); + } + + /** + * 构建Get请求 + */ + @NonNull + public static Request createGet() { + return create(GET); + } + + /** + * 构建PostFrom请求 + */ + @NonNull + public static Request createPostForm() { + return create(POST_FORM); + } + + /** + * 构建PostJson请求 + */ + @NonNull + public static Request createPostJson() { + return create(POST_JSON); + } + + /** + * 构建PutForm请求 + */ + @NonNull + public static Request createPutForm() { + return create(PUT_FORM); + } + + /** + * 构建PutJson请求 + */ + @NonNull + public static Request createPutJson() { + return create(PUT_JSON); + } + + /** + * 构建DeleteForm请求 + */ + @NonNull + public static Request createDeleteForm() { + return create(DELETE_FORM); + } + + /** + * 构建DeleteJson请求 + */ + @NonNull + public static Request createDeleteJson() { + return create(DELETE_JSON); + } + + + /** + * 构建请求类型 + */ + @Contract(value = "_ -> new", pure = true) + @NonNull + private static Request create(String method) { + return new Request(method); + } + + /** + * 网络请求主类 + */ + public static class Request { + private String method; + private String requestId; + + + Request(String method) { + this.method = method; + requestId = UUID.randomUUID().toString().replaceAll("-", ""); + } + + /** + * 无参请求返回String类型 + * + * @param life 声明周期实现类 + * @param url 请求URL + */ + public Observable request(LifecycleOwner life, String url) { + return request(life, url, null, (Class) String.class); + } + + /** + * 无参请求返回T类型 + * + * @param life 声明周期实现类 + * @param url 请求URL + * @param type 响应泛型类型 + */ + public Observable request(LifecycleOwner life, String url, Class type) { + return request(life, url, null, type); + } + + /** + * 有参请求返回String类型 + * + * @param life 声明周期实现类 + * @param url 请求URL + * @param param 请求参数 + */ + public Observable request(LifecycleOwner life, String url, RequestParam param) { + return request(life, url, param, (Class) String.class); + } + + /** + * 有参请求返回T类型 + * + * @param life 声明周期实现类 + * @param url 请求URLu + * @param param 请求参数 + * @param type 响应泛型类型 + */ + public Observable request(LifecycleOwner life, String url, RequestParam param, Class type) { + return Observable.create( + subscriber -> { + if (!NetworkUtil.isNetworkEnabled(BaseApplication.i())) { + //无可用网络 + subscriber.onError(new NetErrorException(BaseApplication.i().getString(R.string.network_error_no_net))); + return; + } + Disposable subscribe = checkRequest(url, param) + .as(RxLife.asOnMain(life)) //感知生命周期,并在主线程回调 + .subscribe(response -> + checkResponse(url, response).subscribe(baseResponse -> { + if (type == BaseResponse.class) { + subscriber.onNext((T) baseResponse); + } else if (type == String.class) { + subscriber.onNext((T) baseResponse.getData()); + } else { + //根据类型解析 + subscriber.onNext(JsonUtils.getObject(baseResponse.getData(), type)); + } + }, subscriber::onError), throwable -> subscriber.onError(handleError(throwable))); + BaseApplication.i().addNetDisposable(requestId, subscribe); + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + + /** + * 无参请求返回List类型 + * + * @param life 声明周期实现类 + * @param url 请求URL + * @param type 响应泛型类型 + */ + public Observable> requestList(LifecycleOwner life, String url, Class type) { + return requestList(life, url, null, type); + } + + /** + * 有参请求返回List类型 + * + * @param life 声明周期实现类 + * @param url 请求URL + * @param param 请求参数 + */ + public Observable> requestList(LifecycleOwner life, String url, RequestParam param) { + return requestList(life, url, param, (Class) String.class); + } + + /** + * 有参请求返回List类型 + * + * @param life 声明周期实现类 + * @param url 请求URL + * @param param 请求参数 + * @param type 响应泛型类型 + */ + public Observable> requestList(LifecycleOwner life, String url, RequestParam param, Class type) { + return Observable.>create( + subscriber -> { + if (!NetworkUtil.isNetworkEnabled(BaseApplication.i())) { + //无可用网络 + subscriber.onError(new NetErrorException(BaseApplication.i().getString(R.string.network_error_no_net))); + return; + } + Disposable subscribe = checkRequest(url, param) + .as(RxLife.asOnMain(life)) //感知生命周期,并在主线程回调 + .subscribe(response -> checkResponse(url, response).subscribe( + baseResponse -> subscriber.onNext(JsonUtils.getList(baseResponse.getData(), type)), + subscriber::onError), throwable -> subscriber.onError(handleError(throwable))); + BaseApplication.i().addNetDisposable(requestId, subscribe); + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + + + /** + * 下载文件 + * + * @param life 声明周期实现类 + * @param downloadUrl 文件的下载地址 + * @param localPath 本地保存的文件路径 + * @return 下载response + */ + public Observable requestDownload(LifecycleOwner life, String downloadUrl, String localPath) { + return Observable.create( + subscriber -> { + if (!NetworkUtil.isNetworkEnabled(BaseApplication.i())) { + //无可用网络 + subscriber.onError(new NetErrorException(BaseApplication.i().getString(R.string.network_error_no_net))); + return; + } + Logger.e("文件下载地址:" + downloadUrl); + DownloadResponse downloadResponse = new DownloadResponse(); + //下载失败,处理相关逻辑 + RxHttp.get(downloadUrl) + .asDownload(localPath, progress -> { + Logger.i("文件下载中," + progress); + //下载进度回调,0-100,仅在进度有更新时才会回调,最多回调101次,最后一次回调文件存储路径 + downloadResponse.setProgress(progress); + subscriber.onNext(downloadResponse); + }) + .observeOn(AndroidSchedulers.mainThread()) + .as(RxLife.as(life)) + .subscribe(s -> { + Logger.e("文件下载完成,保存:" + s); + //下载完成,处理相关逻辑 + downloadResponse.setSuccess(true); + subscriber.onNext(downloadResponse); + }, subscriber::onError); + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + + + /** + * 检查响应 + * + * @param apiUrl 接口地址 + * @param response 响应数据 + * @return BaseResponse + */ + private Observable checkResponse(String apiUrl, String response) { + return Observable.create(subscriber -> { + BaseApplication.i().removeNetDisposable(requestId); + Logger.e(String.format("Request-Id:%s\n响应:%s", requestId, response)); + String state = JsonUtils.getString(response, Constants.Net.STATE); + String message = JsonUtils.getString(response, Constants.Net.MESSAGE); + String data; + try { + //防止返回的data为null导致解析json失败 + data = JsonUtils.getString(response, Constants.Net.DATA); + } catch (Exception e) { + data = ""; + } + if (Constants.Net.Status.CODE_SUCCESS.equals(state)) { + subscriber.onNext(new BaseResponse(state, message, data)); + } else { + if (!Api.LOGIN.equals(apiUrl) && ACCOUNT_ERROR_CODE.contains(state)) { + //如果是非登录接口且错误码为定义的这部分,则需要跳转到登录页面 + synchronized (NetworkManage.class) { + //防止多个线程一瞬间进来了,让需要进来的进行排队 + if (count <= 0) { + //Token 过期 跳转登录页面 + toLoginActivity(message); + } + count++; + } + } else { + //接口返回 false 把message、data扔出去 + subscriber.onError(new NetErrorException(state, message, data)); + } + } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + + + /** + * 网络请求之前参数检查,组装公共请求参数,加密等操作 + * + * @param url 接口地址 + * @param param 请求参数 + * @return Observable + */ + @NotNull + private Observable checkRequest(String url, RequestParam param) { + if (!ValidUtils.isValid(param)) { + param = new RequestParam(); + } + //打印请求 + Logger.e(String.format("Request-Id:%s\n%s %s%s\n请求参数:%s", requestId, method, Api.BASE_URL, url, JsonUtils.toJson(param.getParameter()))); + RxHttp rxHttp = buildRequest(url, param); + //添加Header + for (Map.Entry entry : buildHeader().entrySet()) { + rxHttp.addHeader(entry.getKey(), entry.getValue()); + } + return rxHttp.asString(); + } + + /** + * 构建请求 + * + * @param url 接口地址 + * @param param 请求参数 + * @return RxHttp + */ + private RxHttp buildRequest(String url, @NotNull RequestParam param) { + Map fileMap = new HashMap<>(16); + Map> fileListMap = new HashMap<>(16); + Map objectMap = new HashMap<>(16); + + //分离请求参数中的文件 + for (Map.Entry entry : param.getParameter().entrySet()) { + if (entry.getValue() instanceof File) { + //单个文件 + fileMap.put(entry.getKey(), (File) entry.getValue()); + continue; + } + if (entry.getValue() instanceof List && ((List) entry.getValue()).size() > 0) { + if (((List) entry.getValue()).get(0) instanceof File) { + //文件list + fileListMap.put(entry.getKey(), (List) entry.getValue()); + continue; + } + } + + if (entry.getValue() instanceof String) { + //把null转成"" + String value = String.valueOf(entry.getValue()); + if (!ValidUtils.isValid(value) || "null".equalsIgnoreCase(value)) { + value = ""; + } + objectMap.put(entry.getKey(), value); + continue; + } + objectMap.put(entry.getKey(), entry.getValue()); + } + + Map requestParam = new HashMap<>(); + String paramJson = JsonUtils.toJson(objectMap); + /* + *注:该加密方式为:把请求参数key、value以json的格式整体加密,然后再以key、value的形式发送给服务器 + */ + if (!NO_DECRYPTION_LIST.contains(url) && ProfileManager.profile().isSecret()) { + //请求是否需要加密 + requestParam.put(Constants.Net.REQUEST_DATA, AesUtils.getInstance().encrypt(paramJson)); + } else { + requestParam.put(Constants.Net.REQUEST_DATA, paramJson); + } + Logger.i(String.format("加密请求\nRequest-Id:%s\n%s %s%s\n请求参数:%s", + requestId, method, Api.BASE_URL, url, JsonUtils.toJson(requestParam))); + RxHttp rxHttp; + switch (method) { + case POST_JSON: + case PUT_JSON: + case DELETE_JSON: + //JSON格式请求 + if (TextUtils.equals(method, POST_JSON)) { + rxHttp = RxHttp.postJson(url); + } else if (TextUtils.equals(method, DELETE_JSON)) { + rxHttp = RxHttp.deleteJson(url); + } else { + rxHttp = RxHttp.putJson(url); + } + if (isUpLoadFile(url)) { + //上传文件传参特殊 + ((RxHttpJsonParam) rxHttp).addAll(objectMap); + } else { + ((RxHttpJsonParam) rxHttp).addAll(requestParam); + } + + //添加文件请求参数 + if (!fileMap.isEmpty()) { + for (Map.Entry entry : fileMap.entrySet()) { + ((RxHttpJsonParam) rxHttp).add(entry.getKey(), entry.getValue()); + } + } + if (!fileListMap.isEmpty()) { + for (Map.Entry> entry : fileListMap.entrySet()) { + ((RxHttpJsonParam) rxHttp).add(entry.getKey(), entry.getValue()); + } + } + break; + case POST_FORM: + case PUT_FORM: + case DELETE_FORM: + //POST格式请求 + if (TextUtils.equals(method, POST_FORM)) { + rxHttp = RxHttp.postForm(url); + } else if (TextUtils.equals(method, DELETE_FORM)) { + rxHttp = RxHttp.deleteForm(url); + } else { + rxHttp = RxHttp.putForm(url); + } + if (isUpLoadFile(url)) { + //上传文件传参特殊 + ((RxHttpFormParam) rxHttp).addAll(objectMap); + } else { + ((RxHttpFormParam) rxHttp).addAll(requestParam); + }//添加文件请求参数 + if (!fileMap.isEmpty()) { + for (Map.Entry entry : fileMap.entrySet()) { + ((RxHttpFormParam) rxHttp).addFile(entry.getKey(), entry.getValue()); + } + } + if (!fileListMap.isEmpty()) { + for (Map.Entry> entry : fileListMap.entrySet()) { + ((RxHttpFormParam) rxHttp).addFile(entry.getKey(), entry.getValue()); + } + } + break; + case GET: + default: + rxHttp = RxHttp.get(url); + ((RxHttpNoBodyParam) rxHttp).addAll(requestParam); + //添加文件请求参数 + if (!fileMap.isEmpty()) { + for (Map.Entry entry : fileMap.entrySet()) { + ((RxHttpNoBodyParam) rxHttp).add(entry.getKey(), entry.getValue()); + } + } + if (!fileListMap.isEmpty()) { + for (Map.Entry> entry : fileListMap.entrySet()) { + ((RxHttpNoBodyParam) rxHttp).add(entry.getKey(), entry.getValue()); + } + } + break; + } + return rxHttp; + } + + /** + * 构建请求头 + * + * @return 请求头Map + */ + @NotNull + private Map buildHeader() { + String deviceId = BaseApplication.i().getDeviceId(); + String token = BaseApplication.i().getLoginToken(); + Map header = new HashMap<>(); + header.put(Constants.Net.HEADER_CHANNEL, Constants.Common.CHANEL); + header.put(Constants.Net.HEADER_API_VERSION, Constants.Common.API_VERSION); + header.put(Constants.Net.HEADER_DEVICE_ID, ValidUtils.isValid(deviceId) ? deviceId : ""); + header.put(Constants.Net.HEADER_TOKEN, ValidUtils.isValid(token) ? token : ""); + header.put(Constants.Net.HEADER_APP_VERSION, Utils.getVersionName()); + header.put(Constants.Net.HEADER_IP, NetworkUtil.getIpAddress()); + header.put(Constants.Net.REQUEST_ID, requestId); + Logger.i("请求Header:\n" + JsonUtils.toJson(header)); + return header; + } + + /** + * 检查是否上传文件 + * + * @param url url + * @return boolean + */ + @Contract(value = "null -> false", pure = true) + private boolean isUpLoadFile(String url) { + return Api.UPLOAD_FILE.equals(url); + } + + + /** + * 处理网络请求中的异常 + * + * @param t Throwable + * @return NetErrorException + */ + @Contract("_ -> new") + @NotNull + private NetErrorException handleError(@NotNull Throwable t) { + BaseApplication.i().removeNetDisposable(requestId); + String errorMessage = null; + String loggerMessage = ""; + if (t instanceof SocketTimeoutException) { + errorMessage = BaseApplication.i().getString(R.string.request_time_out); + loggerMessage = errorMessage; + } else if (t instanceof HttpStatusCodeException) { + HttpStatusCodeException codeException = (HttpStatusCodeException) t; + try { + errorMessage = JsonUtils.getString(codeException.getResult(), Constants.Net.MESSAGE); + } catch (Exception e) { + errorMessage = BaseApplication.i().getString(R.string.network_error_server_error); + } + loggerMessage = String.format("%s %s", codeException.getStatusCode(), errorMessage); + } else { + loggerMessage = t.getMessage(); + } + if (!ValidUtils.isValid(errorMessage)) { + errorMessage = BaseApplication.i().getString(R.string.network_error_server_error); + } + Logger.e(String.format("Request-Id:%s\n请求报错:%s", requestId, loggerMessage)); + return new NetErrorException(errorMessage); + } + + /** + * 跳转登录页 + * + * @param errorMessage 错误信息 + */ + private void toLoginActivity(String errorMessage) { + Observable.create( + subscriber -> { + //检查是否存在未完成的请求,有就取消 + for (Map.Entry entry : BaseApplication.i().getNetDisposables().entrySet()) { + if (!entry.getValue().isDisposed()) { + entry.getValue().dispose(); + } + } + ToastUtils.show(errorMessage); + //延时1秒跳转到登录页,最大限度的,避免由于网络延时,带来的token过期退出到登录页的异常 + new Handler().postDelayed(() -> { + ActivityToActivity.toLoginActivity(BaseApplication.i()); + BaseApplication.i().loginOutClean(); + }, 500); + + }) + .subscribeOn(AndroidSchedulers.mainThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(); + } + + } +} diff --git a/common_base/src/main/java/com/wss/common/net/RequestParam.java b/common_base/src/main/java/com/wss/common/net/RequestParam.java deleted file mode 100644 index 6f2bf06..0000000 --- a/common_base/src/main/java/com/wss/common/net/RequestParam.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.wss.common.net; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.LinkedHashMap; -import java.util.Map; - - -/** - * Describe:请求参数封装类 - * Created by 吴天强 on 2017/9/19. - */ -public class RequestParam { - - private Map params; - - public RequestParam() { - params = new LinkedHashMap<>(); - } - - /** - * 普通文本参数 - * - * @param key key - * @param value 值 - */ - public void addParameter(String key, Object value) { - if (this.params == null) { - params = new LinkedHashMap<>(); - } - params.put(key, value); - } - - - public void addParameter(String key, int value) { - addParameter(key, String.valueOf(value)); - } - - public void addParameter(String key, long value) { - addParameter(key, String.valueOf(value)); - } - - public void addParameter(String key, float value) { - addParameter(key, String.valueOf(value)); - } - - public void addParameter(String key, double value) { - addParameter(key, String.valueOf(value)); - } - - /** - * 获取请求参数 - * - * @return Map - */ - public Map getParameter() { - if (null == params) { - params = new LinkedHashMap<>(); - } - return params; - } - - - /** - * 请求参数转Json - * - * @return String - */ - String toJson() { - JSONObject json = new JSONObject(); - for (Map.Entry entry : params.entrySet()) { - try { - json.put(entry.getKey(), entry.getValue()); - } catch (JSONException e) { - e.printStackTrace(); - } - } - return json.toString(); - } - - @Override - public String toString() { - StringBuilder stringBuffer = new StringBuilder(); - for (Map.Entry entry : params.entrySet()) { - stringBuffer.append(entry.getKey()); - stringBuffer.append(":"); - stringBuffer.append(entry.getValue()); - stringBuffer.append("\t"); - } - return stringBuffer.toString(); - } - -} diff --git a/common_base/src/main/java/com/wss/common/net/callback/OnResultCallBack.java b/common_base/src/main/java/com/wss/common/net/callback/OnResultCallBack.java deleted file mode 100644 index 468b6a4..0000000 --- a/common_base/src/main/java/com/wss/common/net/callback/OnResultCallBack.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.wss.common.net.callback; - - -import com.tamic.novate.Throwable; -import com.tamic.novate.callback.RxGenericsCallback; - -import java.io.IOException; - -import okhttp3.Call; -import okhttp3.ResponseBody; - -/** - * Describe:网络返回基类 - * Created by 吴天强 on 2017/9/26. - */ - -public abstract class OnResultCallBack extends RxGenericsCallback { - protected boolean success; - - @Override - public void onError(Object tag, Throwable e) { - e.printStackTrace(); - onFailure(tag, e); - } - - - @Override - public void onCancel(Object tag, Throwable e) { - } - - @Override - public void onFailure(Call call, IOException e) { - onFailure(tag, e); - } - - @Override - public void onNext(Object tag, int code, String message, T response) { - onSuccess(success, code, msg, tag, response); - } - - @Override - public void onCompleted(Object tag) { - super.onCompleted(tag); - onCompleted(); - } - - public abstract void onSuccess(boolean success, int code, String msg, Object tag, T response); - - public abstract void onFailure(Object tag, Exception e); - - public abstract void onCompleted(); - - -} diff --git a/common_base/src/main/java/com/wss/common/net/callback/OnResultListCallBack.java b/common_base/src/main/java/com/wss/common/net/callback/OnResultListCallBack.java deleted file mode 100644 index a113abb..0000000 --- a/common_base/src/main/java/com/wss/common/net/callback/OnResultListCallBack.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.wss.common.net.callback; - -import com.alibaba.fastjson.JSON; -import com.google.gson.Gson; -import com.orhanobut.logger.Logger; -import com.wss.common.net.NetConfig; - -import org.json.JSONObject; - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; - -import okhttp3.ResponseBody; - -/** - * Describe:返回数组类型数据 - * Created by 吴天强 on 2017/9/28. - */ - -public abstract class OnResultListCallBack extends OnResultCallBack { - - private Type collectionType; - - @Override - public T onHandleResponse(ResponseBody response) throws Exception { - if (collectionType == null) { - collectionType = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; - } - return transform(new String(response.bytes()), null); - } - - public T transform(String response, final Class classOfT) throws ClassCastException { - Logger.e(JSON.toJSONString(response)); - try { - JSONObject jsonObject = new JSONObject(response); - code = jsonObject.optInt(NetConfig.Code.CODE); - msg = jsonObject.optString(NetConfig.Code.MSG); - success = jsonObject.optBoolean(NetConfig.Code.SUCCESS); - dataStr = jsonObject.opt(NetConfig.Code.MODEL).toString(); - dataResponse = new Gson().fromJson(dataStr, collectionType); - } catch (Exception e) { - e.printStackTrace(); - } - - return dataResponse; - } -} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/net/callback/OnResultObjectCallBack.java b/common_base/src/main/java/com/wss/common/net/callback/OnResultObjectCallBack.java deleted file mode 100644 index 151c250..0000000 --- a/common_base/src/main/java/com/wss/common/net/callback/OnResultObjectCallBack.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.wss.common.net.callback; - -import com.google.gson.Gson; -import com.orhanobut.logger.Logger; -import com.wss.common.net.NetConfig; - -import org.json.JSONObject; - -import java.lang.reflect.ParameterizedType; - -import okhttp3.ResponseBody; - -/** - * Describe:返回对象类型数据 - * Created by 吴天强 on 2017/9/28. - */ - -public abstract class OnResultObjectCallBack extends OnResultCallBack { - - @Override - public T onHandleResponse(ResponseBody response) throws Exception { - Class entityClass = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; - return transform(new String(response.bytes()), entityClass); - } - - public T transform(String response, final Class classOfT) throws ClassCastException { - Logger.e(response); - try { - JSONObject jsonObject = new JSONObject(response); - - code = jsonObject.optInt(NetConfig.Code.CODE); - msg = jsonObject.optString(NetConfig.Code.MSG); - success = jsonObject.optBoolean(NetConfig.Code.SUCCESS); - dataStr = jsonObject.opt(NetConfig.Code.MODEL).toString(); - dataResponse = (T) new Gson().fromJson(dataStr, classOfT); - } catch (Exception e) { - e.printStackTrace(); - } - return dataResponse; - } -} - diff --git a/common_base/src/main/java/com/wss/common/net/callback/OnResultStringCallBack.java b/common_base/src/main/java/com/wss/common/net/callback/OnResultStringCallBack.java deleted file mode 100644 index eec4ce6..0000000 --- a/common_base/src/main/java/com/wss/common/net/callback/OnResultStringCallBack.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.wss.common.net.callback; - -import com.alibaba.fastjson.JSON; -import com.orhanobut.logger.Logger; -import com.wss.common.net.NetConfig; - -import org.json.JSONObject; - -import okhttp3.ResponseBody; - -/** - * Describe:String类型数据解析 - * Created by 吴天强 on 2017/9/28. - */ - -public abstract class OnResultStringCallBack extends OnResultCallBack { - - @Override - public String onHandleResponse(ResponseBody response) throws Exception { - return transform(new String(response.bytes())); - } - - private String transform(String response) { - Logger.e(JSON.toJSONString(response)); - try { - JSONObject jsonObject = new JSONObject(response); - code = jsonObject.optInt(NetConfig.Code.CODE); - msg = jsonObject.optString(NetConfig.Code.MSG); - success = jsonObject.optBoolean(NetConfig.Code.SUCCESS); - dataStr = jsonObject.opt(NetConfig.Code.MODEL).toString(); - dataResponse = dataStr; - } catch (Exception e) { - e.printStackTrace(); - } - return dataResponse; - } - -} diff --git a/common_base/src/main/java/com/wss/common/net/request/RequestParam.java b/common_base/src/main/java/com/wss/common/net/request/RequestParam.java new file mode 100644 index 0000000..5d0e5b4 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/net/request/RequestParam.java @@ -0,0 +1,133 @@ +package com.wss.common.net.request; + +import org.jetbrains.annotations.NotNull; + +import java.util.LinkedHashMap; +import java.util.Map; + + +/** + * Describe:请求参数封装类 + * Created by 吴天强 on 2017/9/19. + */ +public class RequestParam { + + /** + * 存放请求参数的Map + */ + private Map params; + + public RequestParam() { + params = new LinkedHashMap<>(); + } + + /** + * 添加请求参数 + * + * @param key key + * @param value 值 + */ + private void buildParameter(String key, Object value) { + if (this.params == null) { + params = new LinkedHashMap<>(); + } + params.put(key, value); + } + + /** + * 添加普通文本 + * + * @param key key + * @param value 值 + */ + public void addParameter(String key, String value) { + buildParameter(key, value); + } + + /** + * 添加int类型 + * + * @param key key + * @param value 值 + */ + public void addParameter(String key, Integer value) { + buildParameter(key, value); + } + + /** + * 添加long类型 + * + * @param key key + * @param value 值 + */ + public void addParameter(String key, Long value) { + buildParameter(key, value); + } + + /** + * 添加float类型 + * + * @param key key + * @param value 值 + */ + public void addParameter(String key, Float value) { + buildParameter(key, value); + } + + /** + * 添加double类型 + * + * @param key key + * @param value 值 + */ + public void addParameter(String key, Double value) { + buildParameter(key, value); + } + + /** + * 添加boolean类型 + * + * @param key key + * @param value 值 + */ + public void addParameter(String key, Boolean value) { + buildParameter(key, value); + } + + /** + * 添加Object类型 + * + * @param key key + * @param value 值 + */ + public void addParameter(String key, Object value) { + buildParameter(key, value); + } + + /** + * 获取请求参数 + * + * @return Map + */ + public Map getParameter() { + if (null == params) { + params = new LinkedHashMap<>(); + } + return params; + } + + + @NotNull + @Override + public String toString() { + StringBuilder stringBuffer = new StringBuilder(); + for (Map.Entry entry : params.entrySet()) { + stringBuffer.append(entry.getKey()); + stringBuffer.append(":"); + stringBuffer.append(entry.getValue()); + stringBuffer.append("\t"); + } + return stringBuffer.toString(); + } + +} diff --git a/common_base/src/main/java/com/wss/common/net/response/BaseResponse.java b/common_base/src/main/java/com/wss/common/net/response/BaseResponse.java new file mode 100644 index 0000000..08f2acd --- /dev/null +++ b/common_base/src/main/java/com/wss/common/net/response/BaseResponse.java @@ -0,0 +1,34 @@ +package com.wss.common.net.response; + + +import com.wss.common.base.bean.BaseBean; + +import lombok.Getter; +import lombok.Setter; + +/** + * Describe:响应基类 + * Created by 吴天强 on 2020/5/6. + */ +@Getter +@Setter +public class BaseResponse extends BaseBean { + /** + * 响应码 + */ + private String code; + /** + * 响应message + */ + private String msg; + /** + * 响应数据 + */ + private String data; + + public BaseResponse(String code, String msg, String data) { + this.code = code; + this.msg = msg; + this.data = data; + } +} diff --git a/common_base/src/main/java/com/wss/common/net/response/DownloadResponse.java b/common_base/src/main/java/com/wss/common/net/response/DownloadResponse.java new file mode 100644 index 0000000..2ad97dc --- /dev/null +++ b/common_base/src/main/java/com/wss/common/net/response/DownloadResponse.java @@ -0,0 +1,25 @@ +package com.wss.common.net.response; + + +import com.wss.common.base.bean.BaseBean; + +import lombok.Getter; +import lombok.Setter; +import rxhttp.wrapper.entity.Progress; + +/** + * Describe:下载文件响应 + * Created by 吴天强 on 2021/5/10. + */ +@Getter +@Setter +public class DownloadResponse extends BaseBean { + /** + * 下载文件进度对象 + */ + private Progress progress; + /** + * 是否下载完成 + */ + private boolean success; +} diff --git a/common_base/src/main/java/com/wss/common/profile/IProfile.java b/common_base/src/main/java/com/wss/common/profile/IProfile.java new file mode 100644 index 0000000..4e9839d --- /dev/null +++ b/common_base/src/main/java/com/wss/common/profile/IProfile.java @@ -0,0 +1,31 @@ +package com.wss.common.profile; + +/** + * Describe:编译环境相关的配置 + * 该类定义 由APP模块的具体实现类创建不同环境的配置 + * Created by 吴天强 on 2019/5/23. + */ +public interface IProfile { + + /** + * 服务地址 + * + * @return API地址 + */ + String getServiceBase(); + + /** + * 请求是否加密 + * + * @return boolean + */ + boolean isSecret(); + + /** + * 返回Aes加解密key + * + * @return key + */ + String getAesSecretKey(); + +} diff --git a/common_base/src/main/java/com/wss/common/profile/IProfileFactory.java b/common_base/src/main/java/com/wss/common/profile/IProfileFactory.java new file mode 100644 index 0000000..af8a080 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/profile/IProfileFactory.java @@ -0,0 +1,15 @@ +package com.wss.common.profile; + +/** + * Describe:环境配置工厂接口 + * Created by 吴天强 on 2019/5/23. + */ +public interface IProfileFactory { + + /** + * 创建配置文件 + * + * @return 配置文件 + */ + IProfile createProfile(); +} diff --git a/common_base/src/main/java/com/wss/common/profile/ProfileManager.java b/common_base/src/main/java/com/wss/common/profile/ProfileManager.java new file mode 100644 index 0000000..65895bb --- /dev/null +++ b/common_base/src/main/java/com/wss/common/profile/ProfileManager.java @@ -0,0 +1,41 @@ +package com.wss.common.profile; + +/** + * Describe:编译环境相关的配置 管理类 + * Created by 吴天强 on 2019/5/23. + */ +public class ProfileManager { + + private IProfileFactory mFactory; + private IProfile mProfile; + public static final ProfileManager inst; + + public void factory(IProfileFactory factory) { + this.mFactory = factory; + } + + public static IProfile profile() { + return inst.getProfile(); + } + + private IProfile getProfile() { + if (this.mProfile == null) { + this.mProfile = this.mFactory.createProfile(); + } + return this.mProfile; + } + + private ProfileManager() { + } + + static { + inst = Holder.holder; + } + + private static final class Holder { + private static final ProfileManager holder = new ProfileManager(); + + private Holder() { + } + } +} diff --git a/common_base/src/main/java/com/wss/common/secret/AesUtils.java b/common_base/src/main/java/com/wss/common/secret/AesUtils.java new file mode 100644 index 0000000..bad264b --- /dev/null +++ b/common_base/src/main/java/com/wss/common/secret/AesUtils.java @@ -0,0 +1,104 @@ +package com.wss.common.secret;//package com.xincheng.common.secret; + + +import com.wss.common.profile.ProfileManager; + +import org.bouncycastle.util.encoders.Base64; + +import java.net.URLEncoder; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + + +/** + * Describe:AES 加解密操作 + * Created by 吴天强 on 2018-08-03 17:47 + **/ +public class AesUtils { + + private static final String ALGORITHM = "AES/ECB/PKCS5Padding"; + private static final String KEY = ProfileManager.profile().getAesSecretKey(); + + private static AesUtils aesUtils; + + private AesUtils() { + } + + public static AesUtils getInstance() { + if (aesUtils == null) { + synchronized (AesUtils.class) { + if (aesUtils == null) { + aesUtils = new AesUtils(); + } + } + } + return aesUtils; + } + + /** + * 获取秘钥方法 + */ + private byte[] getKey() { + KeyGenerator kg; + try { + kg = KeyGenerator.getInstance("AES"); + kg.init(192); + SecretKey sk = kg.generateKey(); + byte[] b = sk.getEncoded(); + System.out.println("KEY---------" + new String(Base64.encode(b))); + return b; + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + return null; + } + + /** + * AES加密方法 + * + * @param str 需要加密的字符串 + * @return 加密后数据 + */ + public String encrypt(String str) { + byte[] result; + try { + Cipher cipher = Cipher.getInstance(ALGORITHM); + //生成加密解密需要的Key + SecretKeySpec keySpec = new SecretKeySpec(Base64.decode(KEY.getBytes()), "AES"); + cipher.init(Cipher.ENCRYPT_MODE, keySpec); + result = cipher.doFinal(str.getBytes("UTF-8")); + return URLEncoder.encode(new String(Base64.encode(result)), "UTF-8"); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + + /** + * AES解密方法 + * + * @param str 需要加密的字符串 + * @return 解密后数据 + */ + public String decrypt(String str) { + String result = null; + try { + Cipher cipher = Cipher.getInstance(ALGORITHM); + SecretKeySpec keySpec = new SecretKeySpec(Base64.decode(KEY.getBytes()), "AES"); + cipher.init(Cipher.DECRYPT_MODE, keySpec); + byte[] decoded = cipher.doFinal(Base64.decode(str.getBytes())); + result = new String(decoded, "UTF-8"); + } catch (Exception e) { + e.printStackTrace(); + } + return result; + } + +} + + diff --git a/common_base/src/main/java/com/wss/common/secret/MD5.java b/common_base/src/main/java/com/wss/common/secret/MD5.java new file mode 100644 index 0000000..e8e9abf --- /dev/null +++ b/common_base/src/main/java/com/wss/common/secret/MD5.java @@ -0,0 +1,64 @@ +package com.wss.common.secret; + + +import org.jetbrains.annotations.NotNull; + +import java.security.MessageDigest; + +/** + * Describe:MD5加密 + * Created by 吴天强 on 2017/10/9. + */ +public class MD5 { + /** + * 生成md5 + * + * @param message 加密字段 + * @return String + */ + @NotNull + public static String getMD5(String message) { + String md5str = ""; + try { + // 1 创建一个提供信息摘要算法的对象,初始化为md5算法对象 + MessageDigest md = MessageDigest.getInstance("MD5"); + // 2 将消息变成byte数组 + byte[] input = message.getBytes(); + // 3 计算后获得字节数组,这就是那128位了 + byte[] buff = md.digest(input); + // 4 把数组每一字节(一个字节占八位)换成16进制连成md5字符串 + md5str = bytesToHex(buff); + + } catch (Exception e) { + e.printStackTrace(); + } + return md5str.toLowerCase(); + } + + /** + * 二进制转十六进制 + * + * @param bytes bytes + * @return String + */ + @NotNull + private static String bytesToHex(@NotNull byte[] bytes) { + StringBuilder md5str = new StringBuilder(); + // 把数组每一字节换成16进制连成md5字符串 + int digital; + for (byte aByte : bytes) { + digital = aByte; + + if (digital < 0) { + digital += 256; + } + if (digital < 16) { + md5str.append("0"); + } + md5str.append(Integer.toHexString(digital)); + } + return md5str.toString().toUpperCase(); + } + + +} diff --git a/common_base/src/main/java/com/wss/common/secret/ParseSystemUtil.java b/common_base/src/main/java/com/wss/common/secret/ParseSystemUtil.java new file mode 100644 index 0000000..bfc5d44 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/secret/ParseSystemUtil.java @@ -0,0 +1,50 @@ +package com.wss.common.secret; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Describe:16进制与二进制转换类 + * Created by 吴天强 on 2018-08-03 16:31 + **/ +public class ParseSystemUtil { + /** + * 将二进制转换成16进制 + * + * @param buf 数据 + * @return String + */ + @NotNull + public static String parseByte2HexStr(@NotNull byte[] buf) { + StringBuilder sb = new StringBuilder(); + for (byte b : buf) { + String hex = Integer.toHexString(b & 0xFF); + if (hex.length() == 1) { + hex = '0' + hex; + } + sb.append(hex.toUpperCase()); + } + return sb.toString(); + } + + /** + * 将16进制转换为二进制 + * + * @param hexStr 数据 + * @return byte[] + */ + @Nullable + public static byte[] parseHexStr2Byte(@NotNull String hexStr) { + if (hexStr.length() < 1) + return null; + byte[] result = new byte[hexStr.length() / 2]; + for (int i = 0; i < hexStr.length() / 2; i++) { + int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16); + int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16); + result[i] = (byte) (high * 16 + low); + } + return result; + } +} + + diff --git a/common_base/src/main/java/com/wss/common/utils/ARouterUtils.java b/common_base/src/main/java/com/wss/common/utils/ARouterUtils.java new file mode 100644 index 0000000..ff5eebb --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/ARouterUtils.java @@ -0,0 +1,36 @@ +package com.wss.common.utils; + +import com.alibaba.android.arouter.launcher.ARouter; +import com.wss.common.base.BaseActivity; +import com.wss.common.base.BaseFragment; + +/** + * Describe:ARouter帮助类 + * Created by 吴天强 on 2018/11/13. + */ +public class ARouterUtils { + + /** + * 根据path返回Fragment + * + * @param path path + * @return fragment + */ + public static BaseFragment getFragment(String path) { + return (BaseFragment) ARouter.getInstance() + .build(path) + .navigation(); + } + + /** + * 根据path返回Activity + * + * @param path path + * @return Activity + */ + public static BaseActivity getActivity(String path) { + return (BaseActivity) ARouter.getInstance() + .build(path) + .navigation(); + } +} diff --git a/common_base/src/main/java/com/wss/common/utils/ActivityToActivity.java b/common_base/src/main/java/com/wss/common/utils/ActivityToActivity.java deleted file mode 100644 index dc8a074..0000000 --- a/common_base/src/main/java/com/wss/common/utils/ActivityToActivity.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.wss.common.utils; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; - -import com.wss.common.base.bean.BaseBean; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -/** - * Describe:Activity跳转 - * Created by 吴天强 on 2018/10/22. - */ - -public class ActivityToActivity { - - - public static void toActivity(Context activity, Class clazz) { - toActivity(activity, clazz, null); - } - - public static void toActivity(Context activity, Class clazz, - Map params) { - Intent intent = new Intent(activity, clazz); - if (params != null) { - for (Map.Entry entry : params.entrySet()) { - String key = entry.getKey(); - Object value = params.get(key); - if (value instanceof String) { - intent.putExtra(key, (String) value); - } else if (value instanceof Boolean) { - intent.putExtra(key, (boolean) value); - } else if (value instanceof Integer) { - intent.putExtra(key, (int) value); - } else if (value instanceof Float) { - intent.putExtra(key, (float) value); - } else if (value instanceof Double) { - intent.putExtra(key, (double) value); - } else if (value instanceof Long) { - intent.putExtra(key, (long) value); - } else if (value instanceof Short) { - intent.putExtra(key, (short) value); - } else if (value instanceof BaseBean) { - intent.putExtra(key, (BaseBean) value); - } else if (value instanceof ArrayList) { - intent.putExtra(key, (ArrayList) value); - } else if (value instanceof HashMap) { - intent.putExtra(key, (HashMap) value); - } - } - } - activity.startActivity(intent); - } - - -} diff --git a/common_base/src/main/java/com/wss/common/utils/BasicThreadFactory.java b/common_base/src/main/java/com/wss/common/utils/BasicThreadFactory.java new file mode 100644 index 0000000..0c0302b --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/BasicThreadFactory.java @@ -0,0 +1,14 @@ +package com.wss.common.utils; + +import java.util.concurrent.ThreadFactory; + +/** + * Describe: + * Created by 吴天强 on 2020/8/27. + */ +public class BasicThreadFactory implements ThreadFactory { + @Override + public Thread newThread(Runnable r) { + return null; + } +} diff --git a/common_base/src/main/java/com/wss/common/utils/BeanCopyUtils.java b/common_base/src/main/java/com/wss/common/utils/BeanCopyUtils.java new file mode 100644 index 0000000..7cbaf2f --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/BeanCopyUtils.java @@ -0,0 +1,48 @@ +package com.wss.common.utils; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +/** + * Describe:实体类Copy + * Created by 吴天强 on 2018/11/5. + */ +public class BeanCopyUtils { + + @SuppressWarnings("unchecked") + public static T copy(T src) { + ByteArrayOutputStream memoryBuffer = new ByteArrayOutputStream(); + ObjectOutputStream out = null; + ObjectInputStream in = null; + T dist = null; + try { + out = new ObjectOutputStream(memoryBuffer); + out.writeObject(src); + out.flush(); + in = new ObjectInputStream(new ByteArrayInputStream(memoryBuffer.toByteArray())); + dist = (T) in.readObject(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (out != null) + try { + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + if (in != null) + try { + in.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return dist; + } + + + +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/utils/CacheUtils.java b/common_base/src/main/java/com/wss/common/utils/CacheUtils.java index 426841e..bfe02b3 100644 --- a/common_base/src/main/java/com/wss/common/utils/CacheUtils.java +++ b/common_base/src/main/java/com/wss/common/utils/CacheUtils.java @@ -8,6 +8,9 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.json.JSONArray; import org.json.JSONObject; @@ -40,35 +43,40 @@ * Describe:缓存工具类。。。能缓存普通的字符串、JsonObject、JsonArray、 Bitmap、Drawable、序列化的java对象,和 byte数据。 * Created by 吴天强 on 2017/10/9. */ - public class CacheUtils { - public static final int TIME_MINUTE = 60; - public static final int TIME_HOUR = 60 * 60; - public static final int TIME_DAY = TIME_HOUR * 24; + private static final int TIME_MINUTE = 60; + private static final int TIME_HOUR = 60 * TIME_MINUTE; + private static final int TIME_DAY = TIME_HOUR * 24; + private static final int MAX_TIME_OUT = 90 * TIME_DAY;//最大缓存90天 private static final int MAX_SIZE = 1000 * 1000 * 50; // 50 mb private static final int MAX_COUNT = Integer.MAX_VALUE; // 不限制存放数据的数量 private static Map mInstanceMap = new HashMap<>(); private ACacheManager mCache; + @NotNull public static CacheUtils get(Context ctx) { return get(ctx, CacheUtils.class.getSimpleName()); } - public static CacheUtils get(Context ctx, String cacheName) { + @NotNull + public static CacheUtils get(@NotNull Context ctx, String cacheName) { File f = new File(ctx.getCacheDir(), cacheName); return get(f, MAX_SIZE, MAX_COUNT); } + @NotNull public static CacheUtils get(File cacheDir) { return get(cacheDir, MAX_SIZE, MAX_COUNT); } - public static CacheUtils get(Context ctx, long max_zise, int max_count) { + @NotNull + public static CacheUtils get(@NotNull Context ctx, long max_zise, int max_count) { File f = new File(ctx.getCacheDir(), CacheUtils.class.getSimpleName()); return get(f, max_zise, max_count); } - public static CacheUtils get(File cacheDir, long max_zise, int max_count) { + @NotNull + public static CacheUtils get(@NotNull File cacheDir, long max_zise, int max_count) { CacheUtils manager = mInstanceMap.get(cacheDir.getAbsoluteFile() + myPid()); if (manager == null) { manager = new CacheUtils(cacheDir, max_zise, max_count); @@ -77,11 +85,12 @@ public static CacheUtils get(File cacheDir, long max_zise, int max_count) { return manager; } + @NotNull private static String myPid() { return "_" + android.os.Process.myPid(); } - private CacheUtils(File cacheDir, long max_size, int max_count) { + private CacheUtils(@NotNull File cacheDir, long max_size, int max_count) { if (!cacheDir.exists() && !cacheDir.mkdirs()) { throw new RuntimeException("can't make dirs in " + cacheDir.getAbsolutePath()); @@ -117,6 +126,16 @@ public void close() throws IOException { // ============ String数据 读写 ============== // ======================================= + /** + * 保存 String数据 到 缓存中 + * + * @param key 保存的key + * @param value 保存的String数据 + */ + public void put(String key, boolean value) { + put(key, String.valueOf(value)); + } + /** * 保存 String数据 到 缓存中 * @@ -124,11 +143,23 @@ public void close() throws IOException { * @param value 保存的String数据 */ public void put(String key, String value) { + put(key, value, MAX_TIME_OUT); + } + + /** + * 保存 String数据 到 缓存中 + * + * @param key 保存的key + * @param value 保存的String数据 + * @param saveTime 保存的时间,单位:秒 + */ + public void put(String key, String value, int saveTime) { + String values = Utils.newStringWithDateInfo(saveTime, value); File file = mCache.newFile(key); BufferedWriter out = null; try { out = new BufferedWriter(new FileWriter(file), 1024); - out.write(value); + out.write(values); } catch (IOException e) { e.printStackTrace(); } finally { @@ -144,17 +175,6 @@ public void put(String key, String value) { } } - /** - * 保存 String数据 到 缓存中 - * - * @param key 保存的key - * @param value 保存的String数据 - * @param saveTime 保存的时间,单位:秒 - */ - public void put(String key, String value, int saveTime) { - put(key, Utils.newStringWithDateInfo(saveTime, value)); - } - /** * 读取 String数据 * @@ -196,6 +216,9 @@ public String getAsString(String key) { } } + public boolean getAsBoolean(String key) { + return Boolean.parseBoolean(getAsString(key)); + } // ======================================= // ============= JSONObject 数据 读写 ============== // ======================================= @@ -206,7 +229,7 @@ public String getAsString(String key) { * @param key 保存的key * @param value 保存的JSON数据 */ - public void put(String key, JSONObject value) { + public void put(String key, @NotNull JSONObject value) { put(key, value.toString()); } @@ -217,7 +240,7 @@ public void put(String key, JSONObject value) { * @param value 保存的JSONObject数据 * @param saveTime 保存的时间,单位:秒 */ - public void put(String key, JSONObject value, int saveTime) { + public void put(String key, @NotNull JSONObject value, int saveTime) { put(key, value.toString(), saveTime); } @@ -248,7 +271,7 @@ public JSONObject getAsJSONObject(String key) { * @param key 保存的key * @param value 保存的JSONArray数据 */ - public void put(String key, JSONArray value) { + public void put(String key, @NotNull JSONArray value) { put(key, value.toString()); } @@ -259,7 +282,7 @@ public void put(String key, JSONArray value) { * @param value 保存的JSONArray数据 * @param saveTime 保存的时间,单位:秒 */ - public void put(String key, JSONArray value, int saveTime) { + public void put(String key, @NotNull JSONArray value, int saveTime) { put(key, value.toString(), saveTime); } @@ -388,13 +411,13 @@ public byte[] getAsBinary(String key) { // ======================================= /** - * 保存 Serializable数据 到 缓存中 + * 保存 Serializable数据 到 缓存中 默认保存90天 * * @param key 保存的key * @param value 保存的value */ public void put(String key, Serializable value) { - put(key, value, -1); + put(key, value, MAX_TIME_OUT); } /** @@ -581,8 +604,7 @@ public class ACacheManager { private final AtomicInteger cacheCount; private final long sizeLimit; private final int countLimit; - private final Map lastUsageDates = Collections - .synchronizedMap(new HashMap()); + private final Map lastUsageDates = Collections.synchronizedMap(new HashMap<>()); protected File cacheDir; private ACacheManager(File cacheDir, long sizeLimit, int countLimit) { @@ -598,22 +620,19 @@ private ACacheManager(File cacheDir, long sizeLimit, int countLimit) { * 计算 cacheSize和cacheCount */ private void calculateCacheSizeAndCacheCount() { - new Thread(new Runnable() { - @Override - public void run() { - int size = 0; - int count = 0; - File[] cachedFiles = cacheDir.listFiles(); - if (cachedFiles != null) { - for (File cachedFile : cachedFiles) { - size += calculateSize(cachedFile); - count += 1; - lastUsageDates.put(cachedFile, - cachedFile.lastModified()); - } - cacheSize.set(size); - cacheCount.set(count); + new Thread(() -> { + int size = 0; + int count = 0; + File[] cachedFiles = cacheDir.listFiles(); + if (cachedFiles != null) { + for (File cachedFile : cachedFiles) { + size += calculateSize(cachedFile); + count += 1; + lastUsageDates.put(cachedFile, + cachedFile.lastModified()); } + cacheSize.set(size); + cacheCount.set(count); } }).start(); } @@ -636,21 +655,24 @@ private void put(File file) { } cacheSize.addAndGet(valueSize); - Long currentTime = System.currentTimeMillis(); + long currentTime = System.currentTimeMillis(); file.setLastModified(currentTime); lastUsageDates.put(file, currentTime); } + @NotNull private File get(String key) { File file = newFile(key); - Long currentTime = System.currentTimeMillis(); + long currentTime = System.currentTimeMillis(); file.setLastModified(currentTime); lastUsageDates.put(file, currentTime); return file; } - private File newFile(String key) { + @NotNull + @Contract("_ -> new") + private File newFile(@NotNull String key) { return new File(cacheDir, key.hashCode() + ""); } @@ -705,7 +727,7 @@ private long removeNext() { return fileSize; } - private long calculateSize(File file) { + private long calculateSize(@NotNull File file) { return file.length(); } } @@ -723,7 +745,7 @@ private static class Utils { * @param str * @return true:到期了 false:还没有到期 */ - private static boolean isDue(String str) { + private static boolean isDue(@NotNull String str) { return isDue(str.getBytes()); } @@ -750,11 +772,13 @@ private static boolean isDue(byte[] data) { return false; } + @NotNull private static String newStringWithDateInfo(int second, String strInfo) { return createDateInfo(second) + strInfo; } - private static byte[] newByteArrayWithDateInfo(int second, byte[] data2) { + @NotNull + private static byte[] newByteArrayWithDateInfo(int second, @NotNull byte[] data2) { byte[] data1 = createDateInfo(second).getBytes(); byte[] retdata = new byte[data1.length + data2.length]; System.arraycopy(data1, 0, retdata, 0, data1.length); @@ -762,6 +786,7 @@ private static byte[] newByteArrayWithDateInfo(int second, byte[] data2) { return retdata; } + @Contract("null -> null") private static String clearDateInfo(String strInfo) { if (strInfo != null && hasDateInfo(strInfo.getBytes())) { strInfo = strInfo.substring(strInfo.indexOf(mSeparator) + 1, @@ -778,11 +803,13 @@ private static byte[] clearDateInfo(byte[] data) { return data; } + @Contract(value = "null -> false", pure = true) private static boolean hasDateInfo(byte[] data) { return data != null && data.length > 15 && data[13] == '-' && indexOf(data, mSeparator) > 14; } + @Nullable private static String[] getDateInfoFromDate(byte[] data) { if (hasDateInfo(data)) { String saveDate = new String(copyOfRange(data, 0, 13)); @@ -793,6 +820,7 @@ private static String[] getDateInfoFromDate(byte[] data) { return null; } + @Contract(pure = true) private static int indexOf(byte[] data, char c) { for (int i = 0; i < data.length; i++) { if (data[i] == c) { @@ -802,6 +830,7 @@ private static int indexOf(byte[] data, char c) { return -1; } + @NotNull private static byte[] copyOfRange(byte[] original, int from, int to) { int newLength = to - from; if (newLength < 0) @@ -815,9 +844,9 @@ private static byte[] copyOfRange(byte[] original, int from, int to) { private static final char mSeparator = ' '; private static String createDateInfo(int second) { - String currentTime = System.currentTimeMillis() + ""; + StringBuilder currentTime = new StringBuilder(System.currentTimeMillis() + ""); while (currentTime.length() < 13) { - currentTime = "0" + currentTime; + currentTime.insert(0, "0"); } return currentTime + "-" + second + mSeparator; } @@ -825,6 +854,7 @@ private static String createDateInfo(int second) { /* * Bitmap → byte[] */ + @Contract("null -> null") private static byte[] Bitmap2Bytes(Bitmap bm) { if (bm == null) { return null; @@ -837,7 +867,8 @@ private static byte[] Bitmap2Bytes(Bitmap bm) { /* * byte[] → Bitmap */ - private static Bitmap Bytes2Bimap(byte[] b) { + @Nullable + private static Bitmap Bytes2Bimap(@NotNull byte[] b) { if (b.length == 0) { return null; } @@ -847,6 +878,7 @@ private static Bitmap Bytes2Bimap(byte[] b) { /* * Drawable → Bitmap */ + @Contract("null -> null") private static Bitmap drawable2Bitmap(Drawable drawable) { if (drawable == null) { return null; @@ -870,6 +902,7 @@ private static Bitmap drawable2Bitmap(Drawable drawable) { /* * Bitmap → Drawable */ + @Contract("null -> null") @SuppressWarnings("deprecation") private static Drawable bitmap2Drawable(Bitmap bm) { if (bm == null) { diff --git a/common_base/src/main/java/com/wss/common/utils/DateUtils.java b/common_base/src/main/java/com/wss/common/utils/DateUtils.java index ae0a878..1e4f303 100644 --- a/common_base/src/main/java/com/wss/common/utils/DateUtils.java +++ b/common_base/src/main/java/com/wss/common/utils/DateUtils.java @@ -2,13 +2,21 @@ import android.annotation.SuppressLint; import android.text.TextUtils; +import android.util.Log; -import com.wss.common.constants.Constant; +import com.orhanobut.logger.Logger; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Calendar; import java.util.Date; +import java.util.GregorianCalendar; +import java.util.List; import java.util.TimeZone; /** @@ -18,6 +26,16 @@ @SuppressLint("SimpleDateFormat") public class DateUtils { + public static final String DATE_FORMAT = "yyyyMMddHH"; + public static final String DATE_FORMAT_SLASH = "yyyy/MM/dd"; + public static final String DATE_FORMAT_LINE = "yyyy-MM-dd"; + public static final String DATE_FORMAT_TIME = "HH:mm:ss"; + public static final String DATE_FORMAT_DEFAULT = DATE_FORMAT_SLASH + " " + DATE_FORMAT_TIME; + private static final int SECOND = 60; + private static final int DAY = 60; + private static final int MOUTH = 60; + + /** * 获取当前时间戳 * @@ -33,9 +51,35 @@ public static long getCurrentTimeStamp() { * @return String */ public static String getCurrentDateStr() { - return getFormatDate(getCurrentTimeStamp(), Constant.DATE_FORMAT_LINE); + return getFormatDate(getCurrentTimeStamp(), DATE_FORMAT_LINE); } + /** + * 获取格式化的当前系统时间 + * + * @return String + */ + public static String getCurrentDateStr(String pattern) { + return getFormatDate(getCurrentTimeStamp(), pattern); + } + + /** + * 获取格式化时间 + * + * @param date date + * @param pattern 格式化格式(默认yyyy-MM-dd HH:mm:ss) + */ + @NotNull + public static String getFormatDate(Date date, String pattern) { + if (date == null) { + return ""; + } + if (!ValidUtils.isValid(pattern)) { + pattern = DATE_FORMAT_DEFAULT; + } + SimpleDateFormat format = new SimpleDateFormat(pattern); + return format.format(date); + } /** * 获取格式化时间 @@ -49,7 +93,7 @@ public static String getFormatDate(long timeStamp, String pattern) { timeStamp *= 1000; } if (TextUtils.isEmpty(pattern)) { - pattern = Constant.DATE_FORMAT_DEFAULT; + pattern = DATE_FORMAT_DEFAULT; } SimpleDateFormat format = new SimpleDateFormat(pattern); format.setTimeZone(TimeZone.getDefault()); @@ -64,21 +108,14 @@ public static String getFormatDate(long timeStamp, String pattern) { * @param pattern pattern * @return String */ - public static String getFormatDate(String stringDate, String pattern) { + public static String getFormatDate2(String stringDate, String pattern) { if (TextUtils.isEmpty(stringDate)) { return ""; } - String parentPattern; - if (stringDate.length() == 16) { - parentPattern = "yyyy-MM-dd HH:mm"; - } else if (stringDate.length() == 19) { - parentPattern = "yyyy-MM-dd HH:mm:ss"; - } else { - return stringDate; - } - SimpleDateFormat sdf1 = new SimpleDateFormat(pattern); try { - return sdf1.format(new SimpleDateFormat(parentPattern).parse(stringDate)); + SimpleDateFormat sdf1 = new SimpleDateFormat(pattern); + String sourcePattern = String.format("%s %s", DATE_FORMAT_LINE, DATE_FORMAT_TIME); + return sdf1.format(new SimpleDateFormat(sourcePattern).parse(stringDate)); } catch (ParseException e) { e.printStackTrace(); } @@ -86,39 +123,59 @@ public static String getFormatDate(String stringDate, String pattern) { } /** - * 获取系统的时间 + * 格式化返回日期时间 * + * @param stringDate stringDate + * @param pattern pattern * @return String */ - public static String getCurrentTimeStr() { - SimpleDateFormat fort = new SimpleDateFormat("HH:mm:ss"); - fort.setTimeZone(TimeZone.getTimeZone("GMT+8:00")); - return fort.format(getCurrentTimeStamp()); + public static String getFormatDate(String stringDate, String pattern) { + if (TextUtils.isEmpty(stringDate)) { + return ""; + } + String parentPattern; + switch (stringDate.length()) { + case 10: + parentPattern = "yyyy-MM-dd"; + break; + case 16: + parentPattern = "yyyy-MM-dd HH:mm"; + break; + case 19: + parentPattern = "yyyy-MM-dd HH:mm:ss"; + break; + default: + return stringDate; + } + SimpleDateFormat sdf1 = new SimpleDateFormat(pattern); + try { + return sdf1.format(new SimpleDateFormat(parentPattern).parse(stringDate)); + } catch (ParseException e) { + e.printStackTrace(); + } + return stringDate; } + /** - * 获取当前时间的下一天/ 前一天时间 - * - * @param time time - * @param day 正数为以后 负数为以前 - * @return + * 日期转日历 */ - public static long getNextDayTimeStamp(long time, int day) { - long timeStamp = 0; - Calendar cal = Calendar.getInstance(); - Date date = new Date(time); - SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); + @NotNull + public static Calendar getFormatDate(String stringDate) { + SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_LINE); + Calendar calendar = Calendar.getInstance(); try { - cal.setTime(date); - cal.add(Calendar.DATE, 1); - timeStamp = getStringToTimeStamp(sdf.format(cal.getTime())); - } catch (Exception e) { + Date date = sdf.parse(stringDate); + if (date != null) { + calendar.setTime(date); + } + } catch (ParseException e) { e.printStackTrace(); - return timeStamp; } - return timeStamp; + return calendar; } + /** * 根据字符串时间获取时间戳 * @@ -134,7 +191,9 @@ public static long getStringToTimeStamp(String stringDate) { } try { date = simpleDateFormat.parse(stringDate); - timeStamp = date.getTime(); + if (date != null) { + timeStamp = date.getTime(); + } } catch (ParseException e) { return timeStamp; } @@ -142,32 +201,213 @@ public static long getStringToTimeStamp(String stringDate) { } /** - * 根据当前时间获取问候语 + * 时间转换 * - * @return 问候语 + * @param date 转换的时间戳 */ - public static String getTimeToken() { - Calendar calendar = Calendar.getInstance(); - int hour = calendar.get(Calendar.HOUR_OF_DAY); - if (hour >= 0 && hour < 5) { - return "凌晨"; - } else if (hour >= 5 && hour < 7) { - return "清晨"; - } else if (hour >= 7 && hour < 9) { - return "早上"; - } else if (hour >= 9 && hour < 12) { - return "上午"; - } else if (hour >= 12 && hour < 14) { - return "中午"; - } else if (hour >= 14 && hour < 17) { - return "下午"; - } else if (hour >= 17 && hour < 19) { - return "傍晚"; - } else if (hour >= 19 && hour < 21) { - return "晚上"; - } else if (hour >= 21 && hour < 24) { - return "深夜"; - } - return ""; + public static String dateTransformation(long date) { + long difference = (getCurrentTimeStamp() / 1000) - (date / 1000); + if (difference > 0) { + if (difference < SECOND * SECOND) { + long temp = difference / 60; + if (temp < 1) { + return "1分钟前"; + } + return difference / 60 + "分钟前"; + } else if (difference < DAY * SECOND * SECOND) { + + return (int) (difference / 60 / 60) + "小时前"; + } else if (difference < MOUTH * DAY * SECOND * SECOND) { + + return (int) (difference / 60 / 60 / 24) + "日前"; + } else { + return getFormatDate(date, DATE_FORMAT_SLASH); + } + } else { + return getFormatDate(date, DATE_FORMAT_SLASH); + } } + + /** + * 格式化时间 + * + * @param dateStr 5月21日 17:00 + * @return String + */ + @Nullable + public static String getMessageDate(long dateStr) { + try { + Date date = new Date(); + date.setTime(dateStr); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return String.format("%s月%s日%s:%s", (calendar.get(Calendar.MONTH) + 1), + calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE)); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * 根据月份返回中文 + * + * @param month 月份 + * @return 中文 + */ + @NotNull + @Contract(pure = true) + public static String getDateFormatMonth(int month) { + switch (month) { + case 1: + return "一月"; + case 2: + return "二月"; + case 3: + return "三月"; + case 4: + return "四月"; + case 5: + return "五月"; + case 6: + return "六月"; + case 7: + return "七月"; + case 8: + return "八月"; + case 9: + return "九月"; + case 10: + return "十月"; + case 11: + return "十一月"; + case 12: + return "十二月"; + default: + return ""; + } + } + + /** + * 计算两个时间的时间差 + * + * @param time 时间 + * @return 差值 + */ + public static long getTimeDifference(long time) { + long difference = System.currentTimeMillis() - time; + Log.e("时间差:", difference + "|" + difference / 1000); + return difference / 1000; + } + + /** + * 获取当前时间往前往后n的日期 + * eg: date ==null 默认是系统当前时间 否则以date为时间起点 + * n > 0 往后 + * n = 0 当前时间 + * n < 0 往前 + * + * @param date + * @param n + * @return + * @author zhangheng5@lenovo.com + */ + @NotNull + public static List getBeforeDate(Date date, int n) { + List list = new ArrayList<>(); + Calendar calender = Calendar.getInstance(); + Date today = new Date(); + if (null != date) { + today = date; + } + calender.setTime(today); + + if (n > 0) { + for (int i = 0; i <= n; i++) { + calender.add(Calendar.DATE, i); + list.add(getFormatDate(calender.getTime(), DATE_FORMAT_LINE)); + calender.setTime(today); + } + } else if (n < 0) { + for (int i = n; i <= 0; i++) { + calender.add(Calendar.DATE, i); + list.add(getFormatDate(calender.getTime(), DATE_FORMAT_LINE)); + calender.setTime(today); + } + } else { + calender.add(Calendar.DATE, 0); + list.add(getFormatDate(calender.getTime(), DATE_FORMAT_LINE)); + calender.setTime(today); + } + + return list; + } + + /** + * 根据时间戳判断是否是明天 + * + * @param time 秒级 时间戳 + * @return b + */ + public static boolean isTomorrow(long time) { + Calendar curDate = Calendar.getInstance(); + Calendar tomorrowCalendar = new GregorianCalendar(curDate + .get(Calendar.YEAR), curDate.get(Calendar.MONTH), curDate + .get(Calendar.DATE) + 1, 0, 0, 0); + long timeInMillis = tomorrowCalendar.getTimeInMillis(); + return time >= tomorrowCalendar.getTimeInMillis() / 1000; + } + + /** + * 格式化时间 + * + * @param dateStr 时间字符串 + * @return boolean + */ + public static boolean isToday(String dateStr) { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + Date date = null; + try { + date = format.parse(dateStr); + } catch (ParseException e) { + e.printStackTrace(); + } + Calendar c1 = Calendar.getInstance(); + c1.setTime(date); + int year1 = c1.get(Calendar.YEAR); + int month1 = c1.get(Calendar.MONTH) + 1; + int day1 = c1.get(Calendar.DAY_OF_MONTH); + Calendar c2 = Calendar.getInstance(); + c2.setTime(new Date()); + int year2 = c2.get(Calendar.YEAR); + int month2 = c2.get(Calendar.MONTH) + 1; + int day2 = c2.get(Calendar.DAY_OF_MONTH); + return year1 == year2 && month1 == month2 && day1 == day2; + } + + /** + * 判断某一个时间是不是下午 + * + * @param dateStr 时间字符串 + * @return boolean + */ + public static boolean isDatePm(String dateStr) { + int a = Integer.parseInt(getFormatDate2(dateStr, "HH")); + return a >= 12 && a < 24; + } + + + /** + * 获取afterMinute钟以前或者以后的时间 + * + * @param afterMinute 之前传负数 之后传正数 + * @return yyyy-MM-dd HH:mm:ss; + */ + public static String getAfterMinuteTime(int afterMinute) { + SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date date = new Date(getCurrentTimeStamp() + 60 * 1000 * afterMinute); + return sd.format(date); + } + + } diff --git a/common_base/src/main/java/com/wss/common/utils/EventBusUtils.java b/common_base/src/main/java/com/wss/common/utils/EventBusUtils.java index 001b5ca..3a89779 100644 --- a/common_base/src/main/java/com/wss/common/utils/EventBusUtils.java +++ b/common_base/src/main/java/com/wss/common/utils/EventBusUtils.java @@ -1,14 +1,14 @@ package com.wss.common.utils; + import com.wss.common.bean.Event; import org.greenrobot.eventbus.EventBus; /** - * Describe:EventBusUtils + * Describe:EventBus帮助类 * Created by 吴天强 on 2018/10/22. */ - public class EventBusUtils { /** @@ -31,5 +31,18 @@ public static void unregister(Object subscriber) { public static void sendEvent(Event event) { EventBus.getDefault().post(event); } - //... + + /** + * 发送普通事件 + */ + public static void sendEvent(String action) { + sendEvent(new Event(action)); + } + + /** + * 发送普通事件 + */ + public static void sendEvent(String action, Object data) { + sendEvent(new Event<>(action, data)); + } } diff --git a/common_base/src/main/java/com/wss/common/utils/FileUtils.java b/common_base/src/main/java/com/wss/common/utils/FileUtils.java index 5b35a8d..f4c885c 100644 --- a/common_base/src/main/java/com/wss/common/utils/FileUtils.java +++ b/common_base/src/main/java/com/wss/common/utils/FileUtils.java @@ -1,30 +1,50 @@ package com.wss.common.utils; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.ContentUris; import android.content.Context; +import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Environment; -import android.support.annotation.Nullable; -import android.support.v4.content.FileProvider; +import android.provider.DocumentsContract; +import android.provider.MediaStore; import com.orhanobut.logger.Logger; import com.wss.common.base.BaseApplication; +import org.jetbrains.annotations.NotNull; + import java.io.File; +import java.text.SimpleDateFormat; +import java.util.Calendar; import java.util.Collections; import java.util.LinkedList; +import androidx.annotation.Nullable; +import androidx.core.content.FileProvider; + /** * Describe:文件帮助类 * Created by 吴天强 on 2018/10/25. */ - public class FileUtils { - public static final String APPS_ROOT_DIR = getExternalStorePath() + File.separator + BaseApplication.getApplication().getPackageName(); - public static final String IMAGE_PATH = APPS_ROOT_DIR + "/Image"; - public static final String TEMP_PATH = APPS_ROOT_DIR + "/Temp"; - public static final String APP_CRASH_PATH = APPS_ROOT_DIR + "/AppCrash"; - public static final String FILE_PATH = APPS_ROOT_DIR + "/File"; + public static final String APPS_ROOT_DIR = getExternalStorePath() + File.separator + BaseApplication.i().getPackageName(); + public static final String IMAGE_PATH = APPS_ROOT_DIR + Constant.IMAGE; + public static final String TEMP_PATH = APPS_ROOT_DIR + Constant.TEMP; + public static final String APP_CRASH_PATH = APPS_ROOT_DIR + Constant.APP_CRASH; + public static final String FILE_PATH = APPS_ROOT_DIR + Constant.FILE; + + /** + * 存放文件的目录 + */ + public interface Constant { + String IMAGE = "/Image"; + String TEMP = "/Temp"; + String FILE = "/File"; + String APP_CRASH = "/AppCrash"; + } /** @@ -49,6 +69,7 @@ public static boolean isExistExternalStore() { return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); } + @org.jetbrains.annotations.Nullable private static File create(String path) { if (!isExistExternalStore()) { Logger.e("储存卡已拔出"); @@ -56,7 +77,7 @@ private static File create(String path) { } File directory = new File(path); if (!directory.exists()) { - directory.mkdir(); + directory.mkdirs(); } return directory; } @@ -67,8 +88,9 @@ private static File create(String path) { * @return File */ @Nullable - public static File getImagePath() { - return create(IMAGE_PATH); + public static String getImagePath() { + File file = create(IMAGE_PATH); + return ValidUtils.isValid(file) ? file.getAbsolutePath() : ""; } /** @@ -77,8 +99,9 @@ public static File getImagePath() { * @return File */ @Nullable - public static File getTempPath() { - return create(TEMP_PATH); + public static String getTempPath() { + File file = create(TEMP_PATH); + return ValidUtils.isValid(file) ? file.getAbsolutePath() : ""; } /** @@ -86,8 +109,10 @@ public static File getTempPath() { * * @return File */ - public static File getAppCrashPath() { - return create(APP_CRASH_PATH); + @NotNull + public static String getAppCrashPath() { + File file = create(APP_CRASH_PATH); + return ValidUtils.isValid(file) ? file.getAbsolutePath() : ""; } /** @@ -95,8 +120,10 @@ public static File getAppCrashPath() { * * @return File */ - public static File getFilePath() { - return create(FILE_PATH); + @NotNull + public static String getFilePath() { + File file = create(FILE_PATH); + return ValidUtils.isValid(file) ? file.getAbsolutePath() : ""; } /** @@ -116,6 +143,108 @@ public static Uri getUriForFile(Context context, File file) { return fileUri; } + /** + * URI转文件 + * + * @param context context + * @param uri uri + * @return File + */ + @org.jetbrains.annotations.Nullable + public static String uriToFile(@NotNull Activity context, @NotNull Uri uri) { + if ("content".equalsIgnoreCase(uri.getScheme())) { + String filePath = null; + if (DocumentsContract.isDocumentUri(context, uri)) { + // 如果是document类型的 uri, 则通过document id来进行处理 + String documentId = DocumentsContract.getDocumentId(uri); + if (isMediaDocument(uri)) { // MediaProvider + // 使用':'分割 + String type = documentId.split(":")[0]; + String id = documentId.split(":")[1]; + + String selection = MediaStore.Images.Media._ID + "=?"; + String[] selectionArgs = {id}; + Uri contentUri = null; + if ("image".equals(type)) { + contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + } else if ("video".equals(type)) { + contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; + } else if ("audio".equals(type)) { + contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; + } + + filePath = getDataColumn(context, contentUri, selection, selectionArgs); + } else if (isDownloadsDocument(uri)) { // DownloadsProvider + Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.parseLong(documentId)); + filePath = getDataColumn(context, contentUri, null, null); + } else if (isExternalStorageDocument(uri)) { + // ExternalStorageProvider + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + if ("primary".equalsIgnoreCase(type)) { + filePath = Environment.getExternalStorageDirectory() + "/" + split[1]; + } + } //Log.e("路径错误"); + + } else if ("content".equalsIgnoreCase(uri.getScheme())) { + // 如果是 content 类型的 Uri + filePath = getDataColumn(context, uri, null, null); + } else if ("file".equals(uri.getScheme())) { + // 如果是 file 类型的 Uri,直接获取图片对应的路径 + filePath = uri.getPath(); + } + return filePath; + } else if ("file".equalsIgnoreCase(uri.getScheme())) { + return uri.getPath(); + } + return ""; + } + + + /** + * 获取数据库表中的 _data 列,即返回Uri对应的文件路径 + * + * @return + */ + private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { + String path = null; + + String[] projection = new String[]{MediaStore.Images.Media.DATA}; + Cursor cursor = null; + try { + cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); + if (cursor != null && cursor.moveToFirst()) { + int columnIndex = cursor.getColumnIndexOrThrow(projection[0]); + path = cursor.getString(columnIndex); + } + } catch (Exception e) { + if (cursor != null) { + cursor.close(); + } + } + return path; + } + + /** + * @param uri the Uri to check + * @return Whether the Uri authority is MediaProvider + */ + private static boolean isMediaDocument(@NotNull Uri uri) { + return "com.android.providers.media.documents".equals(uri.getAuthority()); + } + + private static boolean isExternalStorageDocument(@NotNull Uri uri) { + return "com.android.externalstorage.documents".equals(uri.getAuthority()); + } + + /** + * @param uri the Uri to check + * @return Whether the Uri authority is DownloadsProvider + */ + private static boolean isDownloadsDocument(@NotNull Uri uri) { + return "com.android.providers.downloads.documents".equals(uri.getAuthority()); + } /** * 获取目录下的文件列表 @@ -159,4 +288,15 @@ public static void deleteFile(String filesName) { } } + /** + * 读取文件的最后修改时间的方法 + */ + public static String getFileLastModifiedTime(File f) { + Calendar cal = Calendar.getInstance(); + long time = f.lastModified(); + @SuppressLint("SimpleDateFormat") + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + cal.setTimeInMillis(time); + return formatter.format(cal.getTime()); + } } diff --git a/common_base/src/main/java/com/wss/common/utils/GroupUtils.java b/common_base/src/main/java/com/wss/common/utils/GroupUtils.java new file mode 100644 index 0000000..4ad3bfc --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/GroupUtils.java @@ -0,0 +1,150 @@ +package com.wss.common.utils; + +import android.text.TextUtils; +import android.util.Log; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import androidx.annotation.Nullable; + +/** + * Describe:Lis分组 + * Created by 吴天强 on 2018-05-31 08:45 + **/ +public class GroupUtils { + private static final String TAG = GroupUtils.class.getSimpleName(); + + /** + * 分组依据接口,用于集合分组时,获取分组依据 + * + * @param + */ + public interface GroupBy { + T groupBy(Object obj); + } + + /** + * 待分组的集合 + * 分组依据 + * + * @param + * @param + * @return 分组数据 + */ + public static , D> Map> group(Collection colls, GroupBy gb) { + if (colls == null || colls.isEmpty()) { + Log.e(TAG, "分组集合不能为空"); + return null; + } + if (gb == null) { + Log.e(TAG, " 分组依据接口不能为Null! "); + return null; + } + Iterator iter = colls.iterator(); + Map> map = new HashMap<>(); + while (iter.hasNext()) { + D d = iter.next(); + T t = gb.groupBy(d); + if (map.containsKey(t)) { + map.get(t).add(d); + } else { + List list = new ArrayList<>(); + list.add(d); + map.put(t, list); + } + } + return map; + } + + /** + * 将List按照V的methodName方法返回值(返回值必须为K类型)分组,合入到Map>中
+ * 要保证入参的method必须为V的某一个有返回值的方法,并且该返回值必须为K类型 + * + * @param list 待分组的列表 + * @param map 存放分组后的map + * @param clazz 泛型V的类型 + * @param methodName 方法名 + */ + public static void listGroup2Map(List list, Map> map, Class clazz, String methodName) { + // 入参非法行校验 + if (null == list || null == map || null == clazz || TextUtils.isEmpty(methodName)) { + return; + } + + // 获取方法 + Method method = getMethodByName(clazz, methodName); + // 非空判断 + if (null == method) { + return; + } + // 正式分组 + listGroup2Map(list, map, method); + } + + /** + * 根据类和方法名,获取方法对象 + * + * @param clazz class + * @param methodName 风机组方法名 + * @return Method + */ + @Nullable + private static Method getMethodByName(Class clazz, String methodName) { + Method method = null; + // 入参不能为空 + if (null == clazz || TextUtils.isEmpty(methodName)) { + Log.e(TAG, "GroupUtils.getMethodByName 入参错误,clazz:" + clazz + " ;methodName:" + methodName); + return null; + } + try { + method = clazz.getDeclaredMethod(methodName); + } catch (Exception e) { + Log.e(TAG, "类获取方法失败"); + } + + return method; + } + + /** + * 将List按照V的某个方法返回值(返回值必须为K类型)分组,合入到Map>中
+ * 要保证入参的method必须为V的某一个有返回值的方法,并且该返回值必须为K类型 + * + * @param list 待分组的列表 + * @param map 存放分组后的map + * @param method 方法 + */ + @SuppressWarnings("unchecked") + private static void listGroup2Map(List list, Map> map, Method method) { + // 入参非法行校验 + if (null == list || null == map || null == method) { + Log.e(TAG, "GroupUtils.listGroup2Map 入参错误,list:" + list + " ;map:" + map + " ;method:" + method); + return; + } + try { + // 开始分组 + Object key; + List listTmp; + for (V val : list) { + key = method.invoke(val); + listTmp = map.get(key); + if (null == listTmp) { + listTmp = new ArrayList<>(); + map.put((K) key, listTmp); + } + listTmp.add(val); + } + } catch (Exception e) { + e.printStackTrace(); + Log.e(TAG, "数据分组异常" + e.getMessage()); + } + } + +} + + diff --git a/common_base/src/main/java/com/wss/common/utils/ImageUtils.java b/common_base/src/main/java/com/wss/common/utils/ImageUtils.java index f089b07..fc42d9e 100644 --- a/common_base/src/main/java/com/wss/common/utils/ImageUtils.java +++ b/common_base/src/main/java/com/wss/common/utils/ImageUtils.java @@ -1,83 +1,347 @@ package com.wss.common.utils; -import android.content.Context; +import android.app.Activity; +import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Matrix; import android.media.ExifInterface; -import android.support.annotation.Nullable; +import android.net.Uri; +import android.os.Environment; +import android.provider.MediaStore; +import android.view.View; import android.widget.ImageView; +import android.widget.ScrollView; -import com.squareup.picasso.Picasso; +import com.bigkoo.convenientbanner.ConvenientBanner; +import com.bigkoo.convenientbanner.listener.OnItemClickListener; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; +import com.bumptech.glide.request.RequestOptions; +import com.wss.common.adapter.BannerImgAdapter; import com.wss.common.base.R; -import com.youth.banner.Banner; -import com.youth.banner.BannerConfig; -import com.youth.banner.Transformer; -import com.youth.banner.loader.ImageLoader; +import com.wss.common.manage.BlurTransformation; +import com.wss.common.manage.GlideRoundTransform; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.List; +import androidx.annotation.DrawableRes; +import androidx.annotation.Nullable; + /** * Describe:图片工具类 * Created by 吴天强 on 2018/10/17. */ - public class ImageUtils { - public static void loadImage(Context context, String url, ImageView imageView) { - Picasso.get() + /** + * 加载手机图片 + * file + * + * @param imageView imageView + * @param file file + */ + public static void loadImage(@NotNull ImageView imageView, File file) { + loadImage(imageView, file, R.color.color_999999); + } + + /** + * 加载手机图片 + * + * @param imageView imageView + * @param file file + * @param error 加载失败占位图 + */ + public static void loadImage(@NotNull ImageView imageView, File file, @DrawableRes int error) { + Glide.with(imageView.getContext()) + .load(file) + .apply(new RequestOptions() + .placeholder(R.color.color_999999) + .error(error) + .fallback(error)) + .into(imageView); + } + + /** + * 加载手机图片 + * + * @param imageView imageView + */ + public static void loadImage(@NotNull ImageView imageView, Bitmap bitmap) { + Glide.with(imageView.getContext()) + .load(bitmap) + .apply(new RequestOptions() + .placeholder(R.color.color_999999)) + .into(imageView); + } + + /** + * 加载本地图片 + * + * @param drawable drawable + * @param imageView imageView + */ + public static void loadImage(@NotNull ImageView imageView, @DrawableRes int drawable) { + loadImage(imageView, drawable, R.color.color_999999); + } + + /** + * 加载本地图片 + * + * @param drawable drawable + * @param imageView imageView + * @param error 加载失败占位图 + */ + public static void loadImage(@NotNull ImageView imageView, int drawable, @DrawableRes int error) { + Glide.with(imageView.getContext()) + .load(drawable) + .apply(new RequestOptions() + .placeholder(R.color.color_999999) + .error(error) + .fallback(error)) + .into(imageView); + } + + /** + * 加载网络图片 + * + * @param url url + * @param imageView imageView + */ + public static void loadImage(ImageView imageView, String url) { + loadImage(imageView, url, R.color.color_999999); + } + + /** + * 加载网络图片 + * + * @param url url + * @param imageView imageView + * @param error 加载失败占位图 + */ + public static void loadImage(@NotNull ImageView imageView, String url, @DrawableRes int error) { + Glide.with(imageView.getContext()) .load(url) - .placeholder(R.drawable.bg_load_failed) - .error(R.drawable.bg_load_failed) + .apply(new RequestOptions() + .placeholder(R.color.color_999999) + .error(error) + .fallback(error)) .into(imageView); } /** - * 加载轮播图 + * 加载圆形图片 * - * @param banner 轮播图控件 - * @param images 图片集合 + * @param url url + * @param imageView imageView */ - public static void loadBanner(Banner banner, List images) { - if (banner == null || images == null) { - return; - } - //如果没有图片集合 则给出一个默认占位图 - if (images.size() == 0) { - images.add("R.drawable.bg_load_failed"); - } - //设置banner样式 - banner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR); - //设置图片加载器 - banner.setImageLoader(new PicassoImageLoader()); - //设置banner动画效果 - banner.setBannerAnimation(Transformer.DepthPage); - //设置自动轮播,默认为true -// banner.isAutoPlay(false); - //设置轮播时间 - banner.setDelayTime(2000); - //设置指示器位置(当banner模式中有指示器时) - banner.setIndicatorGravity(BannerConfig.RIGHT); - //设置图片集合 - banner.setImages(images); - //banner设置方法全部调用完毕时最后调用 - banner.start(); + public static void loadImageCircle(ImageView imageView, String url) { + loadImageCircle(imageView, url, R.color.color_999999, R.color.color_999999); + } + + /** + * 加载圆形图片 + * + * @param url url + * @param imageView imageView + * @param error 加载失败占位图 + */ + public static void loadImageCircle(@NotNull ImageView imageView, String url, @DrawableRes int placeholder, @DrawableRes int error) { + Glide.with(imageView.getContext()) + .load(url) + .apply(RequestOptions.circleCropTransform() + .placeholder(placeholder) + .error(error) + .fallback(error)) + .into(imageView); } /** - * Banner图片加载器 + * 加载圆角图片 + * + * @param imageView ImageView + * @param url URL + * @param dp 圆角角度 */ - private static class PicassoImageLoader extends ImageLoader { + public static void loadImageCircleBead(@NotNull ImageView imageView, String url, int dp) { + loadImageCircleBead(imageView, url, dp, R.color.color_999999); + } - @Override - public void displayImage(Context context, Object path, ImageView imageView) { - loadImage(context, (String) path, imageView); - } + /** + * 加载圆角图片 + * + * @param imageView ImageView + * @param url URL + * @param dp 圆角角度 + * @param error 加载失败占位图 + */ + public static void loadImageCircleBead(@NotNull ImageView imageView, String url, int dp, @DrawableRes int error) { + Glide.with(imageView.getContext()) + .load(url) + .apply(new RequestOptions() + .placeholder(R.color.color_999999) + .error(error) + .transform(new GlideRoundTransform(imageView.getContext(), dp))) + .into(imageView); + } + + public static void loadImageTest(@NotNull ImageView imageView, String url, int dp) { + + //设置图片圆角角度 + RoundedCorners roundedCorners = new RoundedCorners(dp); + //通过RequestOptions扩展功能,override:采样率,因为ImageView就这么大,可以压缩图片,降低内存消耗 + // RequestOptions options = RequestOptions.bitmapTransform(roundedCorners).override(300, 300); + RequestOptions options = RequestOptions.bitmapTransform(roundedCorners); + options.placeholder(R.color.color_999999); + Glide.with(imageView.getContext()).load(url).apply(options).into(imageView); + + } + + /** + * 加载圆角图片 + * + * @param imageView ImageView + * @param file file + * @param dp 圆角角度 + */ + public static void loadImageCircleBead(@NotNull ImageView imageView, File file, int dp) { + loadImageCircleBead(imageView, file, dp, R.color.color_999999); + } + + /** + * 加载圆角图片 + * + * @param imageView ImageView + * @param file file + * @param dp 圆角角度 + * @param error 加载失败占位图 + */ + public static void loadImageCircleBead(@NotNull ImageView imageView, File file, int dp, @DrawableRes int error) { + Glide.with(imageView.getContext()) + .load(file) + .apply(new RequestOptions() + .placeholder(R.color.color_999999) + .error(error) + .transform(new GlideRoundTransform(imageView.getContext(), dp))) + .into(imageView); + } + + + /** + * 加载高斯模糊图 + * + * @param imageView imageView + * @param drawable drawable + */ + public static void loadImageBlur(ImageView imageView, int drawable) { + Glide.with(imageView) + .load(drawable) + .apply(RequestOptions.bitmapTransform( + new BlurTransformation(imageView.getContext(), 5f)) + .error(R.color.theme)) + .into(imageView); + } + + /** + * 加载高斯模糊图 + * + * @param imageView imageView + * @param drawable drawable + * @param error 加载失败占位图 + */ + public static void loadImageBlur(ImageView imageView, @DrawableRes int drawable, @DrawableRes int error) { + Glide.with(imageView) + .load(drawable) + .apply(RequestOptions.bitmapTransform( + new BlurTransformation(imageView.getContext(), 5f)) + .placeholder(R.color.color_999999) + .error(error)) + .into(imageView); + } + + /** + * 加载高斯模糊图 + * + * @param imageView imageView + * @param url url + */ + public static void loadImageBlur(ImageView imageView, String url) { + Glide.with(imageView) + .load(url) + .apply(RequestOptions.bitmapTransform( + new BlurTransformation(imageView.getContext())) + .error(R.color.theme)) + .into(imageView); + } + + /** + * 加载高斯模糊图 + * + * @param imageView imageView + * @param url url + * @param error 加载失败占位图 + */ + public static void loadImageBlur(ImageView imageView, String url, @DrawableRes int error) { + Glide.with(imageView) + .load(url) + .apply(RequestOptions.bitmapTransform( + new BlurTransformation(imageView.getContext())) + .placeholder(R.color.color_999999) + .error(error)) + .into(imageView); + } + + + /** + * 加载圆形头像 + * + * @param imageView imageView + * @param url 图片链接 + */ + public static void loadCircleHeader(ImageView imageView, String url) { + loadImageCircle(imageView, url, R.drawable.header_load_default, R.drawable.header_load_default); + } + + /** + * 加载只有一张图的Banner + * + * @param banner banner控件 + * @param imgUrl banner图片集合 + * @param listener listener + */ + public static void loadBanner(ConvenientBanner banner, List imgUrl, OnItemClickListener listener) { + banner.setPages(new BannerImgAdapter(), imgUrl) + .setPageIndicator(new int[]{R.drawable.shape_item_index_white, R.drawable.shape_item_index_red}) + .setPageIndicatorAlign(ConvenientBanner.PageIndicatorAlign.ALIGN_PARENT_RIGHT) + .setOnItemClickListener(listener) + .startTurning(); + } + + /** + * 加载只有一张图的Banner + * + * @param banner banner控件 + * @param imgUrl banner图片集合 + * @param listener listener + */ + public static void loadBanner(ConvenientBanner banner, List imgUrl, boolean circle, OnItemClickListener listener) { + banner.setPages(new BannerImgAdapter(circle), imgUrl) + .setPageIndicator(new int[]{R.drawable.shape_item_index_white, R.drawable.shape_item_index_red}) + .setPageIndicatorAlign(ConvenientBanner.PageIndicatorAlign.ALIGN_PARENT_RIGHT) + .setOnItemClickListener(listener) + .startTurning(); } /** @@ -91,10 +355,12 @@ public static File compressImage(String filePath) { if (!FileUtils.isExistExternalStore()) { return null; } - int quality = 100; - Bitmap bm = getSmallBitmap(filePath);//获取一定尺寸的图片 - int degree = readPictureDegree(filePath);//获取相片拍摄角度 - if (degree != 0) {//旋转照片角度,防止头像横着显示 + int quality = 100;//获取一定尺寸的图片 + Bitmap bm = getSmallBitmap(filePath); + //获取相片拍摄角度 + int degree = readPictureDegree(filePath); + if (degree != 0) { + //旋转照片角度,防止头像横着显示 bm = rotateBitmap(bm, degree); } File outputFile = null; @@ -117,10 +383,14 @@ public static File compressImage(String filePath) { /** * 根据路径获得图片信息并按比例压缩,返回bitmap + * + * @param filePath 图片地址 + * @return Bitmap */ public static Bitmap getSmallBitmap(String filePath) { final BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true;//只解析图片边沿,获取宽高 + //只解析图片边沿,获取宽高 + options.inJustDecodeBounds = true; BitmapFactory.decodeFile(filePath, options); // 计算缩放比 options.inSampleSize = calculateInSampleSize(options, 480, 800); @@ -129,15 +399,22 @@ public static Bitmap getSmallBitmap(String filePath) { return BitmapFactory.decodeFile(filePath, options); } - public static int calculateInSampleSize(BitmapFactory.Options options, - int reqWidth, int reqHeight) { + /** + * 计算缩放比 + * + * @param options options + * @param reqWidth 宽 + * @param reqHeight 高 + * @return 缩放比 + */ + public static int calculateInSampleSize(@NotNull BitmapFactory.Options options, int reqWidth, int reqHeight) { final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); - inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; + inSampleSize = Math.min(heightRatio, widthRatio); } return inSampleSize; } @@ -145,8 +422,8 @@ public static int calculateInSampleSize(BitmapFactory.Options options, /** * 获取照片角度 * - * @param path - * @return + * @param path 图片地址 + * @return int */ public static int readPictureDegree(String path) { int degree = 0; @@ -175,10 +452,11 @@ public static int readPictureDegree(String path) { /** * 旋转照片 * - * @param bitmap - * @param degress - * @return + * @param bitmap bitmap + * @param degress 旋转角度 + * @return Bitmap */ + @Contract("null, _ -> null") public static Bitmap rotateBitmap(Bitmap bitmap, int degress) { if (bitmap != null) { Matrix m = new Matrix(); @@ -187,7 +465,224 @@ public static Bitmap rotateBitmap(Bitmap bitmap, int degress) { bitmap.getHeight(), m, true); return bitmap; } - return bitmap; + return null; + } + + /** + * 把View转成Bitmap 注意:改view必须是已经显示到页面上的 + * + * @return Bitmap + */ + public static Bitmap viewConversionBitmap(@NotNull View view, String color) { + + int w = view.getWidth(); + int h = view.getHeight(); + + Bitmap bmp = Bitmap.createBitmap(w, w, Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(bmp); + + c.drawColor(Color.parseColor(color)); + view.layout(0, 0, w, h); + view.draw(c); + return bmp; + + } + + /** + * 把View转成Bitmap 注意:改view必须是已经显示到页面上的 + * + * @return Bitmap + */ + public static Bitmap viewConversionBitmap(@NotNull ScrollView scrollView, String color) { + + int height = 20; + //正确获取ScrollView + for (int i = 0; i < scrollView.getChildCount(); i++) { + height += scrollView.getChildAt(i).getHeight(); + } + + int w = scrollView.getWidth(); + + Bitmap bmp = Bitmap.createBitmap(w, height, Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(bmp); + + c.drawColor(Color.parseColor(color)); + scrollView.layout(0, 0, w, height); + scrollView.draw(c); + return bmp; + + } + + /** + * 把Bitmap转成图片 + * + * @param bitmap bitmap + * @param imageName 文件名称 + * @return 图片的本地地址 + */ + @org.jetbrains.annotations.Nullable + public static String bitmapSaveToImage(Bitmap bitmap, String imageName) { + FileOutputStream b = null; + String filePath = FileUtils.getImagePath() + File.separator + imageName + ".jpg"; + try { + b = new FileOutputStream(filePath); + // 把数据写入文件 + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, b); + return filePath; + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (b != null) { + b.flush(); + b.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + return null; + } + + + /** + * 添加图片到系统相册 + * + * @param activity activity + * @param filePath 图片路径 + * @param fileName 图片名称 + */ + public static boolean addImageToAlbum(@NotNull Activity activity, String filePath, String fileName) { + try { + MediaStore.Images.Media.insertImage(activity.getContentResolver(), filePath, fileName, null); + activity.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + Environment.getExternalStorageDirectory().getPath()))); + return true; + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + return false; + } + + /** + * 生成个人名片海报图片 + * + * @param activity activity + * @param scrollView ScrollView + */ + public static boolean generatePoster(Activity activity, ScrollView scrollView) { + String fileName = String.valueOf(DateUtils.getCurrentTimeStamp()); + String filePath = ImageUtils.bitmapSaveToImage(ImageUtils.viewConversionBitmap(scrollView, "#F5F5F5"), fileName); + if (ValidUtils.isValid(filePath)) { + return addImageToAlbum(activity, filePath, fileName); + } + return false; + } + + /** + * 生成个人名片海报图片 + * + * @param activity activity + * @param scrollView ScrollView + */ + public static String generatePosterPath(Activity activity, ScrollView scrollView) { + return ImageUtils.bitmapSaveToImage(ImageUtils.viewConversionBitmap(scrollView, "#F5F5F5"), String.valueOf(DateUtils.getCurrentTimeStamp())); + } + + /** + * Bitmap转二进制流 + * + * @param bitmap bitmap + * @return byte[] + */ + @NotNull + public static byte[] getBitmapByte(@NotNull Bitmap bitmap) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out); + try { + out.flush(); + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + return out.toByteArray(); + } + + /** + * Bitmap转换成byte[]并且进行压缩,压缩到不大于maxkb + * + * @param bitmap bitmap + * @param maxKb 最大大小 + * @return 字节数组 + */ + @NotNull + public static byte[] bitmapBytes(@NotNull Bitmap bitmap, int maxKb) { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, output); + int options = 100; + while (output.toByteArray().length > maxKb && options != 10) { + output.reset(); //清空output + //这里压缩options%,把压缩后的数据存放到output中 + bitmap.compress(Bitmap.CompressFormat.JPEG, options, output); + options -= 10; + } + return output.toByteArray(); + } + + /** + * 获取海报的图片二进制流 + * + * @param layoutView 海报view + * @return byte[] + */ + @NotNull + public static byte[] getPosterByte(View layoutView) { + return getBitmapByte(ImageUtils.viewConversionBitmap(layoutView, "#F5F5F5")); + } + + /** + * 获取微信分享图片的缩略图 + * + * @param image 图片流 + * @return 图片流 + */ + @NotNull + public static byte[] getWXShareThumbImage(byte[] image) { + return bitmapBytes(getBitmapByBytes(image, 200, 300), 32); + } + + /** + * 根据图片生成缩略图 + * + * @param bytes 图片流 + * @param maxHeight 最大高度 + * @param maxWidth 最大宽度 + * @return Bitmap + */ + @NotNull + public static Bitmap getBitmapByBytes(byte[] bytes, int maxHeight, int maxWidth) { + //对于图片的二次采样,主要得到图片的宽与高 + int width = 0; + int height = 0; + //默认缩放为1 + int sampleSize = 1; + BitmapFactory.Options options = new BitmapFactory.Options(); + //仅仅解码边缘区域 + options.inJustDecodeBounds = true; + //如果指定了inJustDecodeBounds,decodeByteArray将返回为空 + BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); + //得到宽与高 + height = options.outHeight; + width = options.outWidth; + + //图片实际的宽与高,根据默认最大大小值,得到图片实际的缩放比例 + while ((height / sampleSize > maxHeight) || (width / sampleSize > maxWidth)) { + sampleSize *= 2; + } + //不再只加载图片实际边缘 + options.inJustDecodeBounds = false; + //并且制定缩放比例 + options.inSampleSize = sampleSize; + return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); } } diff --git a/common_base/src/main/java/com/wss/common/utils/JsonUtils.java b/common_base/src/main/java/com/wss/common/utils/JsonUtils.java new file mode 100644 index 0000000..297a8fd --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/JsonUtils.java @@ -0,0 +1,306 @@ +package com.wss.common.utils; + +import android.text.TextUtils; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; + +/** + * Describe:Gson 解析封装 + * Created by 吴天强 on 2018/12/12. + */ +public class JsonUtils { + + /** + * 对象转Json + * + * @param obj 对象 + * @return json String + */ + public static String toJson(Object obj) { + if (ValidUtils.isValid(obj)) { + return new Gson().toJson(obj); + } + return "{}"; + } + + /** + * 解析T对象 + * + * @param json json + * @param type 目标类型 + * @param 类型 + * @return T + */ + public static T getObject(String json, Class type) { + return new Gson().fromJson(json, type); + } + + /** + * 解析String + * + * @param json json + * @param key key + * @return String + */ + @Contract("null, _ -> !null") + public static String getString(JsonObject json, String key) { + if (json == null) { + return ""; + } + JsonElement jsonElement = json.get(key); + if (jsonElement == null) { + return ""; + } + return jsonElement.getAsString(); + } + + /** + * 解析String + * + * @param element json + * @return String + */ + @Contract("null, _ -> !null") + public static String getString(JsonElement element, String key) { + if (element == null) { + return ""; + } + return getString(element.getAsJsonObject(), key); + } + + /** + * 解析String + * + * @param json json + * @param key key + * @return String + */ + public static String getString(String json, String key) { + if (!isJsonObject(json)) { + return ""; + } + JsonElement jsonElement = getJsonObject(json).get(key); + if (jsonElement == null) { + return ""; + } + //判断json对象是否为Object + if (jsonElement.isJsonObject()) { + JsonObject asJsonObject = jsonElement.getAsJsonObject(); + return asJsonObject.toString(); + } + //判断json对象是否为数组 + if (jsonElement.isJsonArray()) { + JsonArray asJsonArray = jsonElement.getAsJsonArray(); + return asJsonArray.toString(); + } + return jsonElement.getAsString(); + } + + + /** + * 解析boolean + * + * @param json json + * @param key key + * @return boolean + */ + @Contract("null, _ -> false") + public static boolean getBoolean(String json, String key) { + if (json == null) { + return false; + } + JsonElement jsonElement = getJsonObject(json).get(key); + return jsonElement != null && jsonElement.getAsBoolean(); + } + + /** + * 解析int + * + * @param json json + * @param key key + * @return String + */ + public static int getInt(String json, String key) { + JsonElement jsonElement = getJsonObject(json).get(key); + if (jsonElement == null) { + return 0; + } + return jsonElement.getAsInt(); + } + + /** + * 解析int + * + * @param json json + * @param key key + * @return boolean + */ + public static int getInt(JsonObject json, String key) { + if (json == null) { + return 0; + } + JsonElement jsonElement = json.get(key); + if (jsonElement == null) { + return 0; + } + return jsonElement.getAsInt(); + } + + + /** + * 解析JsonObject + * + * @param json json + * @return JsonArray + */ + public static JsonObject getJsonObject(String json) { + if (TextUtils.isEmpty(json)) { + return new JsonObject(); + } + return new JsonParser().parse(json).getAsJsonObject(); + } + + /** + * 解析JsonArray + * + * @param json json + * @param key key + * @return JsonArray + */ + @Contract("null, _ -> new") + public static JsonArray getJsonArray(JsonObject json, String key) { + if (json == null) { + return new JsonArray(); + } + JsonElement jsonElement = json.get(key); + if (jsonElement == null) { + return new JsonArray(); + } + return jsonElement.getAsJsonArray(); + } + + + /** + * 解析Map + * + * @param json json + * @return List + */ + @NotNull + public static LinkedHashMap getMap(@NotNull String json) { + LinkedHashMap result = new LinkedHashMap<>(); + try { + JsonObject jsonObject = new JsonParser().parse(json).getAsJsonObject(); + result.putAll(new Gson().fromJson(jsonObject, new TypeToken>() { + }.getType())); + } catch (JsonSyntaxException e) { + e.printStackTrace(); + } + return result; + } + + /** + * 解析List + * + * @param json json + * @param clazz clazz + * @return List + */ + public static List getList(String json, Class clazz) { + if (!ValidUtils.isValid(json)) { + return new ArrayList<>(); + } + List list = null; + try { + list = new ArrayList<>(); + Type type = new TypeToken>() { + }.getType(); + List jsonObjects = new Gson().fromJson(json, type); + if (ValidUtils.isValid(jsonObjects)) { + for (JsonObject jsonObject : jsonObjects) { + list.add(new Gson().fromJson(jsonObject, clazz)); + } + } + } catch (JsonSyntaxException e) { + e.printStackTrace(); + } + return list; + } + + /** + * 解析List + * + * @param json json + * @return List + */ + public static List getList(String json) { + if (!isJsonArray(json)) { + return new ArrayList<>(); + } + return new Gson().fromJson(json, new TypeToken>() { + }.getType()); + } + + /** + * 判断是否JSON数组 + * + * @param json json + * @return boolean + */ + public static boolean isJsonArray(String json) { + JsonElement jsonElement = getJsonElement(json); + if (jsonElement == null) { + return false; + } + return jsonElement.isJsonArray(); + } + + /** + * 判断是否JSON对象 + * + * @param json json + * @return boolean + */ + public static boolean isJsonObject(String json) { + JsonElement jsonElement = getJsonElement(json); + if (jsonElement == null) { + return false; + } + return jsonElement.isJsonObject(); + } + + /** + * 获取JsonElement + * + * @param json json + * @return JsonElement + */ + @Nullable + private static JsonElement getJsonElement(String json) { + JsonElement jsonElement; + if (!ValidUtils.isValid(json)) { + return null; + } + try { + jsonElement = new JsonParser().parse(json); + } catch (Exception e) { + return null; + } + return jsonElement; + } +} diff --git a/common_base/src/main/java/com/wss/common/utils/KeyboardUtils.java b/common_base/src/main/java/com/wss/common/utils/KeyboardUtils.java index a4f2426..a6c3af4 100644 --- a/common_base/src/main/java/com/wss/common/utils/KeyboardUtils.java +++ b/common_base/src/main/java/com/wss/common/utils/KeyboardUtils.java @@ -4,13 +4,20 @@ import android.view.View; import android.view.inputmethod.InputMethodManager; +import org.jetbrains.annotations.NotNull; + /** * Describe:键盘辅助类 * Created by 吴天强 on 2017/11/2. */ public class KeyboardUtils { - public static void showKeyboard(View view) { + /** + * 显示键盘 + * + * @param view view + */ + public static void showKeyboard(@NotNull View view) { InputMethodManager imm = (InputMethodManager) view.getContext() .getSystemService(Context.INPUT_METHOD_SERVICE); if (imm != null) { @@ -19,19 +26,16 @@ public static void showKeyboard(View view) { } } - public static void hideKeyboard(View view) { + /** + * 隐藏键盘 + * + * @param view view + */ + public static void hideKeyboard(@NotNull View view) { InputMethodManager imm = (InputMethodManager) view.getContext() .getSystemService(Context.INPUT_METHOD_SERVICE); if (imm != null) { imm.hideSoftInputFromWindow(view.getWindowToken(), 0); } } - - public static void toggleSoftInput(View view) { - InputMethodManager imm = (InputMethodManager) view.getContext() - .getSystemService(Context.INPUT_METHOD_SERVICE); - if (imm != null) { - imm.toggleSoftInput(0, 0); - } - } } \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/utils/MoneyUtils.java b/common_base/src/main/java/com/wss/common/utils/MoneyUtils.java index 15d12d4..8768e44 100644 --- a/common_base/src/main/java/com/wss/common/utils/MoneyUtils.java +++ b/common_base/src/main/java/com/wss/common/utils/MoneyUtils.java @@ -10,7 +10,6 @@ * Describe:金钱工具类 * Created by 吴天强 on 2017/11/24. */ - public class MoneyUtils { /** * 计算类 @@ -20,32 +19,32 @@ public static class Algorithm { /** * 金钱加法 * - * @param v1 - * @param v2 - * @return + * @param value1 参数1 + * @param value2 参数2 + * @return 结果 */ - public static String add(String v1, String v2) { - BigDecimal b1 = new BigDecimal(v1); - BigDecimal b2 = new BigDecimal(v2); + public static String add(Object value1, Object value2) { + BigDecimal b1 = new BigDecimal(String.valueOf(value1)); + BigDecimal b2 = new BigDecimal(String.valueOf(value2)); return b1.add(b2).toString(); } /** * 金钱减法 * - * @param v1 - * @param v2 - * @return + * @param value1 参数1 + * @param value2 参数2 + * @return 结果 */ - public static String subtract(String v1, String v2) { - if (TextUtils.isEmpty(v1)) { - v1 = "0"; + public static String subtract(String value1, String value2) { + if (TextUtils.isEmpty(value1)) { + value1 = "0"; } - if (TextUtils.isEmpty(v2)) { - v2 = "0"; + if (TextUtils.isEmpty(value2)) { + value2 = "0"; } - BigDecimal b1 = new BigDecimal(v1); - BigDecimal b2 = new BigDecimal(v2); + BigDecimal b1 = new BigDecimal(value1); + BigDecimal b2 = new BigDecimal(value2); return b1.subtract(b2).toString(); } @@ -53,25 +52,25 @@ public static String subtract(String v1, String v2) { /** * 金钱乘法 * - * @param v1 - * @param v2 - * @return + * @param value1 参数1 + * @param value2 参数2 + * @return 结果 */ - public static String multiply(String v1, String v2) { - return multiply(v1, v2, 0); + public static String multiply(String value1, String value2) { + return multiply(value1, value2, 0); } /** * 金钱乘法 * - * @param v1 乘数 - * @param v2 被乘数 - * @param scale 小数点保留位数 - * @return + * @param value1 乘数 + * @param value2 被乘数 + * @param scale 小数点保留位数 + * @return 结果 */ - public static String multiply(String v1, String v2, int scale) { - BigDecimal b1 = new BigDecimal(v1); - BigDecimal b2 = new BigDecimal(v2); + public static String multiply(String value1, String value2, int scale) { + BigDecimal b1 = new BigDecimal(value1); + BigDecimal b2 = new BigDecimal(value2); BigDecimal result = b1.multiply(b2); result = result.setScale(scale, BigDecimal.ROUND_HALF_UP); return result.toString(); @@ -80,13 +79,13 @@ public static String multiply(String v1, String v2, int scale) { /** * 金钱除法 * - * @param v1 - * @param v2 - * @return + * @param value1 参数1 + * @param value2 参数2 + * @return 结果 */ - public static String divide(String v1, String v2) { - BigDecimal b1 = new BigDecimal(v1); - BigDecimal b2 = new BigDecimal(v2); + public static String divide(String value1, String value2) { + BigDecimal b1 = new BigDecimal(value1); + BigDecimal b2 = new BigDecimal(value2); return b1.divide(b2, BigDecimal.ROUND_HALF_UP).toString(); } } @@ -98,7 +97,7 @@ public static String divide(String v1, String v2) { * @return String */ public static String formatPrice(String textPrice) { - return formatPrice(Double.valueOf(textPrice) / 100); + return formatPrice(Double.parseDouble(textPrice) / 100); } /** @@ -108,13 +107,6 @@ public static String formatPrice(String textPrice) { * @return String */ public static String formatPrice(double price) { - if (price > 0 && price < 1) { - String temp = String.valueOf(price); - if (temp.contains(".") && temp.substring(temp.indexOf(".") + 1, temp.length()).length() <= 1) { - return String.format("%s%s", price, 0); - } - return String.format("%s", price); - } if (price <= 0) { return "0.00"; } @@ -123,4 +115,28 @@ public static String formatPrice(double price) { return String.format("%s", format.format(price)); } + /** + * 将金钱转成显示的格式¥xx.xx如此格式 + * + * @param price 单位为元的金额 + * @return String + */ + public static String formatPriceT(double price) { + if (price <= 0) { + return "0.00"; + } + DecimalFormat format = new DecimalFormat("0.00"); + format.setRoundingMode(RoundingMode.FLOOR); + return String.format("%s", format.format(price)); + } + /** + * 将金钱转成显示的格式¥xx.xx如此格式 + * + * @param textPrice 单位为分的金额 + * @return String + */ + public static String formatPriceT(String textPrice) { + return formatPriceT(Double.parseDouble(textPrice) / 10000); + } + } diff --git a/common_base/src/main/java/com/wss/common/utils/NetworkUtil.java b/common_base/src/main/java/com/wss/common/utils/NetworkUtil.java index d2fa331..047924f 100644 --- a/common_base/src/main/java/com/wss/common/utils/NetworkUtil.java +++ b/common_base/src/main/java/com/wss/common/utils/NetworkUtil.java @@ -6,8 +6,15 @@ import android.net.NetworkInfo; import android.telephony.TelephonyManager; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Enumeration; import java.util.List; +import androidx.annotation.NonNull; + /** * Describe:网络状态工具类 * Created by 吴天强 on 2018/10/25. @@ -18,18 +25,15 @@ public class NetworkUtil { * 是否有可用网络 * * @param context context - * @return + * @return boolean */ - public static boolean isNetworkAvailable(Context context) { - ConnectivityManager connectivity = (ConnectivityManager) context - .getSystemService(Context.CONNECTIVITY_SERVICE); + public static boolean isNetworkEnabled(@NonNull Context context) { + ConnectivityManager connectivity = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); if (connectivity != null) { NetworkInfo[] info = connectivity.getAllNetworkInfo(); - if (info != null) { - for (int i = 0; i < info.length; i++) { - if (info[i].getState() == NetworkInfo.State.CONNECTED) { - return true; - } + for (NetworkInfo networkInfo : info) { + if (networkInfo.getState() == NetworkInfo.State.CONNECTED) { + return true; } } } @@ -37,60 +41,97 @@ public static boolean isNetworkAvailable(Context context) { } /** - * gps能用? + * gps能用 * - * @param context context - * @return + * @param context ctx + * @return boolean */ - public static boolean isGpsEnabled(Context context) { - LocationManager locationManager = ((LocationManager) context - .getSystemService(Context.LOCATION_SERVICE)); - List accessibleProviders = locationManager.getProviders(true); - return accessibleProviders != null && accessibleProviders.size() > 0; + public static boolean isGpsEnabled(@NonNull Context context) { + LocationManager locationManager = ((LocationManager) context.getSystemService(Context.LOCATION_SERVICE)); + if (locationManager != null) { + List accessibleProviders = locationManager.getProviders(true); + return accessibleProviders.size() > 0; + } + return false; } /** * Wifi是否可用 * * @param context context - * @return + * @return boolean */ - public static boolean isWifiEnabled(Context context) { - ConnectivityManager mgrConn = (ConnectivityManager) context - .getSystemService(Context.CONNECTIVITY_SERVICE); - TelephonyManager mgrTel = (TelephonyManager) context - .getSystemService(Context.TELEPHONY_SERVICE); - return ((mgrConn.getActiveNetworkInfo() != null && mgrConn - .getActiveNetworkInfo().getState() == NetworkInfo.State.CONNECTED) || mgrTel - .getNetworkType() == TelephonyManager.NETWORK_TYPE_UMTS); + public static boolean isWifiEnabled(@NonNull Context context) { + ConnectivityManager mgrConn = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + if (mgrConn != null && mgrConn.getActiveNetworkInfo() != null && + mgrConn.getActiveNetworkInfo().getState() == NetworkInfo.State.CONNECTED) { + return true; + } + TelephonyManager mgrTel = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + return mgrTel != null && mgrTel.getNetworkType() == TelephonyManager.NETWORK_TYPE_UMTS; } /** * 当前网络是否为Wifi * * @param context context - * @return + * @return boolean */ - public static boolean isWifi(Context context) { - ConnectivityManager connectivityManager = (ConnectivityManager) context - .getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo(); - return activeNetInfo != null - && activeNetInfo.getType() == ConnectivityManager.TYPE_WIFI; + public static boolean isWifi(@NonNull Context context) { + ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + if (connectivityManager != null) { + NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo(); + return activeNetInfo != null && activeNetInfo.getType() == ConnectivityManager.TYPE_WIFI; + } + return false; } /** - * 当前网络是否为收集数据 + * 当前网络是否为移动数据 * * @param context context - * @return + * @return boolean + */ + public static boolean isMobile(@NonNull Context context) { + ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + if (connectivityManager != null) { + NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo(); + return activeNetInfo != null && activeNetInfo.getType() == ConnectivityManager.TYPE_MOBILE; + } + return false; + } + + /** + * 获取当前网络IP + * + * @return 网络IP + */ + public static String getIpAddress() { + try { + for (Enumeration enNetI = NetworkInterface.getNetworkInterfaces(); enNetI + .hasMoreElements(); ) { + NetworkInterface netI = enNetI.nextElement(); + for (Enumeration enumIpAddr = netI.getInetAddresses(); enumIpAddr.hasMoreElements(); ) { + InetAddress inetAddress = enumIpAddr.nextElement(); + if (inetAddress instanceof Inet4Address && !inetAddress.isLoopbackAddress()) { + return inetAddress.getHostAddress(); + } + } + } + } catch (SocketException e) { + e.printStackTrace(); + } + return ""; + } + + /** + * 检查是否为连接 + * + * @param url url + * @return boolean */ - public static boolean is3G(Context context) { - ConnectivityManager connectivityManager = (ConnectivityManager) context - .getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo(); - return activeNetInfo != null - && activeNetInfo.getType() == ConnectivityManager.TYPE_MOBILE; + public static boolean isLink(String url) { + return ValidUtils.isValid(url) && (url.startsWith("http") || url.startsWith("https")); } } diff --git a/common_base/src/main/java/com/wss/common/utils/PermissionsUtils.java b/common_base/src/main/java/com/wss/common/utils/PermissionsUtils.java index c9b66dd..d941132 100644 --- a/common_base/src/main/java/com/wss/common/utils/PermissionsUtils.java +++ b/common_base/src/main/java/com/wss/common/utils/PermissionsUtils.java @@ -1,59 +1,137 @@ package com.wss.common.utils; +import android.Manifest; import android.app.Activity; -import android.content.pm.PackageManager; -import android.os.Build; -import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; -import java.util.ArrayList; +import com.hjq.permissions.OnPermission; +import com.hjq.permissions.Permission; +import com.hjq.permissions.XXPermissions; +import com.wss.common.base.R; + import java.util.List; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; + /** - * Describe:6.0动态权限管理帮助类 + * Describe:6.0 + 动态权限管理帮助类 * Created by 吴天强 on 2018/10/25. */ - public class PermissionsUtils { + /** + * 授权所有权限 + * + * @param activity activity + */ + public static void authorizationAllPermissions(Activity activity) { + XXPermissions.with(activity) + .request(new OnPermission() { + @Override + public void hasPermission(List granted, boolean isAll) { + } + + @Override + public void noPermission(List denied, boolean quick) { + } + }); + } /** - * 判断权限 + * 检查权限 * - * @param context context - * @param permissions 权限列表 + * @param activity activity + * @param permissions 权限组 + * @return Observable */ - public static void checkPermissions(Activity context, String... permissions) { + public static Observable checkPermissions(Activity activity, String... permissions) { + return Observable.create( + subscriber -> { + if (XXPermissions.isHasPermission(activity, permissions)) { + subscriber.onNext(true); + } else { + XXPermissions.with(activity) + .permission(permissions) + .request(new OnPermission() { + @Override + public void hasPermission(List granted, boolean isAll) { + subscriber.onNext(isAll); + } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - List permissionsList = new ArrayList<>(); - if (permissions != null && permissions.length != 0) { - for (String permission : permissions) { - if (!isHavePermissions(context, permission)) { - permissionsList.add(permission); + @Override + public void noPermission(List denied, boolean quick) { + subscriber.onNext(false); + ToastUtils.show(activity, activity.getString(R.string.authorise_necessary_authorities)); + } + }); } - } - // 遍历完后申请 - applyPermissions(context, permissionsList); - } - } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + + + /** + * 检查相机权限 + * + * @param activity activity + * @return Observable + */ + public static Observable checkCamera(Activity activity) { + return checkPermissions(activity, Permission.CAMERA, Permission.WRITE_EXTERNAL_STORAGE); + } + + + /** + * 检查文件读写权限 + * + * @param activity activity + * @return boolean + */ + public static Observable checkStorage(Activity activity) { + String[] storage = { + Permission.READ_EXTERNAL_STORAGE, + Permission.WRITE_EXTERNAL_STORAGE}; + return checkPermissions(activity, storage); + } + + + /** + * 检查录音相关权限 + */ + public static Observable checkRecord(Activity activity) { + return checkPermissions(activity, Permission.READ_EXTERNAL_STORAGE, + Permission.WRITE_EXTERNAL_STORAGE, + Permission.RECORD_AUDIO); } /** - * 检查是否授权某权限 + * 检查拨打电话相关权限 */ - private static boolean isHavePermissions(Activity context, String permissions) { - return ContextCompat.checkSelfPermission(context, permissions) == PackageManager.PERMISSION_GRANTED; + public static Observable checkCallPhone(Activity activity) { + return checkPermissions(activity, Permission.CALL_PHONE); } /** - * 申请权限 + * 检查定位权限 + * + * @param activity activity + * @return boolean */ - private static void applyPermissions(Activity context, List permissions) { - if (!permissions.isEmpty()) { - ActivityCompat.requestPermissions(context, permissions.toArray(new String[permissions.size()]), 1); - } + public static Observable checkLocation(Activity activity) { + return checkPermissions(activity, Permission.ACCESS_FINE_LOCATION, + Permission.ACCESS_COARSE_LOCATION, Permission.RECORD_AUDIO); } + /** + * 允许锁屏状态下,app在后台运行 + * + * @param activity activity + * @return boolean + */ + public static Observable checkWakeLock(Activity activity) { + return checkPermissions(activity, Manifest.permission.WAKE_LOCK); + } } diff --git a/common_base/src/main/java/com/wss/common/utils/PhoneUtils.java b/common_base/src/main/java/com/wss/common/utils/PhoneUtils.java new file mode 100644 index 0000000..a47b278 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/PhoneUtils.java @@ -0,0 +1,46 @@ +package com.wss.common.utils; + +import android.annotation.SuppressLint; +import android.content.Intent; +import android.net.Uri; + +import com.wss.common.base.BaseApplication; + +/** + * description: 拨打电话工具类 + * + * @author 杨伟-tony + * create by 2020/5/22 11:26 + */ +public class PhoneUtils { + /** + * 拨打电话 + * ps:调起拨号盘 + * + * @param phoneNum 电话号码 + */ + public static void callPhoneWaitFor(String phoneNum) { + Intent intent = new Intent(Intent.ACTION_DIAL); + Uri data = Uri.parse("tel:" + phoneNum); + intent.setData(data); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + BaseApplication.i().startActivity(intent); + + } + + /** + * 拨打电话 + * 吊起拨号盘 直接拨打电话 + * + * @param phoneNum 电话号码 + */ + @SuppressLint("MissingPermission") + public static void callPhoneDirect(String phoneNum) { + Intent intent = new Intent(Intent.ACTION_CALL); + Uri data = Uri.parse("tel:" + phoneNum); + intent.setData(data); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + BaseApplication.i().startActivity(intent); + } + +} diff --git a/common_base/src/main/java/com/wss/common/utils/PxUtils.java b/common_base/src/main/java/com/wss/common/utils/PxUtils.java index 005da2a..6522fa6 100644 --- a/common_base/src/main/java/com/wss/common/utils/PxUtils.java +++ b/common_base/src/main/java/com/wss/common/utils/PxUtils.java @@ -1,29 +1,43 @@ package com.wss.common.utils; import android.content.Context; +import android.graphics.Paint; +import android.view.View; + +import com.wss.common.base.BaseApplication; + +import androidx.annotation.NonNull; /** * Describe:尺寸工具类 * Created by 吴天强 on 2017/9/19. */ - public class PxUtils { /** * 得到设备屏幕的宽度 + * + * @param context ctx + * @return int */ - public static int getScreenWidth(Context context) { + public static int getScreenWidth(@NonNull Context context) { return context.getResources().getDisplayMetrics().widthPixels; } /** * 得到设备屏幕的高度 + * + * @param context ctx + * @return int */ - public static int getScreenHeight(Context context) { + public static int getScreenHeight(@NonNull Context context) { return context.getResources().getDisplayMetrics().heightPixels; } /** * 得到设备的密度 + * + * @param context ctx + * @return float */ public static float getScreenDensity(Context context) { return context.getResources().getDisplayMetrics().density; @@ -31,33 +45,64 @@ public static float getScreenDensity(Context context) { /** * 把密度转换为像素 + * + * @param dpValue dp值 + * @return int */ - public static int dp2px(Context context, float dipValue) { - final float scale = getScreenDensity(context); - return (int) (dipValue * scale + 0.5); + public static int dp2px(float dpValue) { + float scale = getScreenDensity(BaseApplication.i()); + return (int) (dpValue * scale + 0.5); } /** * 将像素转换成dp * - * @param context - * @param pxValue - * @return + * @param pxValue px值 + * @return int */ - public static int px2dp(Context context, float pxValue) { - final float scale = context.getResources().getDisplayMetrics().density; + public static int px2dp(float pxValue) { + float scale = getScreenDensity(BaseApplication.i()); return (int) (pxValue / scale + 0.5f); } /** * 将sp值转换为px值,保证文字大小不变 * - * @param context context - * @param spValue - * @return + * @param spValue sp值 + * @return int */ - public static int sp2px(Context context, float spValue) { - final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; + public static int sp2px(float spValue) { + float fontScale = getScreenDensity(BaseApplication.i()); return (int) (spValue * fontScale + 0.5f); } + + /** + * 测量 View + * + * @param measureSpec + * @param defaultSize View 的默认大小 + * @return + */ + public static int measure(int measureSpec, int defaultSize) { + int result = defaultSize; + int specMode = View.MeasureSpec.getMode(measureSpec); + int specSize = View.MeasureSpec.getSize(measureSpec); + + if (specMode == View.MeasureSpec.EXACTLY) { + result = specSize; + } else if (specMode == View.MeasureSpec.AT_MOST) { + result = Math.min(result, specSize); + } + return result; + } + /** + * 测量文字高度 + * + * @param paint + * @return + */ + public static float measureTextHeight(Paint paint) { + Paint.FontMetrics fontMetrics = paint.getFontMetrics(); + return (Math.abs(fontMetrics.ascent) - fontMetrics.descent); + } } diff --git a/common_base/src/main/java/com/wss/common/utils/RomUtil.java b/common_base/src/main/java/com/wss/common/utils/RomUtil.java new file mode 100644 index 0000000..ecc138b --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/RomUtil.java @@ -0,0 +1,152 @@ +package com.wss.common.utils; + +import android.os.Build; +import android.text.TextUtils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +/** + * Describe:判断手机ROM类型 + * Created by 吴天强 on 2020/6/24. + */ +public class RomUtil { + + public static final String ROM_MIUI = "MIUI"; + public static final String ROM_EMUI = "EMUI"; + public static final String ROM_FLYME = "FLYME"; + public static final String ROM_OPPO = "OPPO"; + public static final String ROM_SMARTISAN = "SMARTISAN"; + public static final String ROM_VIVO = "VIVO"; + public static final String ROM_QIKU = "QIKU"; + + private static final String KEY_VERSION_MIUI = "ro.miui.ui.version.name"; + private static final String KEY_VERSION_EMUI = "ro.build.version.emui"; + private static final String KEY_VERSION_OPPO = "ro.build.version.opporom"; + private static final String KEY_VERSION_SMARTISAN = "ro.smartisan.version"; + private static final String KEY_VERSION_VIVO = "ro.vivo.os.version"; + + private static String sName; + private static String sVersion; + + /** + * 华为 + * + * @return boolean + */ + public static boolean isEmui() { + return check(ROM_EMUI); + } + + /** + * 小米 + * + * @return boolean + */ + public static boolean isMiui() { + return check(ROM_MIUI); + } + + /** + * vivo + * + * @return boolean + */ + public static boolean isVivo() { + return check(ROM_VIVO); + } + + /** + * oppo + * + * @return boolean + */ + public static boolean isOppo() { + return check(ROM_OPPO); + } + + /** + * 魅族 + * + * @return boolean + */ + public static boolean isFlyme() { + return check(ROM_FLYME); + } + + /** + * 360手机 + * + * @return boolean + */ + public static boolean is360() { + return check(ROM_QIKU) || check("360"); + } + + public static boolean isSmartisan() { + return check(ROM_SMARTISAN); + } + + public static String getName() { + if (sName == null) { + check(""); + } + return sName; + } + + public static String getVersion() { + if (sVersion == null) { + check(""); + } + return sVersion; + } + + public static boolean check(String rom) { + if (sName != null) { + return sName.equals(rom); + } + if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_MIUI))) { + sName = ROM_MIUI; + } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_EMUI))) { + sName = ROM_EMUI; + } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_OPPO))) { + sName = ROM_OPPO; + } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_VIVO))) { + sName = ROM_VIVO; + } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_SMARTISAN))) { + sName = ROM_SMARTISAN; + } else { + sVersion = Build.DISPLAY; + if (sVersion.toUpperCase().contains(ROM_FLYME)) { + sName = ROM_FLYME; + } else { + sVersion = Build.UNKNOWN; + sName = Build.MANUFACTURER.toUpperCase(); + } + } + return sName.equals(rom); + } + + public static String getProp(String name) { + String line = null; + BufferedReader input = null; + try { + Process p = Runtime.getRuntime().exec("getprop " + name); + input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024); + line = input.readLine(); + input.close(); + } catch (IOException ex) { + return null; + } finally { + if (input != null) { + try { + input.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return line; + } +} diff --git a/common_base/src/main/java/com/wss/common/utils/SelectorPopupWindow.java b/common_base/src/main/java/com/wss/common/utils/SelectorPopupWindow.java new file mode 100644 index 0000000..3cb335c --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/SelectorPopupWindow.java @@ -0,0 +1,175 @@ +package com.wss.common.utils; + +import android.content.Context; +import android.graphics.Rect; +import android.graphics.drawable.ColorDrawable; +import android.os.Build; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; +import android.widget.PopupWindow; +import android.widget.TextView; + +import com.wss.common.base.R; +import com.wss.common.base.adapter.BaseListAdapter; +import com.wss.common.base.adapter.listener.OnListItemClickListener; +import com.wss.common.bean.SelectorData; + +import org.byteam.superadapter.SuperViewHolder; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +/** + * Describe:弹窗选择器 + * Created by 吴天强 on 2020/4/21. + */ +public class SelectorPopupWindow extends PopupWindow { + + private Context context; + private View parent; + private OnItemClickListener itemClickListener; + private PopupWindow.OnDismissListener dismissListener; + private List dataList = new ArrayList<>(); + private String defaultChecked; + + /** + * @param context context + * @param parent 显示位置父控件 + */ + public SelectorPopupWindow(Context context, View parent, List dataList) { + this.context = context; + this.parent = parent; + this.dataList.addAll(dataList); + } + + + /** + * 弹窗消失监听 + * + * @param dismissListener 监听 + * @return SelectorPopupWindow + */ + public SelectorPopupWindow setDismissListener(PopupWindow.OnDismissListener dismissListener) { + this.dismissListener = dismissListener; + return this; + } + + /** + * Item点击事件 + * + * @param itemClickListener 事件 + */ + public SelectorPopupWindow setOnItemClickListener(OnItemClickListener itemClickListener) { + this.itemClickListener = itemClickListener; + return this; + } + + /** + * 默认选择项 + * + * @param defaultChecked 默认值 + * @return SelectorPopupWindow + */ + public SelectorPopupWindow setDefaultChecked(String defaultChecked) { + this.defaultChecked = defaultChecked; + return this; + } + + public void show() { + initView(ViewGroup.LayoutParams.MATCH_PARENT); + } + + /** + * 显示自定义宽度值 + * + * @param value 宽度值 + */ + public void showMonthPopup(int value) { + initView(PxUtils.dp2px(value)); + } + + private void initView(int width) { + View childView = View.inflate(context, R.layout.pop_selector, null); + childView.findViewById(R.id.close).setOnClickListener(v -> dismiss()); + initItems(childView); + setWidth(width); + setHeight(ViewGroup.LayoutParams.MATCH_PARENT); + ColorDrawable dw = new ColorDrawable(0); + setBackgroundDrawable(dw); + setFocusable(true); + setOutsideTouchable(true); + setContentView(childView); + showAsDropDown(parent); + update(); + setOnDismissListener(dismissListener); + } + + private void initItems(@NotNull View childView) { + RecyclerView recyclerView = childView.findViewById(R.id.recycle_view); + recyclerView.setLayoutManager(new LinearLayoutManager(context)); + recyclerView.setAdapter(new SelectorAdapter(context, dataList, R.layout.item_of_selector_pop, + (data, position) -> { + if (itemClickListener != null) { + itemClickListener.onItemClick(data); + } + dismiss(); + }, defaultChecked)); + } + + + @Override + public void showAsDropDown(View anchor) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + Rect visibleFrame = new Rect(); + anchor.getGlobalVisibleRect(visibleFrame); + int height; + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O_MR1 && !RomUtil.isVivo()) { + //9.0 + height = anchor.getResources().getDisplayMetrics().heightPixels - (visibleFrame.bottom - PxUtils.dp2px(30)); + } else { + height = anchor.getResources().getDisplayMetrics().heightPixels - visibleFrame.bottom; + } + setHeight(height); + } + super.showAsDropDown(anchor); + } + + /** + * 列表适配器 + */ + private static class SelectorAdapter extends BaseListAdapter { + + private String defaultChecked; + + SelectorAdapter(Context context, List mData, int layoutResId, OnListItemClickListener listener, String defaultChecked) { + super(context, mData, layoutResId, listener); + this.defaultChecked = defaultChecked; + } + + @Override + public void onBindData(@NotNull SuperViewHolder holder, int viewType, int layoutPosition, @NotNull SelectorData data) { + TextView tvName = holder.findViewById(R.id.tv_name); + tvName.setText(data.getName()); + tvName.setSelected(TextUtils.equals(defaultChecked, data.getName())); + } + } + + + /** + * Item点击事件监听 + */ + public interface OnItemClickListener { + + /** + * Item点击回调 + * + * @param data 选择数据 + */ + void onItemClick(SelectorData data); + } +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/utils/StringUtils.java b/common_base/src/main/java/com/wss/common/utils/StringUtils.java deleted file mode 100644 index 55a80df..0000000 --- a/common_base/src/main/java/com/wss/common/utils/StringUtils.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.wss.common.utils; - -/** - * Describe:字符串工具类 - * Created by 吴天强 on 2018/10/17. - */ - -public class StringUtils { - - -} diff --git a/common_base/src/main/java/com/wss/common/utils/SystemUtils.java b/common_base/src/main/java/com/wss/common/utils/SystemUtils.java new file mode 100644 index 0000000..54c55a1 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/SystemUtils.java @@ -0,0 +1,247 @@ +package com.wss.common.utils; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.os.Build; +import android.provider.Settings; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.security.MessageDigest; +import java.util.Locale; +import java.util.UUID; + +/** + * Describe:系统工具类 + * Created by 吴天强 on 2019/5/5. + */ +public class SystemUtils { + /** + * 获取当前手机系统语言。 + * + * @return 返回当前系统语言。例如:当前设置的是“中文-中国”,则返回“zh-CN” + */ + @NotNull + public static String getSystemLanguage() { + return Locale.getDefault().getLanguage(); + } + + /** + * 获取当前系统上的语言列表(Locale列表) + * + * @return 语言列表 + */ + @NotNull + public static Locale[] getSystemLanguageList() { + return Locale.getAvailableLocales(); + } + + /** + * 获取当前手机系统版本号 + * + * @return 系统版本号 + */ + @Contract(pure = true) + public static String getSystemVersion() { + return android.os.Build.VERSION.RELEASE; + } + + /** + * 获取手机型号 + * + * @return 手机型号 + */ + @Contract(pure = true) + public static String getSystemModel() { + return android.os.Build.MODEL; + } + + /** + * 获取手机厂商 + * + * @return 手机厂商 + */ + @Contract(pure = true) + public static String getDeviceBrand() { + return android.os.Build.BRAND; + } + + /** + * 获取手机IMEI(需要“android.permission.READ_PHONE_STATE”权限) + * + * @return 手机IMEI + */ + @Nullable + public static String getDeviceId(@NotNull Context context) { + StringBuilder sbDeviceId = new StringBuilder(); + + //获得设备默认IMEI(>=6.0 需要ReadPhoneState权限) + //获得AndroidId(无需权限) + String androidId = getAndroidId(context); + //获得设备序列号(无需权限) + String serial = getSerial(); + //获得硬件uuid(根据硬件相关属性,生成uuid)(无需权限) + String uuid = getDeviceUUID().replace("-", ""); + //追加androidId + if (androidId != null && androidId.length() > 0) { + sbDeviceId.append(androidId); + sbDeviceId.append("|"); + } + //追加serial + if (serial != null && serial.length() > 0) { + sbDeviceId.append(serial); + sbDeviceId.append("|"); + } + //追加硬件uuid + if (uuid.length() > 0) { + sbDeviceId.append(uuid); + } + + //生成SHA1,统一DeviceId长度 + if (sbDeviceId.length() > 0) { + try { + byte[] hash = getHashByString(sbDeviceId.toString()); + String sha1 = bytesToHex(hash); + if (sha1.length() > 0) { + //返回最终的DeviceId + return sha1; + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + //如果以上硬件标识数据均无法获得, + //则DeviceId默认使用系统随机数,这样保证DeviceId不为空 + return UUID.randomUUID().toString().replace("-", ""); + } + + + /** + * 获得设备的AndroidId + * + * @param context 上下文 + * @return 设备的AndroidId + */ + @SuppressLint("HardwareIds") + private static String getAndroidId(Context context) { + try { + return Settings.Secure.getString(context.getContentResolver(), + Settings.Secure.ANDROID_ID); + } catch (Exception ex) { + ex.printStackTrace(); + } + return ""; + } + + /** + * 获得设备序列号(如:WTK7N16923005607), 个别设备无法获取 + * + * @return 设备序列号 + */ + private static String getSerial() { + try { + return Build.SERIAL; + } catch (Exception ex) { + ex.printStackTrace(); + } + return ""; + } + + /** + * 获得设备硬件uuid + * 使用硬件信息,计算出一个随机数 + * + * @return 设备硬件uuid + */ + @NotNull + private static String getDeviceUUID() { + try { + String dev = "3883756" + + Build.BOARD.length() % 10 + + Build.BRAND.length() % 10 + + Build.DEVICE.length() % 10 + + Build.HARDWARE.length() % 10 + + Build.ID.length() % 10 + + Build.MODEL.length() % 10 + + Build.PRODUCT.length() % 10 + + Build.SERIAL.length() % 10; + return new UUID(dev.hashCode(), + Build.SERIAL.hashCode()).toString(); + } catch (Exception ex) { + ex.printStackTrace(); + return ""; + } + } + + /** + * 取SHA1 + * + * @param data 数据 + * @return 对应的hash值 + */ + private static byte[] getHashByString(String data) { + try { + MessageDigest messageDigest = MessageDigest.getInstance("SHA1"); + messageDigest.reset(); + messageDigest.update(data.getBytes("UTF-8")); + return messageDigest.digest(); + } catch (Exception e) { + return "".getBytes(); + } + } + + /** + * 转16进制字符串 + * + * @param data 数据 + * @return 16进制字符串 + */ + @NotNull + private static String bytesToHex(@NotNull byte[] data) { + StringBuilder sb = new StringBuilder(); + String stmp; + for (byte datum : data) { + stmp = (Integer.toHexString(datum & 0xFF)); + if (stmp.length() == 1) { + sb.append("0"); + } + sb.append(stmp); + } + return sb.toString().toUpperCase(Locale.CHINA); + } + + + public static int getStatusBarHeight(@NotNull Context context) { + int result = 0; + int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + result = context.getResources().getDimensionPixelSize(resourceId); + } + return result; + } + + public static int getScreenWidth(Context context) { + int screenWith = -1; + try { + screenWith = context.getResources().getDisplayMetrics().widthPixels; + } catch (Exception e) { + e.printStackTrace(); + } + return screenWith; + } + + public static int getScreenHeight(Context context) { + int screenHeight = -1; + try { + screenHeight = context.getResources().getDisplayMetrics().heightPixels; + } catch (Exception e) { + e.printStackTrace(); + } + return screenHeight; + } + + +} diff --git a/common_base/src/main/java/com/wss/common/utils/ToastUtils.java b/common_base/src/main/java/com/wss/common/utils/ToastUtils.java index bf07c16..4ed6680 100644 --- a/common_base/src/main/java/com/wss/common/utils/ToastUtils.java +++ b/common_base/src/main/java/com/wss/common/utils/ToastUtils.java @@ -1,40 +1,382 @@ package com.wss.common.utils; +import android.app.AppOpsManager; +import android.app.Application; +import android.app.NotificationManager; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.res.Resources; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; import android.widget.Toast; +import com.wss.common.utils.toast.BaseToast; +import com.wss.common.utils.toast.IToastInterceptor; +import com.wss.common.utils.toast.IToastStrategy; +import com.wss.common.utils.toast.IToastStyle; +import com.wss.common.utils.toast.SafeToast; +import com.wss.common.utils.toast.SupportToast; +import com.wss.common.utils.toast.ToastInterceptor; +import com.wss.common.utils.toast.ToastStrategy; +import com.wss.common.utils.toast.style.ToastBlackStyle; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + /** * Describe:Toast * Created by 吴天强 on 2017/11/2. */ +public final class ToastUtils { + + private static IToastInterceptor sInterceptor; + + private static IToastStrategy sStrategy; + + private static Toast sToast; + + /** + * 不允许外部实例化 + */ + private ToastUtils() { + } + + /** + * 初始化 ToastUtils,在 Application 中初始化 + * + * @param application 应用的上下文 + */ + public static void init(Application application) { + init(application, new ToastBlackStyle(application)); + } + + /** + * 初始化 ToastUtils 及样式 + * + * @param application 应用的上下文 + * @param style Toast样式 + */ + public static void init(Application application, IToastStyle style) { + checkNullPointer(application); + // 初始化 Toast 拦截器 + if (sInterceptor == null) { + setToastInterceptor(new ToastInterceptor()); + } + + // 初始化 Toast 显示处理器 + if (sStrategy == null) { + setToastStrategy(new ToastStrategy()); + } + + // 初始化吐司 + if (areNotificationsEnabled(application)) { + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1) { + // 解决 Android 7.1 上主线程被阻塞后吐司会报错的问题 + setToast(new SafeToast(application)); + } else { + setToast(new BaseToast(application)); + } + } else { + // 解决关闭通知栏权限后 Toast 不显示的问题 + setToast(new SupportToast(application)); + } + + // 设置 Toast 视图 + setView(createTextView(application, style)); + + // 设置 Toast 重心 + setGravity(style.getGravity(), style.getXOffset(), style.getYOffset()); + } + + /** + * 显示一个对象的吐司 + * + * @param object 对象 + */ + public static void show(Object object) { + show(object != null ? object.toString() : "null"); + } + + /** + * 显示一个吐司 + * + * @param id 如果传入的是正确的 string id 就显示对应字符串 + * 如果不是则显示一个整数的string + */ + public static void show(int id) { + checkToastState(); + try { + // 如果这是一个资源 id + show(getContext().getResources().getText(id)); + } catch (Resources.NotFoundException ignored) { + // 如果这是一个 int 整数 + show(String.valueOf(id)); + } + } + + /** + * 显示一个吐司 + * + * @param id 资源 id + * @param args 参数集 + */ + public static void show(int id, Object... args) { + show(getContext().getResources().getString(id), args); + } + + /** + * 显示一个吐司 + * + * @param format 原字符串 + * @param args 参数集 + */ + public static void show(String format, Object... args) { + show(String.format(format, args)); + } + + /** + * 显示一个吐司 + * + * @param text 需要显示的文本 + */ + public static synchronized void show(CharSequence text) { + checkToastState(); + if (sInterceptor.intercept(sToast, text)) { + return; + } + sStrategy.show(text); + } + -public class ToastUtils { + //***************************************兼容老的Toast的调用方法 start************************************* - public static void showToast(Context context, int strings) { - showToast(context, context.getString(strings)); + /** + * 老的Toast调用方法 + * + * @param context context + * @param text 显示的内容 + */ + public static void show(Context context, String text) { + show(text); } + //***************************************兼容老的Toast的调用方法 end************************************* - public static void showToast(Context context, String text) { -// showToast(context, title, Gravity.BOTTOM); - Toast.makeText(context, text, Toast.LENGTH_SHORT).show(); + /** + * 取消吐司的显示 + */ + public static synchronized void cancel() { + checkToastState(); + + sStrategy.cancel(); } /** - * 自定义显示Toast弹出提示框 + * 设置吐司的位置 * - * @param s s - * @param gravity 弹出位置 + * @param gravity 重心 + * @param xOffset x轴偏移 + * @param yOffset y轴偏移 + */ + private static void setGravity(int gravity, int xOffset, int yOffset) { + checkToastState(); + gravity = Gravity.getAbsoluteGravity(gravity, sToast.getView().getResources().getConfiguration().getLayoutDirection()); + sToast.setGravity(gravity, xOffset, yOffset); + } + + /** + * 给当前Toast设置新的布局,具体实现可看{@link BaseToast#setView(View)} + */ + public static void setView(int id) { + checkToastState(); + + setView(View.inflate(getContext(), id, null)); + } + + public static void setView(View view) { + checkToastState(); + + // 这个 View 不能为空 + checkNullPointer(view); + + // 当前必须用 Application 的上下文创建的 View,否则可能会导致内存泄露 + Context context = view.getContext(); + if (!(context instanceof Application)) { + throw new IllegalArgumentException("The view must be initialized using the context of the application"); + } + + // 如果吐司已经创建,就重新初始化吐司 + if (sToast != null) { + // 取消原有吐司的显示 + sToast.cancel(); + sToast.setView(view); + } + } + + /** + * 获取当前 Toast 的视图 + */ + @SuppressWarnings("unchecked") + public static V getView() { + checkToastState(); + + return (V) sToast.getView(); + } + + /** + * 初始化全局的Toast样式 + * + * @param style 样式实现类,框架已经实现三种不同的样式 + */ + public static void initStyle(IToastStyle style) { + checkNullPointer(style); + // 如果吐司已经创建,就重新初始化吐司 + if (sToast != null) { + // 取消原有吐司的显示 + sToast.cancel(); + sToast.setView(createTextView(getContext(), style)); + sToast.setGravity(style.getGravity(), style.getXOffset(), style.getYOffset()); + } + } + + /** + * 设置当前Toast对象 + */ + public static void setToast(Toast toast) { + checkNullPointer(toast); + if (sToast != null && toast.getView() == null) { + // 移花接木 + toast.setView(sToast.getView()); + toast.setGravity(sToast.getGravity(), sToast.getXOffset(), sToast.getYOffset()); + toast.setMargin(sToast.getHorizontalMargin(), sToast.getVerticalMargin()); + } + sToast = toast; + if (sStrategy != null) { + sStrategy.bind(sToast); + } + } + + /** + * 设置 Toast 显示策略 + */ + public static void setToastStrategy(IToastStrategy handler) { + checkNullPointer(handler); + sStrategy = handler; + if (sToast != null) { + sStrategy.bind(sToast); + } + } + + /** + * 设置 Toast 拦截器(可以根据显示的内容决定是否拦截这个Toast) + * 场景:打印 Toast 内容日志、根据 Toast 内容是否包含敏感字来动态切换其他方式显示(这里可以使用我的另外一套框架 XToast) + */ + public static void setToastInterceptor(IToastInterceptor interceptor) { + checkNullPointer(interceptor); + sInterceptor = interceptor; + } + + /** + * 获取当前Toast对象 + */ + public static Toast getToast() { + return sToast; + } + + /** + * 检查吐司状态,如果未初始化请先调用{@link com.wss.common.utils.ToastUtils#init(Application)} + */ + private static void checkToastState() { + // 吐司工具类还没有被初始化,必须要先调用init方法进行初始化 + if (sToast == null) { + throw new IllegalStateException("ToastUtils has not been initialized"); + } + } + + /** + * 检查对象是否为空 + */ + private static void checkNullPointer(Object object) { + if (object == null) { + throw new NullPointerException("are you ok?"); + } + } + + /** + * 生成默认的 TextView 对象 + */ + private static TextView createTextView(Context context, IToastStyle style) { + + GradientDrawable drawable = new GradientDrawable(); + // 设置背景色 + drawable.setColor(style.getBackgroundColor()); + // 设置圆角大小 + drawable.setCornerRadius(style.getCornerRadius()); + + TextView textView = new TextView(context); + textView.setId(android.R.id.message); + textView.setTextColor(style.getTextColor()); + textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, style.getTextSize()); + + // 适配布局反方向 + textView.setPaddingRelative(style.getPaddingStart(), style.getPaddingTop(), style.getPaddingEnd(), style.getPaddingBottom()); + + textView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + + // setBackground API 版本兼容 + textView.setBackground(drawable); + + // 设置 Z 轴阴影 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + textView.setZ(style.getZ()); + } + + // 设置最大显示行数 + if (style.getMaxLines() > 0) { + textView.setMaxLines(style.getMaxLines()); + } + + return textView; + } + + /** + * 获取上下文对象 + */ + private static Context getContext() { + checkToastState(); + return sToast.getView().getContext(); + } + + /** + * 检查通知栏权限有没有开启 + * 参考 SupportCompat 包中的方法: NotificationManagerCompat.from(context).areNotificationsEnabled(); */ + private static boolean areNotificationsEnabled(Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + return manager != null && manager.areNotificationsEnabled(); + } else { + AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); + ApplicationInfo appInfo = context.getApplicationInfo(); + String packageName = context.getApplicationContext().getPackageName(); + int uid = appInfo.uid; - public static void showToast(Context context, CharSequence s, int gravity) { -// Toast toast = new Toast(context); -// View toastView = View.inflate(context, R.layout.toast, null); -// toast.setView(toastView); -// toast.setDuration(Toast.LENGTH_SHORT); -// TextView textView = toastView.findViewById(R.id.tv_message); -// textView.setText(s); -// toast.setGravity(gravity, 0, 100); -// toast.show(); + try { + Class appOpsClass = Class.forName(AppOpsManager.class.getName()); + Method checkOpNoThrowMethod = appOpsClass.getMethod("checkOpNoThrow", Integer.TYPE, Integer.TYPE, String.class); + Field opPostNotificationValue = appOpsClass.getDeclaredField("OP_POST_NOTIFICATION"); + int value = Integer.parseInt(String.valueOf(opPostNotificationValue.get(Integer.class))); + return ((int) checkOpNoThrowMethod.invoke(appOps, value, uid, packageName) == AppOpsManager.MODE_ALLOWED); + } catch (ClassNotFoundException | NoSuchMethodException | NoSuchFieldException + | InvocationTargetException | IllegalAccessException | RuntimeException ignored) { + return true; + } + } } } diff --git a/common_base/src/main/java/com/wss/common/utils/Utils.java b/common_base/src/main/java/com/wss/common/utils/Utils.java index ad05281..63c460f 100644 --- a/common_base/src/main/java/com/wss/common/utils/Utils.java +++ b/common_base/src/main/java/com/wss/common/utils/Utils.java @@ -1,7 +1,18 @@ package com.wss.common.utils; import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.content.res.AssetManager; +import android.graphics.drawable.GradientDrawable; +import android.net.Uri; +import android.widget.TextView; + +import com.wss.common.base.BaseApplication; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; import java.io.BufferedReader; import java.io.IOException; @@ -9,23 +20,23 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import androidx.annotation.NonNull; + /** * Describe:工具类 * Created by 吴天强 on 2018/10/15. */ - public class Utils { - /** * 读取 asset下的文件 * * @param context context * @param fileName fileName - * @return + * @return String */ - public static String getAssetFileData(Context context, String fileName) { - + @NonNull + public static String getAssetFileData(@NotNull Context context, String fileName) { StringBuilder stringBuilder = new StringBuilder(); try { AssetManager assetManager = context.getAssets(); @@ -72,4 +83,122 @@ public static boolean isNumber(String str) { return isNum.matches(); } + /** + * 判断是否为电话,只校验位数 + * + * @param str str + * @return boolean + */ + public static boolean isPhone(String str) { + return ValidUtils.isValid(str) && str.length() == 11; + } + + /** + * 获取APP包名 + * + * @return String + */ + public static String getPackageName() { + return BaseApplication.i().getPackageName(); + } + + /** + * 获取APP 版本名称 + * + * @return String + */ + public static String getVersionName() { + PackageManager packageManager = BaseApplication.i().getPackageManager(); + try { + PackageInfo packageInfo = packageManager.getPackageInfo(getPackageName(), 0); + return packageInfo.versionName; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return ""; + } + + /** + * 获取APP 版本号 + * + * @return Integer + */ + public static Integer getVersionCode() { + PackageManager packageManager = BaseApplication.i().getPackageManager(); + try { + PackageInfo packageInfo = packageManager.getPackageInfo( + getPackageName(), 0); + return packageInfo.versionCode; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return 0; + } + + /** + * 设置TextView的背景色 + * + * @param textView textView + * @param color 颜色 + */ + public static void setTextViewDrawable(@NotNull TextView textView, int color) { + setTextViewDrawable(textView, color, 4); + } + + /** + * 设置TextView的背景色 + * + * @param textView textView + * @param color 颜色 + * @param radius 圆角角度 + */ + public static void setTextViewDrawable(@NotNull TextView textView, int color, float radius) { + GradientDrawable drawable = new GradientDrawable(); + drawable.setCornerRadius(radius); + drawable.setColor(color); + textView.setBackground(drawable); + } + + /** + * 跳转浏览器 + */ + public static void toSystemBrowser(@NonNull Context context, String url) { + //应用内下载失败 跳转浏览器下载 + Intent intent = new Intent(); + intent.setAction("android.intent.action.VIEW"); + Uri contentUrl = Uri.parse(url); + intent.setData(contentUrl); + context.startActivity(intent); + } + + + + + /** + * 电话号码中间替换成* + * + * @param phone 原电话 + * @return 替换后的电话 + */ + @Contract("_ -> param1") + public static String replacePhone(String phone) { + if (!isPhone(phone)) { + return phone; + } + String replace = phone.substring(3, 7); + return phone.replace(replace, "****"); + } + + /** + * 把等于null的字符串转成“” + * + * @param text 原字符串 + * @return 转换后字符串 + */ + public static String checkText(String text) { + if (!ValidUtils.isValid(text)) { + return ""; + } + return text; + } } diff --git a/common_base/src/main/java/com/wss/common/utils/ValidUtils.java b/common_base/src/main/java/com/wss/common/utils/ValidUtils.java new file mode 100644 index 0000000..822401d --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/ValidUtils.java @@ -0,0 +1,39 @@ +package com.wss.common.utils; + +import android.text.TextUtils; + +import org.jetbrains.annotations.Contract; + +import java.util.Collection; +import java.util.Map; + +/** + * 对象空判断验证类 + * Created by 吴天强 on 2017/12/3. + */ +public class ValidUtils { + @Contract(value = "null -> false", pure = true) + public static boolean isValid(Collection collection) { + return collection != null && !collection.isEmpty(); + } + + @Contract("null, _ -> false") + public static boolean isValid(Collection collection, int position) { + return collection != null && !collection.isEmpty() && collection.size() > position; + } + + @Contract(value = "null -> false", pure = true) + public static boolean isValid(Map map) { + return map != null && !map.isEmpty(); + } + + @Contract("null -> false") + public static boolean isValid(String str) { + return !TextUtils.isEmpty(str); + } + + @Contract(value = "null -> false; !null -> true", pure = true) + public static boolean isValid(Object obj) { + return obj != null; + } +} diff --git a/common_base/src/main/java/com/wss/common/utils/ZipUtils.java b/common_base/src/main/java/com/wss/common/utils/ZipUtils.java deleted file mode 100644 index eafd00a..0000000 --- a/common_base/src/main/java/com/wss/common/utils/ZipUtils.java +++ /dev/null @@ -1,390 +0,0 @@ -package com.wss.common.utils; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Enumeration; -import java.util.zip.ZipEntry; -import java.util.zip.ZipException; -import java.util.zip.ZipFile; -import java.util.zip.ZipOutputStream; - -/** - * Java utils 实现的Zip工具 - * - * @author miaowei - */ -public class ZipUtils { - - private static final int BUFF_SIZE = 1024 * 1024; // 1M Byte - - /** - * 批量压缩文件(夹) - * - * @param resFileList 要压缩的文件(夹)列表 - * @param zipFile 生成的压缩文件 - * @throws IOException 当压缩过程出错时抛出 - */ - public static void zipFiles(Collection resFileList, File zipFile) throws IOException { - if (resFileList != null && zipFile != null) { - ZipOutputStream zipout = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile), BUFF_SIZE)); - for (File resFile : resFileList) { - zipFile(resFile, zipout, ""); - } - zipout.close(); - } - } - - /** - * 批量压缩文件(夹) - * - * @param resFileList 要压缩的文件(夹)列表 - * @param zipFile 生成的压缩文件 - * @param comment 压缩文件的注释 - * @throws IOException 当压缩过程出错时抛出 - */ - public static void zipFiles(Collection resFileList, File zipFile, String comment) throws IOException { - ZipOutputStream zipout = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile), BUFF_SIZE)); - for (File resFile : resFileList) { - zipFile(resFile, zipout, ""); - } - zipout.setComment(comment); - zipout.close(); - } - - /** - * 解压缩一个文件 - * - * @param zipFile 压缩文件 - * @param folderPath 解压缩的目标目录 - * @throws IOException 当解压缩过程出错时抛出 - */ - public static void upZipFile(File zipFile, String folderPath) throws IOException { - File desDir = new File(folderPath); - if (!desDir.exists()) { - desDir.mkdirs(); - } - ZipFile zf = new ZipFile(zipFile); - for (Enumeration entries = zf.entries(); entries.hasMoreElements(); ) { - ZipEntry entry = ((ZipEntry) entries.nextElement()); - if (entry.isDirectory()) { - - continue; - } - InputStream in = zf.getInputStream(entry); - String str = folderPath + File.separator + entry.getName(); - str = new String(str.getBytes(), "utf-8"); - File desFile = new File(str); - if (!desFile.exists()) { - File fileParentDir = desFile.getParentFile(); - if (!fileParentDir.exists()) { - fileParentDir.mkdirs(); - } - desFile.createNewFile(); - } - OutputStream out = new FileOutputStream(desFile); - byte buffer[] = new byte[BUFF_SIZE]; - int realLength; - while ((realLength = in.read(buffer)) > 0) { - out.write(buffer, 0, realLength); - } - in.close(); - out.close(); - } - } - - /** - * 解压文件名包含传入文字的文件 - * - * @param zipFile 压缩文件 - * @param folderPath 目标文件夹 - * @param nameContains 传入的文件匹配名 - * @throws ZipException 压缩格式有误时抛出 - * @throws IOException IO错误时抛出 - */ - public static ArrayList upZipSelectedFile(File zipFile, String folderPath, String nameContains) throws IOException { - ArrayList fileList = new ArrayList(); - - File desDir = new File(folderPath); - if (!desDir.exists()) { - desDir.mkdir(); - } - - ZipFile zf = new ZipFile(zipFile); - for (Enumeration entries = zf.entries(); entries.hasMoreElements(); ) { - ZipEntry entry = ((ZipEntry) entries.nextElement()); - if (entry.getName().contains(nameContains)) { - InputStream in = zf.getInputStream(entry); - String str = folderPath + File.separator + entry.getName(); - str = new String(str.getBytes("utf-8"), "gbk"); - // str.getBytes("GB2312"),"8859_1" 输出 - // str.getBytes("8859_1"),"GB2312" 输入 - File desFile = new File(str); - if (!desFile.exists()) { - File fileParentDir = desFile.getParentFile(); - if (!fileParentDir.exists()) { - fileParentDir.mkdirs(); - } - desFile.createNewFile(); - } - OutputStream out = new FileOutputStream(desFile); - byte buffer[] = new byte[BUFF_SIZE]; - int realLength; - while ((realLength = in.read(buffer)) > 0) { - out.write(buffer, 0, realLength); - } - in.close(); - out.close(); - fileList.add(desFile); - } - } - return fileList; - } - - /** - * 获得压缩文件内文件列表 - * - * @param zipFile 压缩文件 - * @return 压缩文件内文件名称 - * @throws ZipException 压缩文件格式有误时抛出 - * @throws IOException 当解压缩过程出错时抛出 - */ - public static ArrayList getEntriesNames(File zipFile) throws IOException { - ArrayList entryNames = new ArrayList(); - Enumeration entries = getEntriesEnumeration(zipFile); - while (entries.hasMoreElements()) { - ZipEntry entry = ((ZipEntry) entries.nextElement()); - entryNames.add(new String(getEntryName(entry).getBytes("GB2312"), "8859_1")); - } - return entryNames; - } - - /** - * 获得压缩文件内压缩文件对象以取得其属性 - * - * @param zipFile 压缩文件 - * @return 返回一个压缩文件列表 - * @throws ZipException 压缩文件格式有误时抛出 - * @throws IOException IO操作有误时抛出 - */ - public static Enumeration getEntriesEnumeration(File zipFile) throws IOException { - ZipFile zf = new ZipFile(zipFile); - return zf.entries(); - - } - - /** - * 取得压缩文件对象的注释 - * - * @param entry 压缩文件对象 - * @return 压缩文件对象的注释 - * @throws UnsupportedEncodingException - */ - public static String getEntryComment(ZipEntry entry) throws UnsupportedEncodingException { - return new String(entry.getComment().getBytes("GB2312"), "8859_1"); - } - - /** - * 取得压缩文件对象的名称 - * - * @param entry 压缩文件对象 - * @return 压缩文件对象的名称 - * @throws UnsupportedEncodingException - */ - public static String getEntryName(ZipEntry entry) throws UnsupportedEncodingException { - return new String(entry.getName().getBytes("GB2312"), "8859_1"); - } - - /** - * 压缩文件 - * - * @param resFile 需要压缩的文件(夹) - * @param zipout 压缩的目的文件 - * @param rootpath 压缩的文件路径 - * @throws FileNotFoundException 找不到文件时抛出 - * @throws IOException 当压缩过程出错时抛出 - */ - private static void zipFile(File resFile, ZipOutputStream zipout, String rootpath) throws IOException { - rootpath = rootpath + (rootpath.trim().length() == 0 ? "" : File.separator) + resFile - .getName(); - rootpath = new String(rootpath.getBytes(), "utf-8"); - if (resFile.isDirectory()) { - File[] fileList = resFile.listFiles(); - for (File file : fileList) { - zipFile(file, zipout, rootpath); - } - } else { - byte buffer[] = new byte[BUFF_SIZE]; - BufferedInputStream in = new BufferedInputStream(new FileInputStream(resFile), BUFF_SIZE); - zipout.putNextEntry(new ZipEntry(rootpath)); - int realLength; - while ((realLength = in.read(buffer)) != -1) { - zipout.write(buffer, 0, realLength); - } - in.close(); - zipout.flush(); - zipout.closeEntry(); - } - } - - //第二种实现 - public static void zip(String src, String dest) throws IOException { - // 提供了一个数据项压缩成一个ZIP归档输出流 - ZipOutputStream out = null; - try { - - //DirTraversal.makeRootDirectory(dest); - //File outFile = DirTraversal.getFilePath(dest,"cache.zip"); - - File outFile = new File(dest);// 源文件或者目录 - File fileOrDirectory = new File(src);// 压缩文件路径 - out = new ZipOutputStream(new FileOutputStream(outFile)); - // 如果此文件是一个文件,否则为false。 - if (fileOrDirectory.isFile()) { - zipFileOrDirectory(out, fileOrDirectory, ""); - } else { - // 返回一个文件或空阵列。 - File[] entries = fileOrDirectory.listFiles(); - for (int i = 0; i < entries.length; i++) { - // 递归压缩,更新curPaths - zipFileOrDirectory(out, entries[i], ""); - } - } - } catch (IOException ex) { - ex.printStackTrace(); - } finally { - // 关闭输出流 - if (out != null) { - try { - out.close(); - } catch (IOException ex) { - ex.printStackTrace(); - } - } - } - } - - private static void zipFileOrDirectory(ZipOutputStream out, File fileOrDirectory, String curPath) throws IOException { - // 从文件中读取字节的输入流 - FileInputStream in = null; - try { - // 如果此文件是一个目录,否则返回false。 - if (!fileOrDirectory.isDirectory()) { - // 压缩文件 - byte[] buffer = new byte[4096]; - int bytes_read; - in = new FileInputStream(fileOrDirectory); - // 实例代表一个条目内的ZIP归档 - ZipEntry entry = new ZipEntry(curPath + fileOrDirectory.getName()); - // 条目的信息写入底层流 - out.putNextEntry(entry); - while ((bytes_read = in.read(buffer)) != -1) { - out.write(buffer, 0, bytes_read); - } - out.closeEntry(); - } else { - // 压缩目录 - File[] entries = fileOrDirectory.listFiles(); - for (int i = 0; i < entries.length; i++) { - // 递归压缩,更新curPaths - zipFileOrDirectory(out, entries[i], curPath + fileOrDirectory.getName() + "/"); - } - } - } catch (IOException ex) { - ex.printStackTrace(); - // throw ex; - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException ex) { - ex.printStackTrace(); - } - } - } - } - - @SuppressWarnings("unchecked") - public static void unzip(String zipFileName, String outputDirectory) throws IOException { - ZipFile zipFile = null; - try { - zipFile = new ZipFile(zipFileName); - Enumeration e = zipFile.entries(); - ZipEntry zipEntry = null; - File dest = new File(outputDirectory); - dest.mkdirs(); - while (e.hasMoreElements()) { - zipEntry = (ZipEntry) e.nextElement(); - String entryName = zipEntry.getName(); - InputStream in = null; - FileOutputStream out = null; - try { - if (zipEntry.isDirectory()) { - String name = zipEntry.getName(); - name = name.substring(0, name.length() - 1); - File f = new File(outputDirectory + File.separator + name); - f.mkdirs(); - } else { - int index = entryName.lastIndexOf("\\"); - if (index != -1) { - File df = new File(outputDirectory + File.separator + entryName - .substring(0, index)); - df.mkdirs(); - } - index = entryName.lastIndexOf("/"); - if (index != -1) { - File df = new File(outputDirectory + File.separator + entryName - .substring(0, index)); - df.mkdirs(); - } - File f = new File(outputDirectory + File.separator + zipEntry.getName()); - // f.createNewFile(); - in = zipFile.getInputStream(zipEntry); - out = new FileOutputStream(f); - int c; - byte[] by = new byte[1024]; - while ((c = in.read(by)) != -1) { - out.write(by, 0, c); - } - out.flush(); - } - } catch (IOException ex) { - ex.printStackTrace(); - throw new IOException("解压失败:" + ex.toString()); - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException ex) { - } - } - if (out != null) { - try { - out.close(); - } catch (IOException ex) { - } - } - } - } - } catch (IOException ex) { - ex.printStackTrace(); - throw new IOException("解压失败:" + ex.toString()); - } finally { - if (zipFile != null) { - try { - zipFile.close(); - } catch (IOException ex) { - } - } - } - } -} diff --git a/common_base/src/main/java/com/wss/common/utils/toast/BaseToast.java b/common_base/src/main/java/com/wss/common/utils/toast/BaseToast.java new file mode 100644 index 0000000..47ab095 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/toast/BaseToast.java @@ -0,0 +1,70 @@ +package com.wss.common.utils.toast; + +import android.app.Application; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import android.widget.Toast; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/ToastUtils + * time : 2018/11/03 + * desc : Toast 基类 + */ +public class BaseToast extends Toast { + + /** 吐司消息 View */ + private TextView mMessageView; + + public BaseToast(Application application) { + super(application); + } + + @Override + public void setView(View view) { + super.setView(view); + mMessageView = getMessageView(view); + } + + @Override + public void setText(CharSequence s) { + mMessageView.setText(s); + } + + /** + * 智能获取用于显示消息的 TextView + */ + private static TextView getMessageView(View view) { + if (view instanceof TextView) { + return (TextView) view; + } else if (view.findViewById(android.R.id.message) instanceof TextView) { + return ((TextView) view.findViewById(android.R.id.message)); + } else if (view instanceof ViewGroup) { + TextView textView = findTextView((ViewGroup) view); + if (textView != null) { + return textView; + } + } + // 如果设置的布局没有包含一个 TextView 则抛出异常,必须要包含一个 TextView 作为 MessageView + throw new IllegalArgumentException("The layout must contain a TextView"); + } + + /** + * 递归获取 ViewGroup 中的 TextView 对象 + */ + private static TextView findTextView(ViewGroup group) { + for (int i = 0; i < group.getChildCount(); i++) { + View view = group.getChildAt(i); + if ((view instanceof TextView)) { + return (TextView) view; + } else if (view instanceof ViewGroup) { + TextView textView = findTextView((ViewGroup) view); + if (textView != null) { + return textView; + } + } + } + return null; + } +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/utils/toast/IToastInterceptor.java b/common_base/src/main/java/com/wss/common/utils/toast/IToastInterceptor.java new file mode 100644 index 0000000..ff4bc25 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/toast/IToastInterceptor.java @@ -0,0 +1,21 @@ +package com.wss.common.utils.toast; + +import android.widget.Toast; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/ToastUtils + * time : 2019/05/19 + * desc : Toast 拦截器接口 + */ +public interface IToastInterceptor { + + /** + * 根据显示的文本决定是否拦截该 Toast + * + * @param toast Toast + * @param text 显示的Toast内容 + * @return boolean + */ + boolean intercept(Toast toast, CharSequence text); +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/utils/toast/IToastStrategy.java b/common_base/src/main/java/com/wss/common/utils/toast/IToastStrategy.java new file mode 100644 index 0000000..657213b --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/toast/IToastStrategy.java @@ -0,0 +1,40 @@ +package com.wss.common.utils.toast; + +import android.widget.Toast; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/ToastUtils + * time : 2019/05/19 + * desc : Toast 处理策略 + */ +public interface IToastStrategy { + + /** + * 短吐司显示的时长 + */ + int SHORT_DURATION_TIMEOUT = 1500; + /** + * 长吐司显示的时长 + */ + int LONG_DURATION_TIMEOUT = 3500; + + /** + * 绑定 Toast 对象 + * + * @param toast Toast + */ + void bind(Toast toast); + + /** + * 显示 Toast + * + * @param text 文本 + */ + void show(CharSequence text); + + /** + * 取消 Toast + */ + void cancel(); +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/utils/toast/IToastStyle.java b/common_base/src/main/java/com/wss/common/utils/toast/IToastStyle.java new file mode 100644 index 0000000..ac85720 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/toast/IToastStyle.java @@ -0,0 +1,101 @@ +package com.wss.common.utils.toast; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/ToastUtils + * time : 2018/09/01 + * desc : 默认样式接口 + */ +public interface IToastStyle { + + /** + * 吐司的重心 + * + * @return 重心 + */ + int getGravity(); + + /** + * X轴偏移 + * + * @return X轴偏移 + */ + int getXOffset(); + + /** + * Y轴偏移 + * + * @return Y轴偏移 + */ + int getYOffset(); + + /** + * 吐司 Z 轴坐标 + * + * @return 吐司 Z 轴坐标 + */ + int getZ(); + + /** + * 背景圆角大小 + * + * @return 圆角大小 + */ + int getCornerRadius(); + + /** + * 背景颜色 + * + * @return 背景颜色 + */ + int getBackgroundColor(); + + /** + * 文本颜色 + * + * @return 文本颜色 + */ + int getTextColor(); + + /** + * 文本大小 + * + * @return 文本大小 + */ + float getTextSize(); + + /** + * 最大行数 + * + * @return 最大显示行数 + */ + int getMaxLines(); + + /** + * 开始内边距 + * + * @return 顶部内边距 + */ + int getPaddingStart(); + + /** + * 顶部内边距 + * + * @return 顶部内边距 + */ + int getPaddingTop(); + + /** + * 结束内边距 + * + * @return 结束内边距 + */ + int getPaddingEnd(); + + /** + * 底部内边距 + * + * @return 底部内边距 + */ + int getPaddingBottom(); +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/utils/toast/SafeHandler.java b/common_base/src/main/java/com/wss/common/utils/toast/SafeHandler.java new file mode 100644 index 0000000..cb93144 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/toast/SafeHandler.java @@ -0,0 +1,34 @@ +package com.wss.common.utils.toast; + +import android.os.Handler; +import android.os.Message; +import android.view.WindowManager; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/ToastUtils + * time : 2018/12/06 + * desc : Toast 显示安全处理 + */ +final class SafeHandler extends Handler { + + private Handler mHandler; + + SafeHandler(Handler handler) { + mHandler = handler; + } + + @Override + public void handleMessage(Message msg) { + // 捕获这个异常,避免程序崩溃 + try { + // 目前发现在 Android 7.1 主线程被阻塞之后弹吐司会导致崩溃,可使用 Thread.sleep(5000) 进行复现 + // 查看源码得知 Google 已经在 Android 8.0 已经修复了此问题 + // 主线程阻塞之后 Toast 也会被阻塞,Toast 因为超时导致 Window Token 失效 + mHandler.handleMessage(msg); + } catch (WindowManager.BadTokenException | IllegalStateException ignored) { + // android.view.WindowManager$BadTokenException:Unable to add window -- token android.os.BinderProxy is not valid; is your activity running? + // java.lang.IllegalStateException:java.lang.IllegalStateException:View android.widget.LinearLayout has already been added to the window manager. + } + } +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/utils/toast/SafeToast.java b/common_base/src/main/java/com/wss/common/utils/toast/SafeToast.java new file mode 100644 index 0000000..45439e5 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/toast/SafeToast.java @@ -0,0 +1,39 @@ +package com.wss.common.utils.toast; + +import android.app.Application; +import android.os.Handler; +import android.widget.Toast; + +import java.lang.reflect.Field; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/ToastUtils + * time : 2018/12/06 + * desc : Toast 显示安全处理 + */ +public final class SafeToast extends BaseToast { + + public SafeToast(Application application) { + super(application); + + // 反射 Toast 中的字段 + try { + + // 获取 mTN 字段对象 + Field mTnField = Toast.class.getDeclaredField("mTN"); + mTnField.setAccessible(true); + Object mTn = mTnField.get(this); + + // 获取 mTN 中的 mHandler 字段对象 + Field mHandlerField = mTnField.getType().getDeclaredField("mHandler"); + mHandlerField.setAccessible(true); + Handler mHandler = (Handler) mHandlerField.get(mTn); + + // 偷梁换柱 + mHandlerField.set(mTn, new SafeHandler(mHandler)); + + } catch (IllegalAccessException | NoSuchFieldException ignored) { + } + } +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/utils/toast/SupportToast.java b/common_base/src/main/java/com/wss/common/utils/toast/SupportToast.java new file mode 100644 index 0000000..0160916 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/toast/SupportToast.java @@ -0,0 +1,32 @@ +package com.wss.common.utils.toast; + +import android.app.Application; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/ToastUtils + * time : 2018/11/02 + * desc : Toast 无通知栏权限兼容 + */ +public final class SupportToast extends BaseToast { + + /** 吐司弹窗显示辅助类 */ + private final ToastHelper mToastHelper; + + public SupportToast(Application application) { + super(application); + mToastHelper = new ToastHelper(this, application); + } + + @Override + public void show() { + // 显示吐司 + mToastHelper.show(); + } + + @Override + public void cancel() { + // 取消显示 + mToastHelper.cancel(); + } +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/utils/toast/ToastHelper.java b/common_base/src/main/java/com/wss/common/utils/toast/ToastHelper.java new file mode 100644 index 0000000..da4ee47 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/toast/ToastHelper.java @@ -0,0 +1,152 @@ +package com.wss.common.utils.toast; + +import android.app.Activity; +import android.app.Application; +import android.content.Context; +import android.graphics.PixelFormat; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.view.WindowManager; +import android.widget.Toast; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/ToastUtils + * time : 2018/11/02 + * desc : 自定义 Toast 辅助类 + */ +final class ToastHelper extends Handler { + + /** + * 当前的吐司对象 + */ + private final Toast mToast; + + /** + * WindowManager 辅助类 + */ + private final WindowHelper mWindowHelper; + + /** + * 当前应用的包名 + */ + private final String mPackageName; + + /** + * 当前是否已经显示 + */ + private boolean mShow; + + ToastHelper(Toast toast, Application application) { + super(Looper.getMainLooper()); + mToast = toast; + mPackageName = application.getPackageName(); + mWindowHelper = WindowHelper.register(this, application); + } + + @Override + public void handleMessage(Message msg) { + // 收到取消显示的消息 + cancel(); + } + + boolean isShow() { + return mShow; + } + + void setShow(boolean show) { + mShow = show; + } + + /*** + * 显示吐司弹窗 + */ + void show() { + if (!isShow()) { + /* + 这里解释一下,为什么不复用 WindowManager.LayoutParams 这个对象 + 因为如果复用了,不同 Activity 之间不能共用一个,第一个 Activity 调用显示方法可以显示出来,但是会导致后面的 Activity 都显示不出来 + 又或者说,非第一次调用显示方法的 Activity 都会把这个显示请求推送给之前第一个调用显示的 Activity 上面,如果第一个 Activity 已经销毁,还会报以下异常 + android.view.WindowManager$BadTokenException: + Unable to add window -- token android.os.BinderProxy@ef1ccb6 is not valid; is your activity running? + */ + final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); + + /* + // 为什么不能加 TYPE_TOAST,因为通知权限在关闭后设置显示的类型为 Toast 会报错 + // android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running? + params.type = WindowManager.LayoutParams.TYPE_TOAST; + */ + + /* + // 这个是旧版本的写法,新版本已经废弃,因为 Activity onPause 方法被调用后这里把 Toast 取消显示了,这样做的原因:防止内存泄露 + // 判断是否为 Android 6.0 及以上系统并且有悬浮窗权限 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(mToast.getView().getContext())) { + // 解决使用 WindowManager 创建的 Toast 只能显示在当前 Activity 的问题 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + } else { + params.type = WindowManager.LayoutParams.TYPE_PHONE; + } + } + */ + params.height = WindowManager.LayoutParams.WRAP_CONTENT; + params.width = WindowManager.LayoutParams.WRAP_CONTENT; + params.format = PixelFormat.TRANSLUCENT; + params.windowAnimations = android.R.style.Animation_Toast; + params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + params.packageName = mPackageName; + // 重新初始化位置 + params.gravity = mToast.getGravity(); + params.x = mToast.getXOffset(); + params.y = mToast.getYOffset(); + + try { + Activity topActivity = mWindowHelper.getTopActivity(); + if (topActivity != null && !topActivity.isFinishing()) { + WindowManager windowManager = (WindowManager) topActivity.getSystemService(Context.WINDOW_SERVICE); + if (windowManager != null) { + windowManager.addView(mToast.getView(), params); + } + } + // 添加一个移除吐司的任务 + sendEmptyMessageDelayed(hashCode(), mToast.getDuration() == Toast.LENGTH_LONG ? + IToastStrategy.LONG_DURATION_TIMEOUT : IToastStrategy.SHORT_DURATION_TIMEOUT); + // 当前已经显示 + setShow(true); + } catch (IllegalStateException | WindowManager.BadTokenException ignored) { + // 如果这个 View 对象被重复添加到 WindowManager 则会抛出异常 + // java.lang.IllegalStateException: View android.widget.TextView has already been added to the window manager. + // 如果 WindowManager 绑定的 Activity 已经销毁,则会抛出异常 + // android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@ef1ccb6 is not valid; is your activity running? + } + } + } + + /** + * 取消吐司弹窗 + */ + void cancel() { + // 移除之前移除吐司的任务 + removeMessages(hashCode()); + if (isShow()) { + try { + Activity topActivity = mWindowHelper.getTopActivity(); + if (topActivity != null) { + WindowManager windowManager = (WindowManager) topActivity.getSystemService(Context.WINDOW_SERVICE); + if (windowManager != null) { + windowManager.removeViewImmediate(mToast.getView()); + } + } + } catch (IllegalArgumentException ignored) { + // 如果当前 WindowManager 没有附加这个 View 则会抛出异常 + // java.lang.IllegalArgumentException: View=android.widget.TextView not attached to window manager + } + // 当前没有显示 + setShow(false); + } + } +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/utils/toast/ToastInterceptor.java b/common_base/src/main/java/com/wss/common/utils/toast/ToastInterceptor.java new file mode 100644 index 0000000..f0311ce --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/toast/ToastInterceptor.java @@ -0,0 +1,18 @@ +package com.wss.common.utils.toast; + +import android.widget.Toast; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/ToastUtils + * time : 2019/05/19 + * desc : Toast 默认拦截器 + */ +public class ToastInterceptor implements IToastInterceptor { + + @Override + public boolean intercept(Toast toast, CharSequence text) { + // 如果是空对象或者空文本就进行拦截 + return text == null || "".equals(text.toString()); + } +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/utils/toast/ToastStrategy.java b/common_base/src/main/java/com/wss/common/utils/toast/ToastStrategy.java new file mode 100644 index 0000000..24ebdfe --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/toast/ToastStrategy.java @@ -0,0 +1,144 @@ +package com.wss.common.utils.toast; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.widget.Toast; + +import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/ToastUtils + * time : 2018/11/12 + * desc : Toast 默认处理器 + */ +public class ToastStrategy extends Handler implements IToastStrategy { + + /** + * 延迟时间 + */ + private static final int DELAY_TIMEOUT = 200; + + /** + * 显示吐司 + */ + private static final int TYPE_SHOW = 1; + /** + * 继续显示 + */ + private static final int TYPE_CONTINUE = 2; + /** + * 取消显示 + */ + private static final int TYPE_CANCEL = 3; + + /** + * 队列最大容量 + */ + private static final int MAX_TOAST_CAPACITY = 3; + + /** + * 吐司队列 + */ + private volatile Queue mQueue; + + /** + * 当前是否正在执行显示操作 + */ + private volatile boolean mShow; + + /** + * 吐司对象 + */ + private Toast mToast; + + public ToastStrategy() { + super(Looper.getMainLooper()); + mQueue = getToastQueue(); + } + + @Override + public void bind(Toast toast) { + mToast = toast; + } + + @Override + public void show(CharSequence text) { + if (mQueue.isEmpty() || !mQueue.contains(text)) { + // 添加一个元素并返回true,如果队列已满,则返回false + if (!mQueue.offer(text)) { + // 移除队列头部元素并添加一个新的元素 + mQueue.poll(); + mQueue.offer(text); + } + } + + if (!mShow) { + mShow = true; + // 延迟一段时间之后再执行,因为在没有通知栏权限的情况下,Toast 只能显示当前 Activity + // 如果当前 Activity 在 ToastUtils.show 之后进行 finish 了,那么这个时候 Toast 可能会显示不出来 + // 因为 Toast 会显示在销毁 Activity 界面上,而不会显示在新跳转的 Activity 上面 + sendEmptyMessageDelayed(TYPE_SHOW, DELAY_TIMEOUT); + } + } + + @Override + public void cancel() { + if (mShow) { + mShow = false; + sendEmptyMessage(TYPE_CANCEL); + } + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case TYPE_SHOW: + // 返回队列头部的元素,如果队列为空,则返回null + CharSequence text = mQueue.peek(); + if (text != null) { + mToast.setText(text); + mToast.show(); + // 等这个 Toast 显示完后再继续显示,要加上一些延迟 + // 不然在某些手机上 Toast 可能会来不及消失就要进行显示,这样是显示不出来的 + sendEmptyMessageDelayed(TYPE_CONTINUE, getToastDuration(text) + DELAY_TIMEOUT); + } else { + mShow = false; + } + break; + case TYPE_CONTINUE: + // 移除并返问队列头部的元素,如果队列为空,则返回null + mQueue.poll(); + if (!mQueue.isEmpty()) { + sendEmptyMessage(TYPE_SHOW); + } else { + mShow = false; + } + break; + case TYPE_CANCEL: + mShow = false; + mQueue.clear(); + mToast.cancel(); + break; + default: + break; + } + } + + /** + * 获取吐司队列 + */ + public Queue getToastQueue() { + return new ArrayBlockingQueue<>(MAX_TOAST_CAPACITY); + } + + /** + * 根据文本来获取吐司的显示时长 + */ + public int getToastDuration(CharSequence text) { + // 如果显示的文字超过了10个就显示长吐司,否则显示短吐司 + return text.length() > 20 ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT; + } +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/utils/toast/WindowHelper.java b/common_base/src/main/java/com/wss/common/utils/toast/WindowHelper.java new file mode 100644 index 0000000..214bdba --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/toast/WindowHelper.java @@ -0,0 +1,91 @@ +package com.wss.common.utils.toast; + +import android.app.Activity; +import android.app.Application; +import android.os.Bundle; + +import org.jetbrains.annotations.NotNull; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/ToastUtils + * time : 2018/11/06 + * desc : WindowManager 辅助类(用于获取当前 Activity 的 WindowManager 对象) + */ +final class WindowHelper implements Application.ActivityLifecycleCallbacks { + + /** + * 栈顶 Activity + */ + private Activity mTopActivity; + + /** + * 用于 Activity 暂停时移除 WindowManager + */ + private final ToastHelper mToastHelper; + + private WindowHelper(ToastHelper toast) { + mToastHelper = toast; + } + + static WindowHelper register(ToastHelper toast, Application application) { + WindowHelper window = new WindowHelper(toast); + application.registerActivityLifecycleCallbacks(window); + return window; + } + + /** + * 获取栈顶的 Activity + */ + Activity getTopActivity() { + return mTopActivity; + } + + /** + * {@link Application.ActivityLifecycleCallbacks} + */ + + @Override + public void onActivityCreated(@NotNull Activity activity, Bundle savedInstanceState) { + mTopActivity = activity; + } + + @Override + public void onActivityStarted(@NotNull Activity activity) { + mTopActivity = activity; + } + + @Override + public void onActivityResumed(@NotNull Activity activity) { + mTopActivity = activity; + } + + // A 跳转 B 页面的生命周期方法执行顺序: + // onPause(A) ---> onCreate(B) ---> onStart(B) ---> onResume(B) ---> onStop(A) ---> onDestroyed(A) + + @Override + public void onActivityPaused(@NotNull Activity activity) { + // 取消这个吐司的显示 + if (mToastHelper.isShow()) { + // 不能放在 onStop 或者 onDestroyed 方法中,因为此时新的 Activity 已经创建完成,必须在这个新的 Activity 未创建之前关闭这个 WindowManager + // 调用取消显示会直接导致新的 Activity 的 onCreate 调用显示吐司可能显示不出来的问题,又或者有时候会立马显示然后立马消失的那种效果 + mToastHelper.cancel(); + } + } + + @Override + public void onActivityStopped(@NotNull Activity activity) { + } + + @Override + public void onActivitySaveInstanceState(@NotNull Activity activity, @NotNull Bundle outState) { + } + + @Override + public void onActivityDestroyed(@NotNull Activity activity) { + if (mTopActivity == activity) { + // 移除对这个 Activity 的引用 + mTopActivity = null; + } + } +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/utils/toast/style/BaseToastStyle.java b/common_base/src/main/java/com/wss/common/utils/toast/style/BaseToastStyle.java new file mode 100644 index 0000000..618b040 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/toast/style/BaseToastStyle.java @@ -0,0 +1,72 @@ +package com.wss.common.utils.toast.style; + +import android.content.Context; +import android.util.TypedValue; +import android.view.Gravity; + +import com.wss.common.utils.toast.IToastStyle; + + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/ToastUtils + * time : 2019/05/19 + * desc : 默认样式基类 + */ +public abstract class BaseToastStyle implements IToastStyle { + + private Context mContext; + + public BaseToastStyle(Context context) { + mContext = context; + } + + @Override + public int getGravity() { + return Gravity.CENTER; + } + + @Override + public int getXOffset() { + return 0; + } + + @Override + public int getYOffset() { + return 0; + } + + @Override + public int getZ() { + return 30; + } + + @Override + public int getMaxLines() { + return 5; + } + + @Override + public int getPaddingEnd() { + return getPaddingStart(); + } + + @Override + public int getPaddingBottom() { + return getPaddingTop(); + } + + /** + * dp转px + */ + protected int dp2px(float dpValue) { + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, mContext.getResources().getDisplayMetrics()); + } + + /** + * sp转px + */ + protected int sp2px(float spValue) { + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, mContext.getResources().getDisplayMetrics()); + } +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/utils/toast/style/ToastBlackStyle.java b/common_base/src/main/java/com/wss/common/utils/toast/style/ToastBlackStyle.java new file mode 100644 index 0000000..a2c04c7 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/utils/toast/style/ToastBlackStyle.java @@ -0,0 +1,46 @@ +package com.wss.common.utils.toast.style; + +import android.content.Context; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/ToastUtils + * time : 2018/09/01 + * desc : 默认黑色样式实现 + */ +public class ToastBlackStyle extends BaseToastStyle { + + public ToastBlackStyle(Context context) { + super(context); + } + + @Override + public int getCornerRadius() { + return dp2px(4); + } + + @Override + public int getBackgroundColor() { + return 0XBF000000; + } + + @Override + public int getTextColor() { + return 0XEEFFFFFF; + } + + @Override + public float getTextSize() { + return sp2px(14); + } + + @Override + public int getPaddingStart() { + return dp2px(12); + } + + @Override + public int getPaddingTop() { + return dp2px(6); + } +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/view/SpringboardActivity.java b/common_base/src/main/java/com/wss/common/view/SpringboardActivity.java new file mode 100644 index 0000000..72be373 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/view/SpringboardActivity.java @@ -0,0 +1,44 @@ +package com.wss.common.view; + +import android.view.View; + +import com.wss.common.base.BaseActionBarActivity; +import com.wss.common.base.BaseFragment; +import com.wss.common.base.R; +import com.wss.common.base.mvp.BasePresenter; +import com.wss.common.constants.Dic; +import com.wss.common.utils.ARouterUtils; + +import androidx.fragment.app.FragmentTransaction; + +/** + * Describe:跳板Activity,主要是跳转一些Fragment + * 统一采用ARouter方式跳转 + * Created by 吴天强 on 2020/5/7. + */ +public class SpringboardActivity extends BaseActionBarActivity { + + @Override + protected int getLayoutId() { + return R.layout.activity_springboard; + } + + @Override + protected void initView() { + boolean isActionBar = getIntent().getBooleanExtra(Dic.IS_ACTION_BAR, false); + String titleText = getIntent().getStringExtra(Dic.TITLE_TEXT); + getTitleBar().setVisibility(isActionBar ? View.VISIBLE : View.GONE); + setCenterText(titleText); + BaseFragment fragment = ARouterUtils.getFragment(getIntent().getStringExtra(Dic.TARGET_FRAGMENT_PATH)); + //给Fragment设置带入参数 + fragment.setArguments(getIntent().getBundleExtra(Dic.BUNDLE_DATA)); + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.add(R.id.fl_content, fragment); + transaction.commitAllowingStateLoss(); + } + + @Override + protected BasePresenter createPresenter() { + return null; + } +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/view/browser/BrowserActivity.java b/common_base/src/main/java/com/wss/common/view/browser/BrowserActivity.java new file mode 100644 index 0000000..aff80ec --- /dev/null +++ b/common_base/src/main/java/com/wss/common/view/browser/BrowserActivity.java @@ -0,0 +1,490 @@ +package com.wss.common.view.browser; + +import android.annotation.SuppressLint; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.webkit.ConsoleMessage; +import android.webkit.JsResult; +import android.webkit.WebChromeClient; +import android.webkit.WebResourceRequest; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.orhanobut.logger.Logger; +import com.wss.common.base.BaseMvpActivity; +import com.wss.common.base.R; +import com.wss.common.base.R2; +import com.wss.common.constants.Constants; +import com.wss.common.constants.Dic; +import com.wss.common.utils.NetworkUtil; +import com.wss.common.utils.PxUtils; +import com.wss.common.utils.ValidUtils; +import com.wss.common.view.browser.mvp.BrowserPresenter; +import com.wss.common.view.browser.mvp.contract.BrowserContract; +import com.wss.common.widget.dialog.AppDialog; + +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import butterknife.BindView; +import butterknife.OnClick; + +/** + * Describe:浏览器页面-WebView + * Created by 吴天强 on 2020/5/18. + */ +public class BrowserActivity extends BaseMvpActivity implements BrowserContract.View { + + @BindView(R2.id.tv_title) + TextView tvTitle; + + @BindView(R2.id.progress_bar) + ProgressBar progressBar; + + @BindView(R2.id.web_view) + WebView webView; + + @BindView(R2.id.iv_empty) + ImageView ivEmpty; + + @BindView(R2.id.tv_empty) + TextView tvEmpty; + + @BindView(R2.id.ll_empty_view) + LinearLayout llEmptyView; + + @BindView(R2.id.img_back) + ImageView ivBack; + @BindView(R2.id.tv_back) + + TextView tvBack; + @BindView(R2.id.tv_close_right) + TextView tvCloseRight; + + @BindView(R2.id.rl_browser_header) + RelativeLayout relHeader; + /** + * 本地缓存目录 + */ + private String appCacheDir = Constants.WebView.CACHE_DIR; + private boolean isFullScreen = false; + private String url; + private String title; + + + @Override + protected int getLayoutId() { + return R.layout.activity_browser; + } + + public void hiddenHeader() { + relHeader.setVisibility(View.GONE); + } + + @Override + protected void initView() { + //沉浸式状态栏 + setImmersionBarColor(R.color.white); + url = getIntent().getStringExtra(Dic.URL); + title = getIntent().getStringExtra(Dic.TITLE_TEXT); + tvCloseRight.setVisibility(View.GONE); + tvBack.setVisibility(View.GONE); + tvTitle.setText(title); + initWebView(); + refreshPage(); + } + + @OnClick({ + R2.id.img_back, + R2.id.tv_back, + R2.id.tv_close_right, + }) + public void onClick(View v) { + int id = v.getId(); + if (id == R.id.img_back) { + finish(); + } else if (id == R.id.tv_back) { + finish(); + } else if (id == R.id.tv_close_right) { + finish(); + } + } + + /** + * 获取一个WebView实例 + * + * @return + */ + public WebView getWebView() { + return webView; + } + + @SuppressLint("SetJavaScriptEnabled") + private void initWebView() { + WebSettings webSettings = webView.getSettings(); + webSettings.setJavaScriptEnabled(true); + webSettings.setDomStorageEnabled(true); + webSettings.setPluginState(WebSettings.PluginState.ON); + webSettings.setAppCacheEnabled(true); + webSettings.setAppCachePath(appCacheDir); + webSettings.setDatabasePath(appCacheDir); + webSettings.setAppCacheMaxSize(100 * 1024 * 1024); + webSettings.setCacheMode(WebSettings.LOAD_DEFAULT); + webSettings.setAllowFileAccess(true); + webSettings.setRenderPriority(WebSettings.RenderPriority.HIGH); + webSettings.setSupportZoom(true); + webSettings.setBuiltInZoomControls(true); + // 添加新参数 + webSettings.setUseWideViewPort(true); + webSettings.setLoadWithOverviewMode(true); + + setPageCacheCapacity(webSettings); + webSettings.enableSmoothTransition(); + // 是否显示缩放按钮 + webSettings.setDisplayZoomControls(false); + + //设置HTTP HTTPS 混合模式加载页面 + webSettings.setMixedContentMode(2); + webView.setWebViewClient(webViewClient); + webView.setWebChromeClient(chromeClient); + } + + + private class JavaScriptApi { + + } + + @Override + protected BrowserPresenter createPresenter() { + return new BrowserPresenter(); + } + + + /** + * WebView调用JS + * + * @param method 方法名 + * @param data 参数多参已逗号分隔 + */ + private void loadJsMethod(String method, String data) { + if (ValidUtils.isValid(data)) { + // 传递参数调用 + String format = String.format("javascript:%s('%s')", method, data); + webView.loadUrl(format); + } else { + // 无参数调用 + webView.loadUrl(String.format("javascript:%s()", method)); + } + } + + + /** + * 刷新页面 显示网页 + */ + private void refreshPage() { + webView.clearCache(true); + if (NetworkUtil.isNetworkEnabled(context)) { + webView.setVisibility(View.VISIBLE); + progressBar.setVisibility(View.VISIBLE); + webView.loadUrl(url); +// webView.loadUrl("file:///android_asset/demo.html"); + } else { + shoeErrorPage(true); + } + } + + /** + * WebView client + */ + private WebViewClient webViewClient = new WebViewClient() { + + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + if (url.startsWith("http:") || url.startsWith("https:")) { + return false; + } + + Logger.e("shouldOverrideUrlLoading: " + url); + //拦截tel:拨打电话。 + if (url.startsWith("tel:")) { + try { + startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(url))); + } catch (Exception e) { + e.printStackTrace(); + } + return true; + } + return false; + } + + @Override + public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + if (request.getUrl().toString().startsWith("http:") || request.getUrl().toString().startsWith("https:")) { + return false; + } + return true; + } + + @Override + public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) { + super.doUpdateVisitedHistory(view, url, isReload); + } + + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + super.onPageStarted(view, url, favicon); + Logger.e("onPageStarted: " + url); + if (progressBar != null) { + progressBar.setVisibility(View.VISIBLE); + } + } + + + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + Logger.e("onPageFinished: " + url); + if (progressBar != null) { + progressBar.setVisibility(View.GONE); + } + } + + @Override + public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { + super.onReceivedError(view, errorCode, description, failingUrl); + if (progressBar != null) { + progressBar.setVisibility(View.GONE); + } + } + + @Override + public void onLoadResource(WebView view, String url) { + super.onLoadResource(view, url); + } + }; + /** + * 显示到选择进度条的进度,根据返回的title获取到当前的标题 + * 如果传过来的参数中带有标题,则显示该标题,不再根据WebChromeClient返回的title获取到当前的标题 + */ + private WebChromeClient chromeClient = new WebChromeClient() { + private View myView = null; + private int mTempProgress = 0; + + @Override + public boolean onConsoleMessage(ConsoleMessage consoleMessage) { + return super.onConsoleMessage(consoleMessage); + } + + @Override + public boolean onJsAlert(WebView view, String url, String message, @NotNull JsResult result) { + showDialog(message); + result.confirm(); + return true; + } + + @Override + public void onProgressChanged(WebView view, int newProgress) { + // 进度加载到100时,就不再重新进行加载,防止页面嵌套页面时,进度条反复加载 + if (progressBar == null || mTempProgress == 100) { + return; + } + mTempProgress = newProgress > 50 ? 100 : newProgress; + if (progressBar.getVisibility() == View.GONE) { + showProgressView(); + } + progressBar.setProgress(mTempProgress); + if (newProgress >= 80 && progressBar.getVisibility() == View.VISIBLE) { + dismissProgressView(); + } + } + + @Override + public void onReceivedTitle(WebView view, String title) { + super.onReceivedTitle(view, title); + if (tvTitle != null && !ValidUtils.isValid(title)) { + tvTitle.setText(title); + } + } + + @Override + public void onShowCustomView(View view, CustomViewCallback callback) { + super.onShowCustomView(view, callback); + isFullScreen = true; + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + ViewGroup parent = (ViewGroup) webView.getParent().getParent(); + parent.setVisibility(View.GONE); + ((ViewGroup) parent.getParent()).addView(view, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT, Gravity.CENTER)); + myView = view; + chromeClient = this; + } + + @Override + public void onHideCustomView() { + isFullScreen = false; + if (myView != null) { + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + if (myView != null && myView.getParent() != null) { + ViewGroup parent = (ViewGroup) myView.getParent(); + parent.removeView(myView); + + if (webView.getParent().getParent() != null) { + ViewGroup parent2 = (ViewGroup) webView.getParent().getParent(); + parent2.setVisibility(View.VISIBLE); + } + } + myView = null; + } + } + }; + + @OnClick({R2.id.img_back, + R2.id.tv_back, + R2.id.tv_close_right, + R2.id.ll_empty_view}) + public void onViewClicked(@NotNull View view) { + int id = view.getId(); + if (id == R.id.img_back || id == R.id.tv_back) { + //返回 + goBack(); + } else if (id == R.id.tv_close_right) { + //关闭 + finish(); + } else if (id == R.id.ll_empty_view) { + //点击错误页 + } + } + + /** + * 显示进度条 + */ + private void showProgressView() { + AlphaAnimation anim = new AlphaAnimation(0, 1.0f); + anim.setDuration(200); + progressBar.startAnimation(anim); + progressBar.setVisibility(View.VISIBLE); + } + + /** + * 隐藏进度条 + */ + protected void dismissProgressView() { + AlphaAnimation anim = new AlphaAnimation(1.0f, 0); + anim.setDuration(200); + anim.setAnimationListener(new Animation.AnimationListener() { + + @Override + public void onAnimationStart(Animation animation) { + } + + @Override + public void onAnimationRepeat(Animation animation) { + } + + // 动画结束后再隐藏进度条 + @Override + public void onAnimationEnd(Animation animation) { + progressBar.setVisibility(View.GONE); + } + }); + progressBar.startAnimation(anim); + // 动画结束前显示进度条 + progressBar.setVisibility(View.VISIBLE); + } + + /** + * 显示错误页面 + * + * @param showError 显示 + */ + private void shoeErrorPage(boolean showError) { + if (showError) { + llEmptyView.setVisibility(View.VISIBLE); + webView.setVisibility(View.GONE); + } else { + llEmptyView.setVisibility(View.GONE); + webView.setVisibility(View.VISIBLE); + } + } + + /** + * 设置WebView缓存容量 + * + * @param webSettings settings + */ + public void setPageCacheCapacity(WebSettings webSettings) { + try { + @SuppressLint("PrivateApi") + Class c = Class.forName("android.webkit.WebSettingsClassic"); + Method tt = c.getMethod("setPageCacheCapacity", int.class); + tt.invoke(webSettings, 5); + } catch (ClassNotFoundException e) { + System.out.println("No such class: " + e); + } catch (NoSuchMethodException | IllegalArgumentException | + IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + } + + /** + * 弹出对话框 + * + * @param message 提示信息 + */ + private void showDialog(String message) { + new AppDialog.Builder(context) + .setContent(message) + .create() + .show(); + } + + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + goBack(); + return true; + } + return super.onKeyDown(keyCode, event); + } + + /** + * 返回上一步 + */ + private void goBack() { + if (webView.canGoBack()) { + webView.goBack(); + } else { + finish(); + } + } + + @OnClick(R2.id.tv_title) + public void test() { + + int height = PxUtils.getScreenHeight(context); + int width = PxUtils.getScreenWidth(context); + + new AppDialog.Builder(context) + .setContent(height+"x"+width) + .setSingleButton("YES") + .create() + .show(); + } +} diff --git a/common_base/src/main/java/com/wss/common/view/browser/mvp/BrowserPresenter.java b/common_base/src/main/java/com/wss/common/view/browser/mvp/BrowserPresenter.java new file mode 100644 index 0000000..a42a522 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/view/browser/mvp/BrowserPresenter.java @@ -0,0 +1,22 @@ +package com.wss.common.view.browser.mvp; + +import com.wss.common.base.mvp.BasePresenter; +import com.wss.common.view.browser.mvp.contract.BrowserContract; +import com.wss.common.view.browser.mvp.model.BrowserModel; + +/** + * Describe:浏览器控制器 + * Created by 吴天强 on 2020/5/18. + */ +public class BrowserPresenter extends BasePresenter + implements BrowserContract.Presenter { + @Override + protected BrowserModel createModule() { + return new BrowserModel(getLifecycleOwner()); + } + + @Override + public void start() { + + } +} diff --git a/common_base/src/main/java/com/wss/common/view/browser/mvp/contract/BrowserContract.java b/common_base/src/main/java/com/wss/common/view/browser/mvp/contract/BrowserContract.java new file mode 100644 index 0000000..002567e --- /dev/null +++ b/common_base/src/main/java/com/wss/common/view/browser/mvp/contract/BrowserContract.java @@ -0,0 +1,23 @@ +package com.wss.common.view.browser.mvp.contract; + + +import com.wss.common.base.mvp.IBaseView; + +/** + * Describe:浏览器页面契约类 + * Created by 吴天强 on 2020/5/18. + */ +public interface BrowserContract { + + interface Model { + + } + + interface View extends IBaseView { + + } + + interface Presenter { + + } +} diff --git a/common_base/src/main/java/com/wss/common/view/browser/mvp/model/BrowserModel.java b/common_base/src/main/java/com/wss/common/view/browser/mvp/model/BrowserModel.java new file mode 100644 index 0000000..83d333f --- /dev/null +++ b/common_base/src/main/java/com/wss/common/view/browser/mvp/model/BrowserModel.java @@ -0,0 +1,17 @@ +package com.wss.common.view.browser.mvp.model; + +import com.wss.common.base.mvp.BaseModel; +import com.wss.common.view.browser.mvp.contract.BrowserContract; + +import androidx.lifecycle.LifecycleOwner; + +/** + * Describe:浏览器Model + * Created by 吴天强 on 2020/5/18. + */ +public class BrowserModel extends BaseModel implements BrowserContract.Presenter { + + public BrowserModel(LifecycleOwner lifecycleOwner) { + super(lifecycleOwner); + } +} diff --git a/common_base/src/main/java/com/wss/common/view/file/SelectFileActivity.java b/common_base/src/main/java/com/wss/common/view/file/SelectFileActivity.java new file mode 100644 index 0000000..86024eb --- /dev/null +++ b/common_base/src/main/java/com/wss/common/view/file/SelectFileActivity.java @@ -0,0 +1,135 @@ +package com.wss.common.view.file; + +import android.content.Intent; + +import com.wss.common.base.BaseActionBarActivity; +import com.wss.common.base.R; +import com.wss.common.base.R2; +import com.wss.common.constants.Constants; +import com.wss.common.constants.Dic; +import com.wss.common.utils.JsonUtils; +import com.wss.common.utils.ToastUtils; +import com.wss.common.view.file.adapter.SelectFileAdapter; +import com.wss.common.view.file.bean.FileInfo; +import com.wss.common.view.file.mvp.SelectFilePresenter; +import com.wss.common.view.file.mvp.contract.SelectFileContract; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import butterknife.BindView; + +/** + * Describe:选择手机文件 + * Created by 吴天强 on 2020/6/20. + */ +public class SelectFileActivity extends BaseActionBarActivity implements SelectFileContract.View { + + @BindView(R2.id.recycle_view) + RecyclerView recyclerView; + + private SelectFileAdapter adapter; + private List fileList = new ArrayList<>(); + + /** + * 最大可选文件数 + */ + private int max = 9; + + @Override + protected SelectFilePresenter createPresenter() { + return new SelectFilePresenter(); + } + + @Override + protected int getLayoutId() { + return R.layout.activity_select_file; + } + + @Override + protected void initView() { + if (getType() == Constants.FileType.IMAGE) { + setCenterText(getString(R.string.slelect_image)); + recyclerView.setLayoutManager(new GridLayoutManager(context, 3)); + + } else { + setCenterText(getString(R.string.select_file)); + recyclerView.setLayoutManager(new LinearLayoutManager(context)); + + } + max = getIntent().getIntExtra(Dic.MAX_SELECT_FILE, max); + adapter = new SelectFileAdapter(context, fileList, (fileInfo, position) -> { + if (!fileInfo.isChecked()) { + List allSelect = getAllSelect(); + if (allSelect.size() >= max) { + if (getType() == Constants.FileType.IMAGE) { + ToastUtils.show(context, getString(R.string.max_select_image, max)); + } else { + ToastUtils.show(context, getString(R.string.max_select_file, max)); + } + return; + } + } + fileList.get(position).setChecked(!fileList.get(position).isChecked()); + adapter.notifyItemChanged(position); + }); + recyclerView.setAdapter(adapter); + setRightText(R.string.confirm, v -> { + ArrayList allSelect = getAllSelect(); + if (allSelect.size() < 1) { + ToastUtils.show(context, getString(R.string.no_select)); + return; + } + Intent intent = new Intent(); + intent.putExtra(Dic.SELECT_FILE_PATHS, JsonUtils.toJson(allSelect)); + setResult(RESULT_OK, intent); + finish(); + }); + getPresenter().start(); + } + + @Override + public int getType() { + return getIntent().getIntExtra(Dic.FROM_TYPE, Constants.FileType.IMAGE); + } + + @Override + public void refreshFileList(List fileList) { + this.fileList.clear(); + this.fileList.addAll(fileList); + adapter.notifyDataSetChanged(); + } + + @Override + public void onError(Object tag, String errorMsg) { + super.onError(tag, errorMsg); + showErrorView(errorMsg); + } + + @Override + public void onEmpty(Object tag) { + super.onEmpty(tag); + showEmptyView(); + } + + /** + * 获取全部已选中文件 + * + * @return 所有已选中的文件的路径 + */ + @NotNull + private ArrayList getAllSelect() { + ArrayList pathList = new ArrayList<>(); + for (FileInfo info : fileList) { + if (info.isChecked()) { + pathList.add(info.getFilePath()); + } + } + return pathList; + } +} diff --git a/common_base/src/main/java/com/wss/common/view/file/adapter/SelectFileAdapter.java b/common_base/src/main/java/com/wss/common/view/file/adapter/SelectFileAdapter.java new file mode 100644 index 0000000..8d8cea3 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/view/file/adapter/SelectFileAdapter.java @@ -0,0 +1,53 @@ +package com.wss.common.view.file.adapter; + +import android.content.Context; + +import com.wss.common.base.R; +import com.wss.common.base.adapter.BaseListAdapter; +import com.wss.common.constants.Constants; +import com.wss.common.utils.ImageUtils; +import com.wss.common.view.file.adapter.holder.SelectFileViewHolder; +import com.wss.common.view.file.bean.FileInfo; + +import org.byteam.superadapter.SuperViewHolder; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * Describe:选择文件适配器 + * Created by 吴天强 on 2020/6/20. + */ +public class SelectFileAdapter extends BaseListAdapter { + + private OnItemClickListener onItemClickListener; + + public SelectFileAdapter(Context context, List mData, OnItemClickListener listener) { + super(context, mData, new SelectFileViewHolder()); + this.onItemClickListener = listener; + } + + @Override + public void onBindData(@NotNull SuperViewHolder holder, int viewType, int layoutPosition, @NotNull FileInfo data) { + holder.findViewById(R.id.iftv_checked).setSelected(data.isChecked()); + if (onItemClickListener != null) { + holder.itemView.setOnClickListener(v -> onItemClickListener.onItemClick(data, layoutPosition)); + } + if (viewType == Constants.FileType.IMAGE) { + //图片 + ImageUtils.loadImageCircleBead(holder.findViewById(R.id.iv_image), data.getFilePath(), 4); + } else { + holder.setText(R.id.tv_name, data.getFileName()); + } + } + + public interface OnItemClickListener { + /** + * Item选择点击事件 + * + * @param fileInfo 文件 + * @param position 下标 + */ + void onItemClick(FileInfo fileInfo, int position); + } +} diff --git a/common_base/src/main/java/com/wss/common/view/file/adapter/holder/SelectFileViewHolder.java b/common_base/src/main/java/com/wss/common/view/file/adapter/holder/SelectFileViewHolder.java new file mode 100644 index 0000000..de7743d --- /dev/null +++ b/common_base/src/main/java/com/wss/common/view/file/adapter/holder/SelectFileViewHolder.java @@ -0,0 +1,33 @@ +package com.wss.common.view.file.adapter.holder; + +import com.wss.common.base.R; +import com.wss.common.constants.Constants; +import com.wss.common.view.file.bean.FileInfo; + +import org.byteam.superadapter.IMulItemViewType; +import org.jetbrains.annotations.NotNull; + +/** + * Describe:选择文件多View + * Created by 吴天强 on 2020/6/20. + */ +public class SelectFileViewHolder implements IMulItemViewType { + + @Override + public int getViewTypeCount() { + return 2; + } + + @Override + public int getItemViewType(int position, @NotNull FileInfo fileInfo) { + return fileInfo.getType(); + } + + @Override + public int getLayoutId(int viewType) { + if (viewType == Constants.FileType.IMAGE) { + return R.layout.item_of_select_image; + } + return R.layout.item_of_select_file; + } +} diff --git a/common_base/src/main/java/com/wss/common/view/file/bean/FileInfo.java b/common_base/src/main/java/com/wss/common/view/file/bean/FileInfo.java new file mode 100644 index 0000000..0a30f85 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/view/file/bean/FileInfo.java @@ -0,0 +1,28 @@ +package com.wss.common.view.file.bean; + + +import com.wss.common.base.bean.BaseBean; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + + +/** + * Describe:选择文件 + * Created by 吴天强 on 2019/11/12. + */ +@Getter +@Setter +@ToString +public class FileInfo extends BaseBean { + private String fileName; + private String filePath; + private long fileSize; + private String time; + private boolean checked; + /** + * 文件类型 Constants.FileType + */ + private int type; +} diff --git a/common_base/src/main/java/com/wss/common/view/file/mvp/SelectFilePresenter.java b/common_base/src/main/java/com/wss/common/view/file/mvp/SelectFilePresenter.java new file mode 100644 index 0000000..b62f236 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/view/file/mvp/SelectFilePresenter.java @@ -0,0 +1,36 @@ +package com.wss.common.view.file.mvp; + + +import com.wss.common.base.mvp.BasePresenter; +import com.wss.common.utils.ValidUtils; +import com.wss.common.view.file.mvp.contract.SelectFileContract; +import com.wss.common.view.file.mvp.model.SelectFileModel; + +/** + * Describe:选择文件控制器 + * Created by 吴天强 on 2020/6/20. + */ +public class SelectFilePresenter extends BasePresenter + implements SelectFileContract.Presenter { + @Override + protected SelectFileModel createModule() { + return new SelectFileModel(getLifecycleOwner()); + } + + @Override + public void start() { + showLoading(); + getModel().queryFile(getView().getType()).subscribe( + fileInfoList -> { + dismissLoading(); + if (ValidUtils.isValid(fileInfoList)) { + getView().refreshFileList(fileInfoList); + } else { + getView().onEmpty(""); + } + }, t -> { + dismissLoading(); + getView().onError("", t.getMessage()); + }); + } +} diff --git a/common_base/src/main/java/com/wss/common/view/file/mvp/contract/SelectFileContract.java b/common_base/src/main/java/com/wss/common/view/file/mvp/contract/SelectFileContract.java new file mode 100644 index 0000000..0225ee3 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/view/file/mvp/contract/SelectFileContract.java @@ -0,0 +1,48 @@ +package com.wss.common.view.file.mvp.contract; + +import com.wss.common.base.mvp.IBaseView; +import com.wss.common.view.file.bean.FileInfo; + +import java.util.List; + +import io.reactivex.Observable; + +/** + * Describe:选择文件契约类 + * Created by 吴天强 on 2020/6/20. + */ +public interface SelectFileContract { + + interface Model { + /** + * 查询文件 + * + * @param type 类型 + * @return 文件列表 + */ + Observable> queryFile(int type); + } + + interface View extends IBaseView { + + /** + * 选择文件的类型 + * + * @return 类型 + */ + int getType(); + + /** + * 刷新文件列表 + * + * @param fileList 文件列表 + */ + void refreshFileList(List fileList); + + } + + interface Presenter { + + + } +} diff --git a/common_base/src/main/java/com/wss/common/view/file/mvp/model/SelectFileModel.java b/common_base/src/main/java/com/wss/common/view/file/mvp/model/SelectFileModel.java new file mode 100644 index 0000000..f1a8319 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/view/file/mvp/model/SelectFileModel.java @@ -0,0 +1,202 @@ +package com.wss.common.view.file.mvp.model; + +import android.content.ContentResolver; +import android.database.Cursor; +import android.provider.MediaStore; + +import com.wss.common.base.BaseApplication; +import com.wss.common.base.mvp.BaseModel; +import com.wss.common.constants.Constants; +import com.wss.common.utils.FileUtils; +import com.wss.common.view.file.bean.FileInfo; +import com.wss.common.view.file.mvp.contract.SelectFileContract; + +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import androidx.lifecycle.LifecycleOwner; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; + +/** + * Describe:选择文件Model + * Created by 吴天强 on 2020/6/20. + */ +public class SelectFileModel extends BaseModel implements SelectFileContract.Model { + /** + * 视频后缀 + */ + private static final String[] VIDEO_SUFFIX = {".mp4", ".rmvb", ".avi", ".flv"}; + /** + * 语音后缀 + */ + private static final String[] VOICE_SUFFIX = {".mp3", ".wav", ".ogg", ".midi"}; + /** + * 文档后缀 + */ + private static final String[] DOCUMENT_SUFFIX = {".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf"}; + /** + * 压缩包后缀 + */ + private static final String[] PACKAGE_SUFFIX = {".jar", ".zip", ".rar", ".gz", ".apk", ".img"}; + /** + * 其他文件后缀 + */ + private static final String[] OTHER_SUFFIX = {".htm", ".html", ".php", ".jsp", ".txt", ".java", ".c", ".cpp", ".py", ".xml", ".json", ".log"}; + + public SelectFileModel(LifecycleOwner lifecycleOwner) { + super(lifecycleOwner); + } + + + @Override + public Observable> queryFile(int type) { + return Observable.>create( + subscriber -> { + switch (type) { + case Constants.FileType.IMAGE: + //读取手机图片 + subscriber.onNext(queryImages()); + break; + case Constants.FileType.VIDEO: + //视频 + subscriber.onNext(readSdCardFile(getQuerySql(VIDEO_SUFFIX))); + break; + case Constants.FileType.AUDIO: + //音频 + subscriber.onNext(readSdCardFile(getQuerySql(VOICE_SUFFIX))); + break; + case Constants.FileType.DOC: + //文档 + subscriber.onNext(readSdCardFile(getQuerySql(DOCUMENT_SUFFIX))); + break; + case Constants.FileType.PACKAGE: + //压缩包 + subscriber.onNext(readSdCardFile(getQuerySql(PACKAGE_SUFFIX))); + break; + case Constants.FileType.OTHER: + default: + //其他 + subscriber.onNext(readSdCardFile(getQuerySql(OTHER_SUFFIX))); + break; + } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + + /** + * 读取手机里的图片 + * + * @return 图片列表 + */ + @NotNull + private List queryImages() { + List imageList = new ArrayList<>(); + ContentResolver mContentResolver = BaseApplication.i().getApplicationContext().getContentResolver(); + String[] projection = { + "_id", + MediaStore.Images.Media.DATA, + MediaStore.Images.Media.BUCKET_DISPLAY_NAME, + MediaStore.Images.Media.DISPLAY_NAME, + MediaStore.Images.Media.DATE_TAKEN, + MediaStore.Images.Media.SIZE}; + + Cursor mCursor = mContentResolver.query( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + projection, "(" + + MediaStore.Images.Media.MIME_TYPE + "=? or " + + MediaStore.Images.Media.MIME_TYPE + "=? or " + + MediaStore.Images.Media.MIME_TYPE + "=? or " + + MediaStore.Images.Media.MIME_TYPE + "=? ) and _size > 0", + new String[]{"image/jpg", "image/png", "image/jpeg"}, + MediaStore.Images.Media.DATE_TAKEN + " desc"); + + if (mCursor != null) { + while (mCursor.moveToNext()) { + FileInfo fileInfo = new FileInfo(); + String path = mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.Media.DATA)); + String displayName = mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)); + int size = mCursor.getInt(mCursor.getColumnIndex(MediaStore.Images.Media.SIZE)); + fileInfo.setFilePath(path); + fileInfo.setFileSize(size); + fileInfo.setFileName(displayName); + imageList.add(fileInfo); + } + mCursor.close(); + } + return imageList; + } + + + /** + * 生成读手机影音SQL + * + * @param suffixList 文件后缀 + * @return 查询SQL + */ + @NotNull + private String getQuerySql(@NotNull String[] suffixList) { + StringBuilder sb = new StringBuilder(); + sb.append("("); + for (String suffix : suffixList) { + sb.append(MediaStore.Files.FileColumns.DATA); + sb.append(" LIKE '%").append(suffix).append("' OR "); + } + return sb.substring(0, sb.length() - 3) + ")"; + } + + + /** + * 读取手机中的文件 + * + * @param sql 查询SQL + * @return 文件列表 + */ + @NotNull + private List readSdCardFile(String sql) { + List result = new ArrayList<>(); + String[] columns = new String[]{ + MediaStore.Files.FileColumns._ID, + MediaStore.Files.FileColumns.MIME_TYPE, + MediaStore.Files.FileColumns.SIZE, + MediaStore.Files.FileColumns.DATE_MODIFIED, + MediaStore.Files.FileColumns.DATA}; + ContentResolver contentResolver = BaseApplication.i().getContentResolver(); + Cursor cursor = contentResolver.query(MediaStore.Files.getContentUri("external"), + columns, sql, null, null); + if (cursor != null) { + int columnIndexOrThrowData = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA); + while (cursor.moveToNext()) { + String path = cursor.getString(columnIndexOrThrowData); + FileInfo document = formatFile(new File(path)); + //去除空文件 + if (document.getFileSize() > 0) { + result.add(document); + } + } + cursor.close(); + } + return result; + } + + /** + * 格式化读取的文件 + * + * @param file 源文件 + * @return 格式化以后的文件 + */ + @NotNull + private FileInfo formatFile(@NotNull File file) { + FileInfo fileInfo = new FileInfo(); + fileInfo.setFileName(file.getName()); + fileInfo.setFilePath(file.getPath()); + fileInfo.setFileSize(file.length()); + fileInfo.setTime(FileUtils.getFileLastModifiedTime(file)); + return fileInfo; + } +} diff --git a/common_base/src/main/java/com/wss/common/view/gallery/IOThread.java b/common_base/src/main/java/com/wss/common/view/gallery/IOThread.java new file mode 100644 index 0000000..6fc9f96 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/view/gallery/IOThread.java @@ -0,0 +1,22 @@ +package com.wss.common.view.gallery; + +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Describe:耗时逻辑处理线程 + * Created by 吴天强 on 2018年11月16日 + */ +class IOThread { + + private static ExecutorService singleThread; + + static Executor getSingleThread() { + if (singleThread == null) { + singleThread = Executors.newSingleThreadExecutor(); + } + return singleThread; + } + +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/view/gallery/ImageDownloader.java b/common_base/src/main/java/com/wss/common/view/gallery/ImageDownloader.java new file mode 100644 index 0000000..4fb1625 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/view/gallery/ImageDownloader.java @@ -0,0 +1,21 @@ +package com.wss.common.view.gallery; + +import android.app.Activity; + +import java.io.File; + +/** + * Describe:图片查看器 下载图片抽象接口 + * Created by 吴天强 on 2018年11月16日 + */ +public interface ImageDownloader { + + /** + * 下载 + * + * @param url URL + * @param activity activity + * @return File + */ + File downLoad(String url, Activity activity); +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/view/gallery/ImageGallery.java b/common_base/src/main/java/com/wss/common/view/gallery/ImageGallery.java new file mode 100644 index 0000000..60c257a --- /dev/null +++ b/common_base/src/main/java/com/wss/common/view/gallery/ImageGallery.java @@ -0,0 +1,36 @@ +package com.wss.common.view.gallery; + + +import com.wss.common.base.bean.BaseBean; + +import lombok.Getter; +import lombok.Setter; + +/** + * Describe:缩放图片Bean + * Created by 吴天强 on 2019/7/31. + */ +@Getter +@Setter +public class ImageGallery extends BaseBean { + /** + * 图片描述 + */ + private String describe; + /** + * 图片地址 如果是本地图片 则是图片文件的绝对路径 + */ + private String url; + + public ImageGallery() { + } + + public ImageGallery(String url) { + this.url = url; + } + + public ImageGallery(String describe, String url) { + this.describe = describe; + this.url = url; + } +} diff --git a/common_base/src/main/java/com/wss/common/view/gallery/ImageGalleryActivity.java b/common_base/src/main/java/com/wss/common/view/gallery/ImageGalleryActivity.java new file mode 100644 index 0000000..73a9d6b --- /dev/null +++ b/common_base/src/main/java/com/wss/common/view/gallery/ImageGalleryActivity.java @@ -0,0 +1,230 @@ +package com.wss.common.view.gallery; + +import android.app.Activity; +import android.net.Uri; +import android.os.Bundle; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.TextView; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.target.Target; +import com.davemorrissey.labs.subscaleview.ImageSource; +import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView; +import com.orhanobut.logger.Logger; +import com.wss.common.base.BaseFullScreenActivity; +import com.wss.common.base.R; +import com.wss.common.base.R2; +import com.wss.common.base.mvp.BasePresenter; +import com.wss.common.bean.Event; +import com.wss.common.constants.Dic; +import com.wss.common.constants.EventAction; +import com.wss.common.utils.EventBusUtils; +import com.wss.common.utils.ToastUtils; +import com.wss.common.utils.ValidUtils; +import com.wss.common.widget.ActionBar; +import com.wss.common.widget.IconFontTextView; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ExecutionException; + +import androidx.annotation.Nullable; +import androidx.viewpager.widget.ViewPager; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.Unbinder; + +/** + * Describe:图片画廊 + * Created by 吴天强 on 2020/4/29. + */ +@SuppressWarnings("unchecked") +public class ImageGalleryActivity extends BaseFullScreenActivity { + + @BindView(R2.id.rl_title_bar) + View titleBarLayout; + + @BindView(R2.id.actionbar) + ActionBar actionBar; + + @BindView(R2.id.view_pager) + ViewPager mViewPager; + + @BindView(R2.id.tv_img_count) + TextView tvImgCount; + + @BindView(R2.id.tv_describe) + TextView tvDescribe; + + private Unbinder butterKnifeBind; + private Activity activity; + + /*** + * 图片集合 + */ + private List imageList = new ArrayList<>(); + private List mViews = new ArrayList<>(); + + private int currentPosition; + + private ImageViewerAdapter mAdapter; + /** + * 是否是本地图片 + */ + private boolean isLocalImage; + /** + * 是否全屏显示 + */ + private boolean isFullScreen; + + @Override + protected BasePresenter createPresenter() { + return null; + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + isFullScreen = getIntent().getBooleanExtra(Dic.FULL_SCREEN, false); + if (isFullScreen) { + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_image_gallery); + activity = this; + butterKnifeBind = ButterKnife.bind(this); + Collection serializableExtra = (Collection) getIntent().getSerializableExtra(Dic.IMAGE_LIST); + if (serializableExtra != null) { + imageList.addAll(serializableExtra); + } else { + ToastUtils.show(context, getString(R.string.please_setting_image)); + finish(); + } + isLocalImage = getIntent().getBooleanExtra(Dic.IMAGE_LOCAL, false); + currentPosition = getIntent().getIntExtra(Dic.IMAGE_POSITION, 0); + loadImage(); + mAdapter = new ImageViewerAdapter(mViews); + mViewPager.setAdapter(mAdapter); + mViewPager.setCurrentItem(currentPosition); + mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + } + + @Override + public void onPageSelected(int position) { + currentPosition = position; + setImageChangeText(); + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + }); + if (!isFullScreen) { + titleBarLayout.setVisibility(View.VISIBLE); + tvImgCount.setVisibility(View.GONE); + IconFontTextView rightIcon = new IconFontTextView(context); + rightIcon.setTextColor(getResources().getColor(R.color.color_333333)); + rightIcon.setTextSize(14); + rightIcon.setValue(getString(R.string.icon_delete)); + rightIcon.setOnClickListener(v -> { + //删除照片发个通知出去 带上删除的position + ToastUtils.show(context, getString(R.string.delete_success)); + EventBusUtils.sendEvent(new Event(EventAction.IMAGE_GALLERY_DELETE, currentPosition)); + imageList.remove(currentPosition); + int size = imageList.size(); + if (size < 1) { + finish(); + return; + } + mViewPager.removeView(mViews.remove(currentPosition)); + mAdapter.notifyDataSetChanged(); + setImageChangeText(); + }); + actionBar.setRightView(rightIcon); + actionBar.setLeftIcon(R.drawable.ic_back_black, v -> { + + }); + actionBar.setCenterTextColor(R.color.color_333333); + //加粗 + actionBar.setCenterTextBold(true); + actionBar.showBackImg(true); + } + setImageChangeText(); + } + + private void setImageChangeText() { + if (isFullScreen) { + tvImgCount.setText(String.format("%s/%s", currentPosition + 1, imageList.size())); + } else { + actionBar.setCenterText(String.format("%s/%s", currentPosition + 1, imageList.size())); + } + tvDescribe.setText(imageList.get(currentPosition).getDescribe()); + } + + private void loadImage() { + for (ImageGallery imageGallery : imageList) { + SubsamplingScaleImageView scaleImageView = new SubsamplingScaleImageView(context); + scaleImageView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + if (isFullScreen) { + scaleImageView.setOnClickListener(v -> finish()); + } + + if (isLocalImage) { + scaleImageView.setImage(ImageSource.uri(Uri.fromFile(new File(imageGallery.getUrl())))); + } else { + if (!ValidUtils.isValid(imageGallery.getUrl())) { + return; + } + IOThread.getSingleThread().execute(() -> { + File downLoadFile; + try { + downLoadFile = mImageDownloader.downLoad(imageGallery.getUrl(), activity); + //如果图片路径不为空则开始执行 + if (ValidUtils.isValid(downLoadFile)) { + activity.runOnUiThread(() -> scaleImageView.setImage(ImageSource.uri(Uri.fromFile(downLoadFile)))); + } + } catch (Exception e) { + e.printStackTrace(); + Logger.e("加载图片异常" + e.getMessage()); + } + }); + } + mViews.add(scaleImageView); + } + + } + + /** + * 图片下载器 + */ + private ImageDownloader mImageDownloader = (url, activity) -> { + File file = null; + try { + file = Glide.with(activity) + .load(url) + .downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) + .get(); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + Logger.e("下载图片异常" + e.getMessage()); + } + return file; + }; + + @Override + protected void onDestroy() { + super.onDestroy(); + if (butterKnifeBind != null) { + butterKnifeBind.unbind(); + } + } +} diff --git a/common_base/src/main/java/com/wss/common/view/gallery/ImageViewerAdapter.java b/common_base/src/main/java/com/wss/common/view/gallery/ImageViewerAdapter.java new file mode 100644 index 0000000..149a774 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/view/gallery/ImageViewerAdapter.java @@ -0,0 +1,58 @@ +package com.wss.common.view.gallery; + +import android.view.View; +import android.view.ViewGroup; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +import androidx.annotation.NonNull; +import androidx.viewpager.widget.PagerAdapter; + +/** + * Describe:图片查看器适配器 + * Created by 吴天强 on 2018年11月16日 + */ +class ImageViewerAdapter extends PagerAdapter { + private List views; + + ImageViewerAdapter(List views) { + this.views = views; + } + + @NonNull + @Override + public Object instantiateItem(@NonNull ViewGroup container, int position) { + container.addView(views.get(position)); + return views.get(position); + } + + @Override + public void destroyItem(@NonNull ViewGroup container, int position, @NotNull Object object) { + if (position == 0 && views.size() == 0) { + return; + } + if (position == views.size()) { + container.removeView(views.get(--position)); + } else { + container.removeView(views.get(position)); + } + } + + @Override + public int getCount() { + return views.size(); + } + + @Override + public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { + return view == object; + } + + @Override + public int getItemPosition(@NonNull Object object) { + return POSITION_NONE; + } + +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/view/scan/ScanActivity.java b/common_base/src/main/java/com/wss/common/view/scan/ScanActivity.java new file mode 100644 index 0000000..b071433 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/view/scan/ScanActivity.java @@ -0,0 +1,106 @@ +package com.wss.common.view.scan; + +import android.view.View; + +import com.orhanobut.logger.Logger; +import com.wss.common.base.BaseActivity; +import com.wss.common.base.R; +import com.wss.common.base.R2; +import com.wss.common.bean.Event; +import com.wss.common.constants.EventAction; +import com.wss.common.utils.EventBusUtils; +import com.wss.common.utils.PermissionsUtils; +import com.wss.common.utils.ToastUtils; + +import java.util.Arrays; + +import butterknife.BindView; +import butterknife.OnClick; +import me.devilsen.czxing.code.BarcodeFormat; +import me.devilsen.czxing.view.ScanBoxView; +import me.devilsen.czxing.view.ScanListener; +import me.devilsen.czxing.view.ScanView; + + +/** + * Describe:扫一扫首页 + * Created by 吴天强 on 2019/12/27. + */ +public class ScanActivity extends BaseActivity { + + @BindView(R2.id.qr_code_view) + ScanView qrCodeView; + + @Override + protected void initView() { + PermissionsUtils.checkCamera(this).subscribe( + aBoolean -> { + if (aBoolean) { + qrCodeView.setScanListener(new ScanListener() { + @Override + public void onScanSuccess(String result, BarcodeFormat format) { + Logger.e("二维码扫描结果:" + result); + ToastUtils.show(result); + EventBusUtils.sendEvent(new Event<>(EventAction.SCAN_QR_CODE_RESULT, result)); + finish(); + } + + @Override + public void onOpenCameraError() { + ToastUtils.show(context, "打开相机出错"); + } + }); + ScanBoxView scanBox = qrCodeView.getScanBox(); + scanBox.setCornerColor(getResources().getColor(R.color.theme));//扫描四角颜色 + scanBox.setScanLineColor(Arrays.asList(getResources().getColor(R.color.theme), + getResources().getColor(R.color.theme), getResources().getColor(R.color.theme)));//扫描线条颜色 + + } else { + finish(); + } + } + ); + } + + + @OnClick(R2.id.iv_back) + public void onClick(View v) { + finish(); + } + + @Override + protected void onResume() { + super.onResume(); + if (qrCodeView != null) { + // 打开后置摄像头开始预览,但是并未开始识别 + qrCodeView.openCamera(); + // 显示扫描框,并开始识别 + qrCodeView.startScan(); + } + } + + @Override + protected void onPause() { + super.onPause(); + if (qrCodeView != null) { + qrCodeView.stopScan(); + //关闭摄像头预览,并且隐藏扫描框 + qrCodeView.closeCamera(); + } + } + + + @Override + protected void onDestroy() { + super.onDestroy(); + if (qrCodeView != null) { + qrCodeView.onDestroy(); + } + } + + @Override + protected int getLayoutId() { + return R.layout.activity_scan; + } + +} diff --git a/common_base/src/main/java/com/wss/common/widget/ActionBar.java b/common_base/src/main/java/com/wss/common/widget/ActionBar.java index bd62685..3e6d4be 100644 --- a/common_base/src/main/java/com/wss/common/widget/ActionBar.java +++ b/common_base/src/main/java/com/wss/common/widget/ActionBar.java @@ -2,7 +2,7 @@ import android.app.Activity; import android.content.Context; -import android.support.v4.content.ContextCompat; +import android.graphics.Typeface; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; @@ -14,14 +14,16 @@ import com.wss.common.base.R; import com.wss.common.utils.PxUtils; +import androidx.core.content.ContextCompat; + /** * Describe:自定义的ActionBar * Created by 吴天强 on 2017/8/22. */ - public class ActionBar extends RelativeLayout { + private LinearLayout llActionbar; private LinearLayout llLeft; private LinearLayout llCenter; private LinearLayout llRight; @@ -34,16 +36,20 @@ public class ActionBar extends RelativeLayout { /** * 字体颜色 */ - private int leftTextColor = R.color.white; - private int centerTextColor = R.color.white; - private int rightTextColor = R.color.white; + private int leftTextColor = R.color.black; + private int centerTextColor = R.color.black; + private int rightTextColor = R.color.black; /** * 字体大小 */ private int lefTextSize = 16; - private int centerTextSize = 16; + private int centerTextSize = 18; private int rightTextSize = 16; + /** + * 中间显示字体是否加粗 + */ + private boolean isCenterTextBold; public ActionBar(Context context) { this(context, null); @@ -80,10 +86,19 @@ public void setRightTextSize(int rightTextSize) { this.rightTextSize = rightTextSize; } + /** + * 是否加粗中间显示的字体 + * + * @param centerTextBold 是否加粗 + */ + public void setCenterTextBold(boolean centerTextBold) { + isCenterTextBold = centerTextBold; + } + /** * 获取跟布局 */ - public View getRootView() { + public View getActionBarRootView() { return rootView; } @@ -92,10 +107,11 @@ public View getRootView() { */ private void initView(Context context) { rootView = View.inflate(context, R.layout.action_bar, this); + llActionbar = findViewById(R.id.ll_actionbar); llLeft = findViewById(R.id.ll_actionbar_left); llCenter = findViewById(R.id.ll_actionbar_centre); llRight = findViewById(R.id.ll_actionbar_right); - showBackImg(); + showBackImg(true); } /** @@ -105,12 +121,15 @@ private void initView(Context context) { * @param l 监听器 */ public void setLeftIcon(int res, OnClickListener l) { - setViewVisibility(llLeft, true); + llLeft.setVisibility(VISIBLE); + llLeft.setVisibility(VISIBLE); llLeft.removeAllViews(); - ImageView ivLeft = new ImageView(getContext()); + ImageView ivLeft = getImageView(); ivLeft.setImageResource(res); llLeft.addView(ivLeft); - llLeft.setOnClickListener(l); + if (l != null) { + llLeft.setOnClickListener(l); + } } /** @@ -120,9 +139,9 @@ public void setLeftIcon(int res, OnClickListener l) { * @param l 监听器 */ public void setCenterIcon(int res, OnClickListener l) { - setViewVisibility(llCenter, true); + llCenter.setVisibility(VISIBLE); llCenter.removeAllViews(); - ImageView center = new ImageView(getContext()); + ImageView center = getImageView(); center.setImageResource(res); llCenter.addView(center); llCenter.setOnClickListener(l); @@ -135,12 +154,14 @@ public void setCenterIcon(int res, OnClickListener l) { * @param l 监听器 */ public void setRightIcon(int res, OnClickListener l) { - setViewVisibility(llRight, true); + llRight.setVisibility(VISIBLE); llRight.removeAllViews(); - ImageView right = new ImageView(getContext()); + ImageView right = getImageView(); right.setImageResource(res); llRight.addView(right); - llRight.setOnClickListener(l); + if (l != null) { + llRight.setOnClickListener(l); + } } /** @@ -150,7 +171,7 @@ public void setRightIcon(int res, OnClickListener l) { * @param l 监听器 */ public void setLeftText(CharSequence text, OnClickListener l) { - setViewVisibility(llLeft, true); + llLeft.setVisibility(VISIBLE); llLeft.removeAllViews(); TextView left = getTextView(); left.setText(text); @@ -179,7 +200,7 @@ public void setLeftText(CharSequence text) { * @param l 监听器 */ public void setCenterText(CharSequence text, OnClickListener l) { - setViewVisibility(llCenter, true); + llCenter.setVisibility(VISIBLE); llCenter.removeAllViews(); TextView center = getTextView(); center.setTextSize(centerTextSize); @@ -187,6 +208,9 @@ public void setCenterText(CharSequence text, OnClickListener l) { center.setEllipsize(TextUtils.TruncateAt.END); center.setMaxLines(1); center.setTextColor(getResources().getColor(centerTextColor)); + if (isCenterTextBold) { + center.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); + } llCenter.addView(center); if (l != null) { llCenter.setOnClickListener(l); @@ -210,7 +234,7 @@ public void setCenterText(CharSequence text) { * @param l 监听器 */ public void setRightText(CharSequence text, OnClickListener l) { - setViewVisibility(llRight, true); + llRight.setVisibility(VISIBLE); llRight.removeAllViews(); TextView tvRight = getTextView(); tvRight.setText(text); @@ -222,6 +246,7 @@ public void setRightText(CharSequence text, OnClickListener l) { } } + /** * 设置右边文字 * @@ -237,7 +262,7 @@ public void setRightText(CharSequence text) { * * @return View */ - public View getRightView() { + public LinearLayout getRightView() { return llRight; } @@ -246,7 +271,7 @@ public View getRightView() { * * @return View */ - public View getCenterView() { + public LinearLayout getCenterView() { return llCenter; } @@ -255,7 +280,7 @@ public View getCenterView() { * * @return View */ - public View getLeftView() { + public LinearLayout getLeftView() { return llLeft; } @@ -265,7 +290,7 @@ public View getLeftView() { * @param v v */ public void setLeftView(View v) { - setViewVisibility(llLeft, true); + llLeft.setVisibility(VISIBLE); llLeft.removeAllViews(); llLeft.addView(v); } @@ -276,7 +301,7 @@ public void setLeftView(View v) { * @param v v */ public void setCenterView(View v) { - setViewVisibility(llCenter, true); + llCenter.setVisibility(VISIBLE); llCenter.removeAllViews(); llCenter.addView(v); } @@ -287,36 +312,42 @@ public void setCenterView(View v) { * @param v v */ public void setRightView(View v) { - setViewVisibility(llRight, true); + setRightView(v, null); + } + + public void setRightView(View v, OnClickListener listener) { llRight.removeAllViews(); llRight.addView(v); + if (listener != null) { + v.setOnClickListener(listener); + } } /** * 显示默认的左边的按钮 */ - public void showBackImg() { - setLeftIcon(R.drawable.ic_back, new OnClickListener() { - @Override - public void onClick(View v) { + public void showBackImg(boolean show) { + if (show) { + setLeftIcon(R.drawable.ic_back_black, v -> { Context ctx = ActionBar.this.getContext(); if (ctx instanceof Activity) { ((Activity) ctx).onBackPressed(); } - } - }); + }); + } else { + llLeft.setVisibility(INVISIBLE); + } } + /** - * 设置View是否显示 + * ActionBar整个替换 * - * @param v - * @param visibility 是否显示 + * @param v v */ - private void setViewVisibility(View v, boolean visibility) { - if (v != null) { - v.setVisibility(visibility ? VISIBLE : INVISIBLE); - } + public void setActionBarView(View v) { + llActionbar.removeAllViews(); + llActionbar.addView(v); } /** @@ -324,7 +355,8 @@ private void setViewVisibility(View v, boolean visibility) { */ public TextView getTextView() { TextView tv = new TextView(getContext()); - tv.setTextSize(16); + tv.setTextSize(centerTextSize); + tv.setTextColor(ContextCompat.getColor(getContext(), centerTextColor)); return tv; } @@ -333,7 +365,7 @@ public TextView getTextView() { */ public ImageView getImageView() { ImageView iv = new ImageView(getContext()); - iv.setLayoutParams(new LayoutParams(PxUtils.dp2px(getContext(), 25), PxUtils.dp2px(getContext(), 25))); + iv.setLayoutParams(new LayoutParams(PxUtils.dp2px(24), PxUtils.dp2px(24))); return iv; } diff --git a/common_base/src/main/java/com/wss/common/widget/AutoTextView.java b/common_base/src/main/java/com/wss/common/widget/AutoTextView.java new file mode 100644 index 0000000..f12011e --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/AutoTextView.java @@ -0,0 +1,65 @@ +package com.wss.common.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.ViewTreeObserver; + +/** + * 自动改变其显示行数的TextView + * create by yangwei + * on 2020/6/8 17:12 + */ +public class AutoTextView extends androidx.appcompat.widget.AppCompatTextView { + public AutoTextView(Context context) { + super(context); + } + + public AutoTextView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + // 标记当前TextView的展开/收缩状态 + // true,已经展开 + // false,以及收缩 + private boolean expandableStatus = false; + private final int MAX_LINES = 2;//默认最多显示多少行 + private int lines;//如果textview内容完全神展开需要显示多少 + + private void init() { + final ViewTreeObserver mViewTreeObserver = this.getViewTreeObserver(); + + mViewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + + @Override + public boolean onPreDraw() { + // 避免重复监听 + AutoTextView.this.getViewTreeObserver().removeOnPreDrawListener(this); + lines = getLineCount(); + return true; + } + }); + this.setMaxLines(MAX_LINES); + } + + /** + * 是否展开 + * + * @param isExpand + */ + public void setExpandable(boolean isExpand) { + if (isExpand) { + + setMaxLines(lines + 1); + } else { + + setMaxLines(MAX_LINES); + } + + expandableStatus = isExpand; + } + + public boolean getExpandableStatus() { + return expandableStatus; + } +} diff --git a/common_base/src/main/java/com/wss/common/widget/CountClickView.java b/common_base/src/main/java/com/wss/common/widget/CountClickView.java index d65b611..97a0b87 100644 --- a/common_base/src/main/java/com/wss/common/widget/CountClickView.java +++ b/common_base/src/main/java/com/wss/common/widget/CountClickView.java @@ -1,6 +1,9 @@ package com.wss.common.widget; import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.text.InputType; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; @@ -11,16 +14,22 @@ import com.wss.common.base.R; import com.wss.common.base.R2; import com.wss.common.utils.PxUtils; +import com.wss.common.utils.ToastUtils; +import com.wss.common.utils.Utils; import com.wss.common.widget.dialog.AppDialog; import com.wss.common.widget.dialog.DialogType; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import androidx.core.content.ContextCompat; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; /** - * Describe:加减Viw + * Describe:加减Viw,最大支持10000,最小支持 0 * Created by 吴天强 on 2018年9月27日18:13:09 */ public class CountClickView extends LinearLayout { @@ -39,21 +48,29 @@ public class CountClickView extends LinearLayout { @BindView(R2.id.iv_minus) ImageView ivMinus; + /** + * 减 控件父类 + */ @BindView(R2.id.ll_minus) - LinearLayout llMinus;//减 控件父类 - + LinearLayout llMinus; + /** + * 加 控件父类 + */ @BindView(R2.id.ll_plus) - LinearLayout llPlus;//加 控件父类 - private Context mContext; + LinearLayout llPlus; + private int maxCount = MAX_COUNT; private int minCount = MIN_COUNT; + private int currentCount = INIT_COUNT; + private int textColor = Color.BLACK; + private int textSize = 14; + + /** + * 是否支持如输入 默认不支持 + */ + private boolean input = false; - //控件资源 - private int minusCan = R.drawable.input_minus_default; - private int minusNot = R.drawable.input_minus_disabled; - private int addCan = R.drawable.input_add_default; - private int addNot = R.drawable.input_add_disabled; - private boolean input = false;//是否支持如输入 默认不支持 + private OnClickAfterListener afterClickListener = null; public CountClickView(Context context) { this(context, null); @@ -62,174 +79,169 @@ public CountClickView(Context context) { public CountClickView(Context context, AttributeSet attrs) { super(context, attrs); - init(context); - } - - - private OnClickAfterListener afterClickListener = null; + TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CountClickView); + maxCount = a.getInt(R.styleable.CountClickView_ccvMax, maxCount); + minCount = a.getInt(R.styleable.CountClickView_ccvMin, minCount); + currentCount = a.getInt(R.styleable.CountClickView_ccvCurrent, currentCount); + input = a.getBoolean(R.styleable.CountClickView_ccvInput, input); + textColor = a.getColor(R.styleable.CountClickView_ccvTextColor, textColor); + textSize = a.getDimensionPixelSize(R.styleable.EnhanceEditText_eTextSize, textSize); + a.recycle(); + init(); + } - private void init(Context context) { - this.mContext = context; + private void init() { this.setBackgroundResource(android.R.color.transparent); - ButterKnife.bind(this, View.inflate(context, R.layout.layout_count_click_view, this)); - - tvCount.setOnClickListener(new OnClickListener() { - @Override - public void onClick(final View v) { - if (input) { - new AppDialog(mContext, DialogType.COUNT) - .setTitle("修改数量") - .setRightButton(new AppDialog.OnButtonClickListener() { - @Override - public void onClick(String val) { - tvCount.setText(val); - if (afterClickListener != null) { - afterClickListener.onAfter(getCount()); - if (getCount() == getMinCount()) { - afterClickListener.onMin(); - } - } - judgeTheViews(Integer.valueOf(val)); - } - }) - .setNumber(minCount, maxCount, getCount()) - .show(); - } - } - }); - + ButterKnife.bind(this, View.inflate(getContext(), R.layout.layout_count_click_view, this)); + setTextColor(textColor); + setTextSize(textSize); + setCurrCount(currentCount); judgeTheViews(getCount()); } - public int getCount() { - String text = tvCount.getText().toString().trim(); - if (TextUtils.isEmpty(text)) { - return INIT_COUNT; - } - return Integer.valueOf(text); - } - - @OnClick({R2.id.iv_plus, R2.id.iv_minus}) - public void onClick(View v) { - int count = Integer.valueOf(tvCount.getText().toString().trim()); - - if (R.id.iv_plus == v.getId()) { - if (count < getMaxCount()) + @OnClick({R2.id.ll_minus, R2.id.ll_plus}) + public void onClick(@NotNull View v) { + int count = getCount(); + if (R.id.ll_plus == v.getId()) { + if (count < getMaxCount()) { tvCount.setText(String.valueOf(++count)); - } - if (R.id.iv_minus == v.getId()) { - if (count > getMinCount()) + } + } else if (R.id.ll_minus == v.getId()) { + if (count > getMinCount()) { tvCount.setText(String.valueOf(--count)); + } + } else if (R.id.tv_count == v.getId()) { } - judgeTheViews(count); - if (afterClickListener != null) { - afterClickListener.onAfter(getCount()); - if (getCount() == getMinCount()) { - afterClickListener.onMin(); - } + afterClickListener.onAfter(this, getCount()); } } - private void judgeTheViews(int count) { - if (count <= getMinCount()) { - ivMinus.setImageResource(minusNot); - } else { - ivMinus.setImageResource(minusCan); - } - if (count >= getMaxCount()) { - ivPlus.setImageResource(addNot); - } else { - ivPlus.setImageResource(addCan); + @OnClick({R2.id.tv_count}) + public void onCountClick() { + if (input) { + new AppDialog.Builder(getContext()) + .setDialogType(DialogType.INPUT) + .setInputType(InputType.TYPE_CLASS_NUMBER) + .setInputDefaultText(String.valueOf(getCount())) + .setTitle("输入数量") + .setRightButton(getContext().getString(R.string.confirm), val -> { + if (Utils.isNumber(val)) { + int count = Integer.parseInt(val); + if (count > MAX_COUNT) { + count = maxCount; + ToastUtils.show("超过了设置的最大值"); + } else if (count < MIN_COUNT) { + count = MIN_COUNT; + ToastUtils.show("超过了设置的最小值"); + } + setCurrCount(count); + } + }) + .create() + .show(); } } + /** - * 设置 按钮父类的大小 + * 根据加减 设置显示的控件View + * + * @param count 数量 */ - public void setBtnParentSize(int width, int height) { - llMinus.setLayoutParams(new LinearLayout.LayoutParams(PxUtils.dp2px(mContext, width), - PxUtils.dp2px(mContext, height))); - llPlus.setLayoutParams(new LinearLayout.LayoutParams(PxUtils.dp2px(mContext, width), - PxUtils.dp2px(mContext, height))); - //如果设置了该处大小 则需要更新中间View的高度 - LinearLayout.LayoutParams layoutParams = (LayoutParams) tvCount.getLayoutParams(); - layoutParams.height = PxUtils.dp2px(mContext, height); + private void judgeTheViews(int count) { + ivMinus.setEnabled(count > getMinCount()); + ivPlus.setEnabled(count < getMaxCount()); } /** - * 设置 按钮父类背景 + * 判断最大值 + * + * @return 返回最大值 */ - public void setBtnParentBg(int bgColor) { - llMinus.setBackgroundColor(getResources().getColor(bgColor)); - llPlus.setBackgroundColor(getResources().getColor(bgColor)); + @Contract(pure = true) + private int getMaxCount() { + return Math.min(maxCount, MAX_COUNT); } - /** - * 设置加减按钮大小 + * 判断最小值 + * + * @return 返回最小值 */ - public void setBtnSize(int width, int height) { - ivPlus.setLayoutParams(new LinearLayout.LayoutParams(PxUtils.dp2px(mContext, width), - PxUtils.dp2px(mContext, height))); - ivMinus.setLayoutParams(new LinearLayout.LayoutParams(PxUtils.dp2px(mContext, width), - PxUtils.dp2px(mContext, height))); + @Contract(pure = true) + private int getMinCount() { + return Math.max(minCount, MIN_COUNT); } /** - * 设置加减控件的资源文件 + * 设置监听 + * + * @param afterClickListener l */ - public void setButtonRes(int minusCan, int minusNot, int addCan, int addNot) { - this.minusCan = minusCan; - this.minusNot = minusNot; - this.addCan = addCan; - this.addNot = addNot; - //给按钮设置默认值 - ivMinus.setImageResource(minusCan); - ivPlus.setImageResource(addCan); + public void setAfterClickListener(OnClickAfterListener afterClickListener) { + this.afterClickListener = afterClickListener; } - /*** - * 设置中间View一些属性 - * @param bgColor 背景色 - * @param textColor 字体颜色 使用默认颜色 给0 即可 - * @param marginLeft marginLeft - * @param marginRight marginRight + /** + * 设置是否支出输入 默认不支持 + * + * @param input 输入 */ - public void setCountViewAttr(int bgColor, int textColor, int marginLeft, int marginRight) { - LinearLayout.LayoutParams layoutParams = (LayoutParams) tvCount.getLayoutParams(); - layoutParams.setMargins(PxUtils.dp2px(mContext, marginLeft), 0, PxUtils.dp2px(mContext, marginRight), 0); - tvCount.setBackgroundColor(getResources().getColor(bgColor)); - if (textColor != 0) { - tvCount.setTextColor(getResources().getColor(textColor)); - } + public void setInput(boolean input) { + this.input = input; } - /** - * 是否支出输入 + * 设置文字颜色 + * + * @param textColor 颜色 */ - public void setInput(boolean input) { - this.input = input; + public void setTextColor(int textColor) { + this.textColor = textColor; + tvCount.setTextColor(textColor); } + /** + * 设置文字大小 + * + * @param textSize 大小 + */ + public void setTextSize(int textSize) { + this.textSize = textSize; + tvCount.setTextSize(this.textSize); + } + /** + * 设置默认显示的数量 + * + * @param count 数量 + */ public void setCurrCount(int count) { tvCount.setText(String.valueOf(count)); judgeTheViews(count); } + /** + * 设置最大值 + * + * @param maxCount 最大值 + */ public void setMaxCount(int maxCount) { if (maxCount < getMinCount()) { maxCount = INIT_COUNT; } - this.maxCount = maxCount; judgeTheViews(getCount()); } + /** + * 设置最小值 + * + * @param minCount 最小值 + */ public void setMinCount(int minCount) { if (minCount > getMaxCount()) { minCount = INIT_COUNT; @@ -239,23 +251,88 @@ public void setMinCount(int minCount) { } - private int getMaxCount() { - return maxCount < MAX_COUNT ? maxCount : MAX_COUNT; + /** + * 返回操作后的数量 + * + * @return 数量 + */ + public int getCount() { + String text = tvCount.getText().toString().trim(); + if (TextUtils.isEmpty(text)) { + return INIT_COUNT; + } + return Integer.parseInt(text); } - private int getMinCount() { - return minCount > MIN_COUNT ? minCount : MIN_COUNT; + + /** + * 设置 按钮父类的大小 + */ + public void setBtnParentSize(int width, int height) { + llMinus.setLayoutParams(new LinearLayout.LayoutParams(PxUtils.dp2px(width), PxUtils.dp2px(height))); + llPlus.setLayoutParams(new LinearLayout.LayoutParams(PxUtils.dp2px(width), PxUtils.dp2px(height))); + //如果设置了该处大小 则需要更新中间View的高度 + LinearLayout.LayoutParams layoutParams = (LayoutParams) tvCount.getLayoutParams(); + layoutParams.height = PxUtils.dp2px(height); } - public interface OnClickAfterListener { + /** + * 设置 按钮父类背景 + */ + public void setBtnParentBg(int bgColor) { + llMinus.setBackgroundColor(ContextCompat.getColor(getContext(), bgColor)); + llPlus.setBackgroundColor(ContextCompat.getColor(getContext(), bgColor)); + } - void onAfter(int value); - void onMin(); + /** + * 设置加减按钮大小 + */ + public void setBtnSize(int width, int height) { + ivPlus.setLayoutParams(new LinearLayout.LayoutParams(PxUtils.dp2px(width), + PxUtils.dp2px(height))); + ivMinus.setLayoutParams(new LinearLayout.LayoutParams(PxUtils.dp2px(width), + PxUtils.dp2px(height))); } - public void setAfterClickListener(OnClickAfterListener afterClickListener) { - this.afterClickListener = afterClickListener; + /** + * 设置加减控件的资源文件 + */ + public void setButtonRes(int addDrawable, int minusDrawable) { + ivPlus.setBackgroundResource(addDrawable); + ivMinus.setBackgroundResource(minusDrawable); + } + + + /*** + * 设置中间View一些属性 + * @param bgColor 背景色 + * @param textColor 字体颜色 使用默认颜色 给0 即可 + * @param marginLeft marginLeft + * @param marginRight marginRight + */ + public void setCountViewAttr(int bgColor, int textColor, int marginLeft, int marginRight) { + LinearLayout.LayoutParams layoutParams = (LayoutParams) tvCount.getLayoutParams(); + layoutParams.setMargins(PxUtils.dp2px(marginLeft), 0, PxUtils.dp2px(marginRight), 0); + tvCount.setBackgroundColor(ContextCompat.getColor(getContext(), bgColor)); + if (textColor != 0) { + tvCount.setTextColor(ContextCompat.getColor(getContext(), textColor)); + } + } + + /** + * 操作之后监听回调 + */ + public interface OnClickAfterListener { + + /** + * 操作之后 + * + * @param view CountClickView + * @param value 值 + */ + void onAfter(CountClickView view, int value); } + } diff --git a/common_base/src/main/java/com/wss/common/widget/EnhanceEditText.java b/common_base/src/main/java/com/wss/common/widget/EnhanceEditText.java new file mode 100644 index 0000000..d34cb5a --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/EnhanceEditText.java @@ -0,0 +1,388 @@ +package com.wss.common.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.text.Editable; +import android.text.InputFilter; +import android.text.InputType; +import android.text.TextWatcher; +import android.text.method.PasswordTransformationMethod; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; +import android.widget.LinearLayout; + +import com.wss.common.base.R; +import com.wss.common.base.R2; +import com.wss.common.utils.KeyboardUtils; +import com.wss.common.utils.ValidUtils; + +import androidx.annotation.Nullable; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; + +/** + * Describe 增强版输入框,左右带图标 + * Created by 吴天强 on 2020/4/20. + */ +public class EnhanceEditText extends LinearLayout { + /** + * 无操作 + */ + public static final int ACTION_NONE = 0; + /** + * 搜索事件 + */ + public static final int ACTION_SEARCH = 1; + /** + * 下一步 + */ + public static final int ACTION_NEXT = 2; + /** + * 完成 + */ + public static final int ACTION_DONE = 3; + + /** + * 输入文本 + */ + public static final int INPUT_TEXT = 0; + /** + * 输入密码 + */ + public static final int INPUT_PASSWORD = 1; + + /** + * 输入手机 + */ + public static final int INPUT_PHONE = 2; + /** + * 输入数字 + */ + public static final int INPUT_NUMBER = 3; + + + @BindView(R2.id.ll_edt_view) + LinearLayout llEdtView; + + @BindView(R2.id.iftv_left) + IconFontTextView iftvLeft; + + @BindView(R2.id.edt_text) + EditText editText; + + @BindView(R2.id.iftv_clean) + IconFontTextView iftvClean; + + @BindView(R2.id.ll_clean_view) + View cleanView; + + private int textColor = 0xFF333333; + private int hintColor = 0XFF999999; + private int leftIconColor = 0XFF999999; + private int cleanIconColor = 0XFF999999; + private String leftIcon; + private String cleanIcon; + private String hintText; + private int textSize = 14; + /** + * 背景资源 + */ + private int bgResource = R.drawable.bg_of_color_f5_4radius; + /** + * 是否显示清除按钮 默认true + */ + private boolean showCleanIcon = true; + /** + * 是否现在左侧icon 默认true + */ + private boolean showLeftIcon = true; + /** + * 是否显示背景 默认true + */ + private boolean showBackground = true; + /** + * 键盘最后执行的Action + */ + private int action = ACTION_NONE; + /** + * 输入框类型 + */ + private int inputType = INPUT_TEXT; + private int maxLength; + private int maxLines; + + private OnTextChangeListener onTextChangeListener; + private OnActionClickListener onActionClickListener; + + + public EnhanceEditText(Context context) { + this(context, null); + } + + public EnhanceEditText(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public EnhanceEditText(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + ButterKnife.bind(this, View.inflate(getContext(), R.layout.layout_search, this)); + TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.EnhanceEditText); + textColor = a.getColor(R.styleable.EnhanceEditText_eTextColor, textColor); + hintColor = a.getColor(R.styleable.EnhanceEditText_eHintTextColor, hintColor); + leftIcon = a.getString(R.styleable.EnhanceEditText_eLeftIconFont); + leftIconColor = a.getColor(R.styleable.EnhanceEditText_eLeftIconColor, leftIconColor); + cleanIcon = a.getString(R.styleable.EnhanceEditText_eCleanIconFont); + cleanIconColor = a.getColor(R.styleable.EnhanceEditText_eCleanIconColor, cleanIconColor); + hintText = a.getString(R.styleable.EnhanceEditText_eHintText); + textSize = a.getDimensionPixelSize(R.styleable.EnhanceEditText_eTextSize, textSize); + showCleanIcon = a.getBoolean(R.styleable.EnhanceEditText_eShowClean, showCleanIcon); + bgResource = a.getResourceId(R.styleable.EnhanceEditText_eBg, bgResource); + showLeftIcon = a.getBoolean(R.styleable.EnhanceEditText_eShowLeft, showLeftIcon); + showBackground = a.getBoolean(R.styleable.EnhanceEditText_eShowBg, showBackground); + action = a.getInt(R.styleable.EnhanceEditText_eAction, action); + inputType = a.getInt(R.styleable.EnhanceEditText_eInputType, inputType); + maxLength = a.getInt(R.styleable.EnhanceEditText_eMaxLength, maxLength); + maxLines = a.getInt(R.styleable.EnhanceEditText_eMaxLines, maxLines); + a.recycle(); + initView(); + } + + private void initView() { + setGravity(Gravity.CENTER_VERTICAL); + if (showBackground) { + setBackgroundResource(bgResource); + } + if (showLeftIcon && ValidUtils.isValid(leftIcon)) { + iftvLeft.setValue(leftIcon); + iftvLeft.setTextColor(leftIconColor); + } else { + iftvLeft.setVisibility(VISIBLE); + } + if (showCleanIcon && ValidUtils.isValid(cleanIcon)) { + iftvClean.setValue(cleanIcon); + iftvClean.setTextColor(cleanIconColor); + } + editText.setHint(hintText); + editText.setTextSize(textSize); + editText.setTextColor(textColor); + editText.setHintTextColor(hintColor); + editText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + if (onTextChangeListener != null) { + onTextChangeListener.beforeTextChanged(s, start, count, after); + } + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + if (onTextChangeListener != null) { + onTextChangeListener.onTextChanged(s, start, before, count); + } + } + + @Override + public void afterTextChanged(Editable s) { + if (onTextChangeListener != null) { + onTextChangeListener.afterTextChanged(s); + } + if (showCleanIcon) { + cleanView.setVisibility(s.toString().length() > 0 ? VISIBLE : GONE); + } + } + }); + setInputFiller(); + setOnEditorAction(); + } + + /** + * 设置输入框的一些属性 + */ + private void setInputFiller() { + switch (inputType) { + case INPUT_PASSWORD: + editText.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD); + editText.setTransformationMethod(PasswordTransformationMethod.getInstance()); + break; + case INPUT_PHONE: + editText.setInputType(InputType.TYPE_CLASS_PHONE); + break; + case INPUT_NUMBER: + editText.setInputType(InputType.TYPE_CLASS_NUMBER); + break; + case INPUT_TEXT: + default: + editText.setInputType(InputType.TYPE_CLASS_TEXT); + break; + } + if (maxLength > 0) { + editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)}); + } + if (maxLines > 0) { + editText.setMaxLines(maxLines); + } + } + + /** + * 设置输入框的执行事件 + */ + private void setOnEditorAction() { + switch (action) { + case ACTION_SEARCH: + editText.setImeOptions(EditorInfo.IME_ACTION_SEARCH); + break; + case ACTION_NEXT: + editText.setImeOptions(EditorInfo.IME_ACTION_NEXT); + break; + case ACTION_DONE: + editText.setImeOptions(EditorInfo.IME_ACTION_DONE); + break; + case ACTION_NONE: + default: + editText.setImeOptions(EditorInfo.IME_ACTION_NONE); + break; + } + + editText.setOnEditorActionListener((v, actionId, event) -> { + KeyboardUtils.hideKeyboard(v); + if (onActionClickListener == null) { + return false; + } + switch (actionId) { + case EditorInfo.IME_ACTION_SEARCH: + //搜索 + onActionClickListener.onEditorAction(ACTION_SEARCH); + break; + case EditorInfo.IME_ACTION_NEXT: + //下一步 + onActionClickListener.onEditorAction(ACTION_NEXT); + break; + case EditorInfo.IME_ACTION_DONE: + //完成 + onActionClickListener.onEditorAction(ACTION_DONE); + break; + case EditorInfo.IME_ACTION_NONE: + default: + //无操作 + onActionClickListener.onEditorAction(ACTION_NONE); + break; + } + return true; + }); + } + + @OnClick(R2.id.ll_clean_view) + public void onClean() { + getEditText().setText(""); + } + + + /** + * 返回输入框字符 + * + * @return String + */ + public String getText() { + return getEditText().getText().toString().trim(); + } + + /** + * 清除搜索框内容 + */ + public void clearSearchText() { + onClean(); + } + + /** + * 返回输入框View + * + * @return EditText + */ + public EditText getEditText() { + return editText; + } + + /** + * 设置搜索按钮点击回调 + * + * @param onActionClickListener OnEditActionCallback + */ + public void setOnActionClickListener(OnActionClickListener onActionClickListener) { + this.onActionClickListener = onActionClickListener; + } + + /** + * 设置文本输入变化监听 + * + * @param textChangeListener listener + */ + public void setTextChangeListener(OnTextChangeListener textChangeListener) { + this.onTextChangeListener = textChangeListener; + } + + /** + * 设置文本 + * + * @param text 文本 + */ + public void setText(CharSequence text) { + editText.setText(text); + } + + /** + * 设置输入框光标位置 + * + * @param position 位置 + */ + public void setSelection(int position) { + editText.setSelection(position); + } + + /** + * 文本输入变化监听 + */ + public interface OnTextChangeListener { + /** + * 输入前 + * + * @param s s + * @param start start + * @param count count + * @param after after + */ + void beforeTextChanged(CharSequence s, int start, int count, int after); + + /** + * 变化 + * + * @param s s + * @param start start + * @param before before + * @param count count + */ + void onTextChanged(CharSequence s, int start, int before, int count); + + /** + * 变化后 + * + * @param s 文本 + */ + void afterTextChanged(Editable s); + } + + /** + * 键盘搜索按钮点击 + */ + public interface OnActionClickListener { + /** + * 按钮被点击 + * + * @param action 事件ID + */ + void onEditorAction(int action); + } +} diff --git a/common_base/src/main/java/com/wss/common/widget/FlowGroupView.java b/common_base/src/main/java/com/wss/common/widget/FlowGroupView.java deleted file mode 100644 index ac16aae..0000000 --- a/common_base/src/main/java/com/wss/common/widget/FlowGroupView.java +++ /dev/null @@ -1,179 +0,0 @@ -package com.wss.common.widget; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.util.AttributeSet; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; - -import java.util.ArrayList; -import java.util.List; - -/** - * Describe:流式布局 - * Created by 吴天强 on 2018年9月27日18:13:09 - */ -public class FlowGroupView extends ViewGroup { - - /** - * 储存所有的view 按行记录 - */ - private List> mAllViews = new ArrayList>(); - /** - * 记录每一行的高度 - */ - private List mLineHeight = new ArrayList(); - private String TAG = "TAG"; - - public FlowGroupView(Context context, AttributeSet attrs, - int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public FlowGroupView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public FlowGroupView(Context context) { - super(context); - } - - /** - * 所有childView的位置的布局 - */ - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - // 当前行的最大高度 - int lineHeight = 0; - // 存储每一行所有的childView - List lineViews; - int left = 0; - int top = 0; - // 得到总行数 - int lineNums = mAllViews.size(); - for (int i = 0; i < lineNums; i++) { - // 每一行的所有的views - lineViews = mAllViews.get(i); - // 当前行的最大高度 - lineHeight = mLineHeight.get(i); -// Logger.e(TAG, "第" + i + "行 :" + lineViews.size() + " , " + lineViews); -// Logger.e(TAG, "第" + i + "行, :" + lineHeight); - - // 遍历当前行所有的View - for (int j = 0; j < lineViews.size(); j++) { - View child = lineViews.get(j); - if (child.getVisibility() == View.GONE) { - continue; - } - - //计算childView的left,top,right,bottom -// int lc = left + lp.leftMargin; -// int tc = top + lp.topMargin; - int lc = left + 2; - int tc = top + 2; - int rc = lc + child.getMeasuredWidth(); - int bc = tc + child.getMeasuredHeight(); - - child.layout(lc, tc, rc, bc); - - left += child.getMeasuredWidth() + 4/*lp.rightMargin+ lp.leftMargin*/; - } - left = 0; - top += lineHeight; - } - Log.v(TAG, "onLayout mAllViews.size() -- > " + mAllViews.size() + " mLineHeight.size() -- > " + mLineHeight.size()); - } - - @SuppressLint("DrawAllocation") - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - // 置空 view 容器 和 lineHeight 容器 重新赋值 - mAllViews.clear(); - mLineHeight.clear(); - - //得到上级容器为其推荐的宽高和计算模式 - int specWidthMode = MeasureSpec.getMode(widthMeasureSpec); - int specHeighMode = MeasureSpec.getMode(heightMeasureSpec); - int specWidthSize = MeasureSpec.getSize(widthMeasureSpec); - int specHeighSize = MeasureSpec.getSize(heightMeasureSpec); - // 计算出所有的 child 的 宽和高 - measureChildren(specWidthSize, specHeighSize); - // 记录如果是 warp_content 是设置的宽和高 - int width = 0; - int height = 0; - // 得到子view的个数 - int cCount = getChildCount(); - /** - * 记录每一行的宽度,width不断取最大宽度 - */ - int lineWidth = 0; - /** - * 每一行的高度,累加至height - */ - int lineHeight = 0; - - // 存储每一行所有的childView - List lineViews = new ArrayList<>(); - for (int i = 0; i < cCount; i++) { - // 得到每个子View - View child = getChildAt(i); - // 测量每个子View的宽高 - measureChild(child, widthMeasureSpec, heightMeasureSpec); - // 当前子view的lp -// MarginLayoutParams lp = () child.getLayoutParams(); - // 子view的宽和高 - int cWidth = 0; - int cheight = 0; - // 当前子 view 实际占的宽 - cWidth = child.getMeasuredWidth() + 4 /*lp.leftMargin + lp.rightMargin*/; - // 当前子View 实际占的高 - cheight = child.getMeasuredHeight() + 4 /*lp.topMargin + lp.bottomMargin*/; - - // 需要换行 - if (lineWidth + cWidth > specWidthSize) { - width = Math.max(lineWidth, cWidth);// 取最大值 - lineWidth = cWidth; // 开启新行的时候重新累加width - height += lineHeight; // 开启新行时累加 height - lineHeight = cheight; // 记录下一行的高度 - mAllViews.add(lineViews); - mLineHeight.add(lineHeight); - lineViews = new ArrayList(); - // 换行的时候把该 view 放进 集合里 - lineViews.add(child);// 这个 view(child) 是下一行的第一个view - Log.v(TAG, "onl mAllViews.size() -- > " + mAllViews.size()); - } else { - // 不需要换行 - lineWidth += cWidth;// - height = Math.max(lineHeight, cheight);// 取最大值 - // 不需要换行时 把子View add 进集合 - lineViews.add(child); - } - - if (i == cCount - 1) { - // 如果是最后一个view - width = Math.max(lineWidth, cWidth); - height += lineHeight; - } - } - // 循环结束后 把最后一行内容add进集合中 - mLineHeight.add(lineHeight); // 记录最后一行 - mAllViews.add(lineViews); - // MeasureSpec.EXACTLY 表示设置了精确的值 - // 如果 mode 是 MeasureSpec.EXACTLY 时候,则不是 warp_content 用计算来的值,否则则用上级布局分给它的值 - setMeasuredDimension(specWidthMode == MeasureSpec.EXACTLY ? specWidthSize : width - , specHeighMode == MeasureSpec.EXACTLY ? specHeighSize : height); - Log.v(TAG, "onLayout onMeasure mAllViews.size() -- > " + mAllViews.size() + " mLineHeight.size() -- > " + mLineHeight.size()); - } - - /** - * 这个一定要设置,否则会包强转错误 - * 设置它支持 marginLayoutParams - */ - @Override - public LayoutParams generateLayoutParams(AttributeSet attrs) { - - return new MarginLayoutParams(getContext(), attrs); - } - -} diff --git a/common_base/src/main/java/com/wss/common/widget/FlowLayout.java b/common_base/src/main/java/com/wss/common/widget/FlowLayout.java new file mode 100644 index 0000000..40ca2e3 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/FlowLayout.java @@ -0,0 +1,224 @@ +package com.wss.common.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.util.LayoutDirection; +import android.view.View; +import android.view.ViewGroup; + +import com.wss.common.base.R; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +import androidx.core.text.TextUtilsCompat; + +/** + * 流式布局 + * Created by lss on 2016/12/15. + */ +public class FlowLayout extends ViewGroup { + private static final String TAG = "FlowLayout"; + private static final int LEFT = -1; + private static final int CENTER = 0; + private static final int RIGHT = 1; + + protected List> mAllViews = new ArrayList<>(); + protected List mLineHeight = new ArrayList<>(); + protected List mLineWidth = new ArrayList<>(); + private int mGravity; + private List lineViews = new ArrayList<>(); + + public FlowLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout); + mGravity = ta.getInt(R.styleable.FlowLayout_tag_gravity, LEFT); + int layoutDirection = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()); + if (layoutDirection == LayoutDirection.RTL) { + if (mGravity == LEFT) { + mGravity = RIGHT; + } else { + mGravity = LEFT; + } + } + ta.recycle(); + } + + public FlowLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public FlowLayout(Context context) { + this(context, null); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int sizeWidth = MeasureSpec.getSize(widthMeasureSpec); + int modeWidth = MeasureSpec.getMode(widthMeasureSpec); + int sizeHeight = MeasureSpec.getSize(heightMeasureSpec); + int modeHeight = MeasureSpec.getMode(heightMeasureSpec); + + // wrap_content + int width = 0; + int height = 0; + + int lineWidth = 0; + int lineHeight = 0; + + int cCount = getChildCount(); + + for (int i = 0; i < cCount; i++) { + View child = getChildAt(i); + if (child.getVisibility() == View.GONE) { + if (i == cCount - 1) { + width = Math.max(lineWidth, width); + height += lineHeight; + } + continue; + } + measureChild(child, widthMeasureSpec, heightMeasureSpec); + MarginLayoutParams lp = (MarginLayoutParams) child + .getLayoutParams(); + + int childWidth = child.getMeasuredWidth() + lp.leftMargin + + lp.rightMargin; + int childHeight = child.getMeasuredHeight() + lp.topMargin + + lp.bottomMargin; + + if (lineWidth + childWidth > sizeWidth - getPaddingLeft() - getPaddingRight()) { + width = Math.max(width, lineWidth); + lineWidth = childWidth; + height += lineHeight; + lineHeight = childHeight; + } else { + lineWidth += childWidth; + lineHeight = Math.max(lineHeight, childHeight); + } + if (i == cCount - 1) { + width = Math.max(lineWidth, width); + height += lineHeight; + } + } + setMeasuredDimension( + // + modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width + getPaddingLeft() + getPaddingRight(), + modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height + getPaddingTop() + getPaddingBottom()// + ); + + } + + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + mAllViews.clear(); + mLineHeight.clear(); + mLineWidth.clear(); + lineViews.clear(); + + int width = getWidth(); + + int lineWidth = 0; + int lineHeight = 0; + + int cCount = getChildCount(); + + for (int i = 0; i < cCount; i++) { + View child = getChildAt(i); + if (child.getVisibility() == View.GONE){ continue;} + MarginLayoutParams lp = (MarginLayoutParams) child + .getLayoutParams(); + + int childWidth = child.getMeasuredWidth(); + int childHeight = child.getMeasuredHeight(); + + if (childWidth + lineWidth + lp.leftMargin + lp.rightMargin > width - getPaddingLeft() - getPaddingRight()) { + mLineHeight.add(lineHeight); + mAllViews.add(lineViews); + mLineWidth.add(lineWidth); + + lineWidth = 0; + lineHeight = childHeight + lp.topMargin + lp.bottomMargin; + lineViews = new ArrayList<>(); + } + lineWidth += childWidth + lp.leftMargin + lp.rightMargin; + lineHeight = Math.max(lineHeight, childHeight + lp.topMargin + + lp.bottomMargin); + lineViews.add(child); + + } + mLineHeight.add(lineHeight); + mLineWidth.add(lineWidth); + mAllViews.add(lineViews); + + + int left = getPaddingLeft(); + int top = getPaddingTop(); + + int lineNum = mAllViews.size(); + + for (int i = 0; i < lineNum; i++) { + lineViews = mAllViews.get(i); + lineHeight = mLineHeight.get(i); + + // set gravity + int currentLineWidth = this.mLineWidth.get(i); + switch (this.mGravity) { + case LEFT: + left = getPaddingLeft(); + break; + case CENTER: + left = (width - currentLineWidth) / 2 + getPaddingLeft(); + break; + case RIGHT: + // 适配了rtl,需要补偿一个padding值 + left = width - (currentLineWidth + getPaddingLeft()) - getPaddingRight(); + // 适配了rtl,需要把lineViews里面的数组倒序排 + Collections.reverse(lineViews); + break; + default: + break; + } + + for (int j = 0; j < lineViews.size(); j++) { + View child = lineViews.get(j); + if (child.getVisibility() == View.GONE) { + continue; + } + + MarginLayoutParams lp = (MarginLayoutParams) child + .getLayoutParams(); + + int lc = left + lp.leftMargin; + int tc = top + lp.topMargin; + int rc = lc + child.getMeasuredWidth(); + int bc = tc + child.getMeasuredHeight(); + + child.layout(lc, tc, rc, bc); + + left += child.getMeasuredWidth() + lp.leftMargin + + lp.rightMargin; + } + top += lineHeight; + } + + } + + @Override + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return new MarginLayoutParams(getContext(), attrs); + } + + @Override + protected LayoutParams generateDefaultLayoutParams() { + return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + } + + @Override + protected LayoutParams generateLayoutParams(LayoutParams p) { + return new MarginLayoutParams(p); + } +} diff --git a/common_base/src/main/java/com/wss/common/widget/GradientTextView.java b/common_base/src/main/java/com/wss/common/widget/GradientTextView.java new file mode 100644 index 0000000..d218b20 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/GradientTextView.java @@ -0,0 +1,56 @@ +package com.wss.common.widget; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.LinearGradient; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.Shader; +import android.util.AttributeSet; + +import androidx.appcompat.widget.AppCompatTextView; + +/** + * Describe:带渐变颜色的TextView + * Created by 吴天强 on 2019/7/1. + */ +public class GradientTextView extends AppCompatTextView { + + private Rect mTextBound = new Rect(); + private boolean gradient; + + public GradientTextView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @SuppressLint("DrawAllocation") + @Override + protected void onDraw(Canvas canvas) { + int mViewWidth = getMeasuredWidth(); + Paint mPaint = getPaint(); + String mTipText = getText().toString(); + mPaint.getTextBounds(mTipText, 0, mTipText.length(), mTextBound); + LinearGradient mLinearGradient; + if (gradient) { + mLinearGradient = new LinearGradient(0, 0, mViewWidth, 0, + new int[]{0xFF1A3AFF, 0xFF10E17D}, null, Shader.TileMode.REPEAT); + mPaint.setFakeBoldText(true); + } else { + mLinearGradient = new LinearGradient(0, 0, mViewWidth, 0, + new int[]{0xFF000000, 0xFF000000}, null, Shader.TileMode.REPEAT); + mPaint.setFakeBoldText(false); + } + mPaint.setShader(mLinearGradient); + canvas.drawText(mTipText, (getMeasuredWidth() >> 1) - (mTextBound.width() >> 1), (getMeasuredHeight() >> 1) + (mTextBound.height() >> 1), mPaint); + invalidate(); + } + + /** + * 设置是否渐变 + */ + public void setGradient(boolean gradient) { + this.gradient = gradient; + postInvalidate(); + } +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/widget/HinderListView.java b/common_base/src/main/java/com/wss/common/widget/HinderListView.java new file mode 100644 index 0000000..7327d15 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/HinderListView.java @@ -0,0 +1,102 @@ +package com.wss.common.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.widget.AbsListView; +import android.widget.ListView; + +/** + * Describe: 滑动中阻碍事件传递 当滑动到顶部 将事件传递给父类 + * Created by 吴天强 on 2018/11/1. + */ +public class HinderListView extends ListView implements AbsListView.OnScrollListener { + private float oldY; + private int currentPosition; + + public HinderListView(Context context) { + super(context); + setOnScrollListener(this); + } + + public HinderListView(Context context, AttributeSet attrs) { + super(context, attrs); + setOnScrollListener(this); + } + + public HinderListView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setOnScrollListener(this); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + switch (ev.getAction()) { + case MotionEvent.ACTION_MOVE: + float Y = ev.getY(); + float Ys = Y - oldY; + int[] location = new int[2]; + getLocationInWindow(location); + + //滑动到顶部让父控件重新获得触摸事件 + if (Ys > 0 && currentPosition == 0) { + getParent().getParent().requestDisallowInterceptTouchEvent(false); + } + break; + + case MotionEvent.ACTION_DOWN: + getParent().getParent().requestDisallowInterceptTouchEvent(true); + oldY = ev.getY(); + break; + + case MotionEvent.ACTION_UP: + getParent().getParent().requestDisallowInterceptTouchEvent(true); + break; + + default: + break; + } + return super.onInterceptTouchEvent(ev); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + switch (ev.getAction()) { + case MotionEvent.ACTION_MOVE: + float Y = ev.getY(); + float Ys = Y - oldY; + float X = ev.getX(); + int[] location = new int[2]; + getLocationInWindow(location); + + //滑动到顶部让父控件重新获得触摸事件 + if (Ys > 0 && currentPosition == 0) { + getParent().getParent().requestDisallowInterceptTouchEvent(false); + } + break; + + case MotionEvent.ACTION_DOWN: + getParent().getParent().requestDisallowInterceptTouchEvent(true); + oldY = ev.getY(); + break; + + case MotionEvent.ACTION_UP: + getParent().getParent().requestDisallowInterceptTouchEvent(true); + break; + + default: + break; + } + return super.onTouchEvent(ev); + } + + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + currentPosition = getFirstVisiblePosition(); + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + + } +} diff --git a/common_base/src/main/java/com/wss/common/widget/HinderWebView.java b/common_base/src/main/java/com/wss/common/widget/HinderWebView.java new file mode 100644 index 0000000..fff0b94 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/HinderWebView.java @@ -0,0 +1,64 @@ +package com.wss.common.widget; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.webkit.WebView; + +/** + * Describe: 滑动中阻碍事件传递 当滑动到顶部 将事件传递给父类 + * Created by 吴天强 on 2018/11/1. + */ +public class HinderWebView extends WebView { + public float oldY; + private int t; + + public HinderWebView(Context context) { + super(context); + } + + public HinderWebView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public HinderWebView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouchEvent(MotionEvent ev) { + switch (ev.getAction()) { + case MotionEvent.ACTION_MOVE: + float Y = ev.getY(); + float Ys = Y - oldY; + //滑动到顶部让父控件重新获得触摸事件 + if (Ys > 0 && t == 0) { + getParent().getParent().requestDisallowInterceptTouchEvent(false); + } + break; + + case MotionEvent.ACTION_DOWN: + getParent().getParent().requestDisallowInterceptTouchEvent(true); + oldY = ev.getY(); + break; + + case MotionEvent.ACTION_UP: + getParent().getParent().requestDisallowInterceptTouchEvent(true); + break; + + default: + break; + } + return super.onTouchEvent(ev); + } + + @Override + protected void onScrollChanged(int l, int t, int oldl, int oldt) { + this.t = t; + super.onScrollChanged(l, t, oldl, oldt); + } + +} diff --git a/common_base/src/main/java/com/wss/common/widget/IconFontTextView.java b/common_base/src/main/java/com/wss/common/widget/IconFontTextView.java new file mode 100644 index 0000000..887a699 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/IconFontTextView.java @@ -0,0 +1,94 @@ +package com.wss.common.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Typeface; +import android.util.AttributeSet; + +import com.wss.common.base.R; +import com.wss.common.utils.ValidUtils; + +import androidx.annotation.Nullable; +import androidx.appcompat.widget.AppCompatTextView; + +/** + * Describe:使用ali IconFont的TextView + * Created by 吴天强 on 2019/7/9. + */ +public class IconFontTextView extends AppCompatTextView { + + private Context context; + private String fontFile = "iconfont/iconfont.ttf"; + private String defaultText;//默认文字 + private String checkedText;//选择之后文字 + + public IconFontTextView(Context context) { + this(context, null); + } + + public IconFontTextView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + this.context = context; + if (attrs != null) { + TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.IconFontTextView); + defaultText = a.getString(R.styleable.IconFontTextView_value); + checkedText = a.getString(R.styleable.IconFontTextView_valueChecked); + setFontFile(a.getString(R.styleable.IconFontTextView_fontFile)); + a.recycle(); + } + setTextState(); + setTypeFace(); + } + + @Override + public void setSelected(boolean selected) { + super.setSelected(selected); + setTextState(); + } + + /*** + * 设置显示Value + */ + public void setValue(String defaultText) { + this.defaultText = defaultText; + setTextState(); + } + + /*** + * 设置显示Value + * @param defaultText 默认显示 + * @param checkedText 选中显示 + */ + public void setValue(String defaultText, String checkedText) { + this.defaultText = defaultText; + this.checkedText = checkedText; + setTextState(); + } + + + /** + * 设置字体库 + */ + public void setFontFile(String typeFacePath) { + if (ValidUtils.isValid(typeFacePath)) { + fontFile = typeFacePath; + } + setTypeFace(); + } + + private void setTextState() { + if (ValidUtils.isValid(checkedText)) { + setText(isSelected() ? checkedText : defaultText); + } else { + setText(defaultText); + } + } + + private void setTypeFace() { + try { + Typeface typeface = Typeface.createFromAsset(context.getAssets(), fontFile); + setTypeface(typeface); + } catch (Throwable ignored) { + } + } +} diff --git a/common_base/src/main/java/com/wss/common/widget/LVCircularRing.java b/common_base/src/main/java/com/wss/common/widget/LVCircularRing.java index 41c84d3..f3184f2 100644 --- a/common_base/src/main/java/com/wss/common/widget/LVCircularRing.java +++ b/common_base/src/main/java/com/wss/common/widget/LVCircularRing.java @@ -16,11 +16,11 @@ import com.wss.common.base.R; + /** * 自定义加载框View - * Created by wtq on 2016/12/15. + * Created by 吴天强 on 2016/12/15. */ - public class LVCircularRing extends View { private float mWidth = 0f; private float mPadding = 0f; diff --git a/common_base/src/main/java/com/wss/common/widget/MultipleItemView.java b/common_base/src/main/java/com/wss/common/widget/MultipleItemView.java index 045e8c1..1105b14 100644 --- a/common_base/src/main/java/com/wss/common/widget/MultipleItemView.java +++ b/common_base/src/main/java/com/wss/common/widget/MultipleItemView.java @@ -3,20 +3,25 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; -import android.support.annotation.Nullable; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.RelativeLayout; import android.widget.TextView; import com.wss.common.base.R; import com.wss.common.base.R2; import com.wss.common.utils.PxUtils; +import org.jetbrains.annotations.NotNull; + +import androidx.annotation.Nullable; import butterknife.BindView; import butterknife.ButterKnife; +import butterknife.OnClick; /** * Describe:多功能Item 支持左右文字显示 最右边图片显示 @@ -25,6 +30,9 @@ public class MultipleItemView extends LinearLayout { + @BindView(R2.id.rl_layout) + RelativeLayout rlLayout; + @BindView(R2.id.iv_left) ImageView ivLeft; @@ -64,6 +72,8 @@ public class MultipleItemView extends LinearLayout { private boolean showRightIcon = true; private boolean showEditText = false; + private OnIconCheckChangedListener leftIconChangedListener, rightIconChangedListener; + public MultipleItemView(Context context) { this(context, null); } @@ -107,6 +117,13 @@ private void init() { edtText.setTextColor(rightTextColor); tvRight.setTextColor(rightTextColor); + //如果手动设置了背景 则赋值给layout 否则设置默认颜色 + Drawable background = getBackground(); + if (background != null) { + rlLayout.setBackground(background); + } else { + rlLayout.setBackgroundColor(Color.WHITE); + } } /** @@ -130,7 +147,7 @@ public MultipleItemView setShowEditText(boolean show) { * @return MultifunctionalItemView */ public MultipleItemView setShowTopLine(boolean show) { - topLine.setVisibility(showTopLine ? VISIBLE : GONE); + topLine.setVisibility(show ? VISIBLE : GONE); return this; } @@ -141,7 +158,7 @@ public MultipleItemView setShowTopLine(boolean show) { * @return MultifunctionalItemView */ public MultipleItemView setShowBottomLine(boolean show) { - bottomLine.setVisibility(showBottomLine ? VISIBLE : GONE); + bottomLine.setVisibility(show ? VISIBLE : GONE); return this; } @@ -225,8 +242,7 @@ public MultipleItemView setRightTextSize(int size) { */ public MultipleItemView setLeftIconMargin(int left, int top, int right, int bottom) { LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) ivLeft.getLayoutParams(); - layoutParams.setMargins(PxUtils.dp2px(getContext(), left), PxUtils.dp2px(getContext(), top), - PxUtils.dp2px(getContext(), right), PxUtils.dp2px(getContext(), bottom)); + layoutParams.setMargins(PxUtils.dp2px(left), PxUtils.dp2px(top), PxUtils.dp2px(right), PxUtils.dp2px(bottom)); return this; } @@ -241,8 +257,8 @@ public MultipleItemView setLeftIconMargin(int left, int top, int right, int bott */ public MultipleItemView setRightIconMargin(int left, int top, int right, int bottom) { LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) ivRight.getLayoutParams(); - layoutParams.setMargins(PxUtils.dp2px(getContext(), left), PxUtils.dp2px(getContext(), top), - PxUtils.dp2px(getContext(), right), PxUtils.dp2px(getContext(), bottom)); + layoutParams.setMargins(PxUtils.dp2px(left), PxUtils.dp2px(top), + PxUtils.dp2px(right), PxUtils.dp2px(bottom)); return this; } @@ -277,8 +293,8 @@ public MultipleItemView setLeftIconResource(int resId) { * @return MultifunctionalItemView */ public MultipleItemView setLeftIconSize(int width, int height) { - LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(PxUtils.dp2px(getContext(), width), - PxUtils.dp2px(getContext(), height)); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(PxUtils.dp2px(width), + PxUtils.dp2px(height)); lp.setMargins(0, 0, 4, 0); ivLeft.setLayoutParams(lp); return this; @@ -315,8 +331,8 @@ public MultipleItemView setRightIconResource(int resId) { * @return MultifunctionalItemView */ public MultipleItemView setRightIconSize(int width, int height) { - ivRight.setLayoutParams(new LinearLayout.LayoutParams(PxUtils.dp2px(getContext(), width), - PxUtils.dp2px(getContext(), height))); + ivRight.setLayoutParams(new LinearLayout.LayoutParams(PxUtils.dp2px(width), + PxUtils.dp2px(height))); return this; } @@ -330,8 +346,58 @@ public MultipleItemView setRightIconSize(int width, int height) { * @return MultifunctionalItemView */ public MultipleItemView setContentPadding(int left, int top, int right, int bottom) { - content.setPadding(PxUtils.dp2px(getContext(), left), PxUtils.dp2px(getContext(), top), - PxUtils.dp2px(getContext(), right), PxUtils.dp2px(getContext(), bottom)); + content.setPadding(PxUtils.dp2px(left), PxUtils.dp2px(top), + PxUtils.dp2px(right), PxUtils.dp2px(bottom)); + return this; + } + + /** + * 设置右icon选择 + * + * @param checked 选择 + * @return this + */ + public MultipleItemView setRightIconChecked(boolean checked) { + ivRight.setSelected(checked); + if (leftIconChangedListener != null) { + leftIconChangedListener.onIconChanged(checked); + } + return this; + } + + /** + * 设置左icon选择 + * + * @param checked 选择 + * @return this + */ + public MultipleItemView setLeftIconChecked(boolean checked) { + ivRight.setSelected(checked); + if (rightIconChangedListener != null) { + rightIconChangedListener.onIconChanged(checked); + } + return this; + } + + /** + * 左侧按钮变化监听 + * + * @param iconChangedListener 监听器 + * @return this + */ + public MultipleItemView setLeftIconChangedListener(OnIconCheckChangedListener iconChangedListener) { + this.leftIconChangedListener = iconChangedListener; + return this; + } + + /** + * 右侧按钮变化监听 + * + * @param iconChangedListener 监听器 + * @return this + */ + public MultipleItemView setRightIconChangedListener(OnIconCheckChangedListener iconChangedListener) { + this.rightIconChangedListener = iconChangedListener; return this; } @@ -365,4 +431,32 @@ public MultipleItemView setRightView(View view) { public String getRightTex() { return edtText.getText().toString().trim(); } + + + @OnClick({R2.id.iv_left, R2.id.iv_right}) + public void onIconClick(@NotNull View view) { + view.setSelected(!view.isSelected()); + if (view.getId() == R.id.iv_left) { + if (leftIconChangedListener != null) { + leftIconChangedListener.onIconChanged(view.isSelected()); + } + } else if (view.getId() == R.id.iv_right) { + if (rightIconChangedListener != null) { + rightIconChangedListener.onIconChanged(view.isSelected()); + } + } + } + + + /** + * 左右按钮 选择状态变化 + */ + public interface OnIconCheckChangedListener { + /** + * 按钮选择状态变化 + * + * @param checked 选择状态 + */ + void onIconChanged(boolean checked); + } } diff --git a/common_base/src/main/java/com/wss/common/widget/NoScrollGridView.java b/common_base/src/main/java/com/wss/common/widget/NoScrollGridView.java new file mode 100644 index 0000000..4e64493 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/NoScrollGridView.java @@ -0,0 +1,47 @@ +package com.wss.common.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.AdapterView; +import android.widget.GridView; + +import androidx.annotation.Nullable; + +/** + * 不可滑动的GridView + * + * @author 杨伟 + * create by 2020-4-27 + */ +public class NoScrollGridView extends GridView { + + public NoScrollGridView(Context context) { + super(context); + } + + public NoScrollGridView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public NoScrollGridView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * 设置GridView不可滑动 + * + * @param widthMeasureSpec + * @param heightMeasureSpec + */ + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, + MeasureSpec.AT_MOST); + super.onMeasure(widthMeasureSpec, expandSpec); + } + + @Override + public void setOnItemClickListener(@Nullable AdapterView.OnItemClickListener listener) { + super.setOnItemClickListener(listener); + } +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/widget/NoScrollListView.java b/common_base/src/main/java/com/wss/common/widget/NoScrollListView.java new file mode 100644 index 0000000..d695c64 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/NoScrollListView.java @@ -0,0 +1,38 @@ +package com.wss.common.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.ListView; + +/** + * 不可滑动的ListView + * @author 杨伟 + * create by 2020-4-17 + */ +public class NoScrollListView extends ListView { + + public NoScrollListView(Context context) { + super(context); + } + + public NoScrollListView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public NoScrollListView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * 设置listView不可滑动 + * + * @param widthMeasureSpec + * @param heightMeasureSpec + */ + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, + MeasureSpec.AT_MOST); + super.onMeasure(widthMeasureSpec, expandSpec); + } +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/widget/NoScrollViewPager.java b/common_base/src/main/java/com/wss/common/widget/NoScrollViewPager.java new file mode 100644 index 0000000..b1074c9 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/NoScrollViewPager.java @@ -0,0 +1,56 @@ +package com.wss.common.widget; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; + +import androidx.viewpager.widget.ViewPager; + +/** + * Describe:可禁止滚动的ViewPager + * Created by 吴天强 on 2018/11/1. + */ +public class NoScrollViewPager extends ViewPager { + private boolean noScroll = false; + + public NoScrollViewPager(Context context, AttributeSet attrs) { + super(context, attrs); + } + + + public NoScrollViewPager(Context context) { + super(context); + } + + public void setNoScroll(boolean noScroll) { + this.noScroll = noScroll; + } + + @Override + public void scrollTo(int x, int y) { + super.scrollTo(x, y); + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouchEvent(MotionEvent arg0) { + return !noScroll && super.onTouchEvent(arg0); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent arg0) { + return !noScroll && super.onInterceptTouchEvent(arg0); + } + + @Override + public void setCurrentItem(int item, boolean smoothScroll) { + super.setCurrentItem(item, smoothScroll); + } + + @Override + public void setCurrentItem(int item) { + super.setCurrentItem(item); + } + +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/widget/NumberProgressBar.java b/common_base/src/main/java/com/wss/common/widget/NumberProgressBar.java new file mode 100644 index 0000000..2b1b74a --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/NumberProgressBar.java @@ -0,0 +1,220 @@ + +package com.wss.common.widget; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Typeface; +import android.util.AttributeSet; +import android.view.View; + +import com.wss.common.base.R; +import com.wss.common.utils.PxUtils; + + +/** + * Describe:数字进度条 + * Created by 吴天强 on 2018/11/1. + */ +public class NumberProgressBar extends View { + + + /** + * 右侧未完成进度条的颜色 + */ + private static final int PAINT_START_COLOR = 0xffe5e5e5; + + /** + * context + */ + private Context context; + + /** + * l + * 主线程传过来进程 0 - 100 + */ + private int progress; + + /** + * 得到自定义视图的宽度 + */ + private int viewWidth; + + + private RectF pieOval; + + private RectF pieOvalIn; + + /** + * 得到自定义视图的Y轴中心点 + */ + private int viewCenterY; + + /** + * 已完成的画笔 + */ + private Paint paintInit = new Paint(); + + + /** + * 未完成进度条画笔的属性 + */ + private Paint paintStart = new Paint(); + + /** + * 大圆的画笔 + */ + private Paint paintEndBig = new Paint(); + + /** + * 小圆的画笔 + */ + private Paint paintSmall = new Paint(); + + + /** + * 画中间的百分比文字的画笔 + */ + private Paint paintText = new Paint(); + + /** + * 要画的文字的宽度 + */ + private int textWidth; + + /** + * 画文字时底部的坐标 + */ + private float textBottomY; + + private int smallR;//小圆的半径 + private int bigR;//大圆半径 + private float radius; + private int jR;//气泡矩形 + + /** + * 文字总共移动的长度(即从0%到100%文字左侧移动的长度) + */ +// private int totalMovedLength; + public NumberProgressBar(Context context, AttributeSet attrs) { + super(context, attrs); + this.context = context; + // 构造器中初始化数据 + smallR = PxUtils.dp2px(4);//小圆半径 + bigR = PxUtils.dp2px(8);//大圆半径 + radius = PxUtils.dp2px(10) / 2;//进度条高度 + jR = PxUtils.dp2px(6);//矩形 + + initData(); + } + + /** + * 初始化数据 + */ + private void initData() { + + // 未完成进度条画笔的属性 + paintStart.setColor(PAINT_START_COLOR); + paintStart.setStrokeWidth(PxUtils.dp2px(1)); + paintStart.setDither(true); + paintStart.setAntiAlias(true); + paintStart.setStyle(Paint.Style.FILL); + + // 已完成进度条画笔的属性 + paintInit.setColor(context.getResources().getColor(R.color.theme)); + paintInit.setStrokeWidth(PxUtils.dp2px(1)); + paintInit.setAntiAlias(true); + paintInit.setDither(true); + paintInit.setStyle(Paint.Style.FILL); + + + // 小圆画笔 + paintSmall.setColor(Color.WHITE); + paintSmall.setAntiAlias(true); + paintSmall.setStyle(Paint.Style.FILL); + + // 大圆画笔 + paintEndBig.setColor(context.getResources().getColor(R.color.theme)); + paintEndBig.setAntiAlias(true); + paintEndBig.setStyle(Paint.Style.FILL); + + + // 百分比文字画笔的属性 + int paintTextSizePx = PxUtils.sp2px(11); //设置百分比文字的尺寸 + paintText.setColor(context.getResources().getColor(R.color.theme)); + paintText.setTextSize(paintTextSizePx); + paintText.setAntiAlias(true); + paintText.setTypeface(Typeface.DEFAULT_BOLD); + + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + + @SuppressLint("DrawAllocation") + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + //得到float型进度 + float progressFloat = progress / 100.0f; + int viewHeight = getMeasuredHeight();//得到控件的高度 + + viewWidth = getMeasuredWidth() - 4 * jR; + + viewCenterY = viewHeight - bigR; + + float currentMovedLen = viewWidth * progressFloat + 2 * jR; + + String str = progress + "%"; + + Rect bounds = new Rect(); + paintText.getTextBounds(str, 0, str.length(), bounds); + textWidth = bounds.width(); + textBottomY = bounds.height(); + /* + * 1:绘画的文本 + * 2.距离x的位移 + * 3.距离Y的位移 + * 4.画笔对象 + */ + canvas.drawText(str, currentMovedLen - textWidth / 2, + viewCenterY - smallR / 2 - bigR / 2 - 2 * jR + textBottomY / 2, + paintText);//文字 + + + //圆角矩形初始的 + canvas.drawRoundRect(new RectF(2 * jR, viewCenterY - radius, currentMovedLen, + viewCenterY + radius), + radius, radius, paintInit); + + //圆角矩形--进行中 + canvas.drawRoundRect(new RectF(currentMovedLen, viewCenterY - radius, viewWidth + 2 * jR, + viewCenterY + radius), radius, radius, paintStart); + + pieOval = new RectF(currentMovedLen - bigR, viewCenterY - bigR, currentMovedLen + bigR, viewCenterY + bigR); + + pieOvalIn = new RectF(currentMovedLen - smallR, viewCenterY - smallR, currentMovedLen + smallR, viewCenterY + smallR); + + //大圆 + canvas.drawArc(pieOval, 0, 360, true, paintEndBig); + + //小圆 + canvas.drawArc(pieOvalIn, 0, 360, true, paintSmall); + } + + /** + * @param progress 外部传进来的当前进度 + */ + public void setProgress(int progress) { + this.progress = progress; + invalidate(); + } +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/widget/ObserverButton.java b/common_base/src/main/java/com/wss/common/widget/ObserverButton.java index 64b9f47..34b165a 100644 --- a/common_base/src/main/java/com/wss/common/widget/ObserverButton.java +++ b/common_base/src/main/java/com/wss/common/widget/ObserverButton.java @@ -11,20 +11,21 @@ import com.wss.common.base.R; +import org.jetbrains.annotations.NotNull; + import java.util.ArrayList; import java.util.List; +import androidx.appcompat.widget.AppCompatTextView; + /** * Describe:观察多个输入框的Button * Created by 吴天强 on 2018/10/26. */ - -public class ObserverButton extends android.support.v7.widget.AppCompatTextView { - +public class ObserverButton extends AppCompatTextView { private List editTextList = new ArrayList<>(); - private boolean canPress; private int defaultBg = Color.GRAY; private int pressBg = Color.BLUE; @@ -55,7 +56,7 @@ public ObserverButton(Context context, AttributeSet attrs, int defStyleAttr) { } - public void observer(EditText... editTexts) { + public void observer(@NotNull EditText... editTexts) { for (EditText editText : editTexts) { editText.addTextChangedListener(textWatcher); editTextList.add(editText); diff --git a/common_base/src/main/java/com/wss/common/widget/PagerSlidingTabStrip.java b/common_base/src/main/java/com/wss/common/widget/PagerSlidingTabStrip.java index 6a098b7..fcb9632 100644 --- a/common_base/src/main/java/com/wss/common/widget/PagerSlidingTabStrip.java +++ b/common_base/src/main/java/com/wss/common/widget/PagerSlidingTabStrip.java @@ -1,17 +1,13 @@ package com.wss.common.widget; -import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Typeface; -import android.os.Build; import android.os.Parcel; import android.os.Parcelable; -import android.support.v4.view.ViewPager; -import android.support.v4.view.ViewPager.OnPageChangeListener; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.TypedValue; @@ -25,23 +21,22 @@ import android.widget.TextView; import com.wss.common.base.R; +import com.wss.common.utils.PxUtils; + +import org.jetbrains.annotations.NotNull; import java.util.Locale; +import androidx.viewpager.widget.ViewPager; + /** * 水平滑动TabView * wtq 2016年6月28日14:09:46 */ public class PagerSlidingTabStrip extends HorizontalScrollView { - private static final String TAG = PagerSlidingTabStrip.class.getSimpleName(); - - public interface IconTabProvider { - int getPageIconResId(int position); - } - private final PageListener pageListener = new PageListener(); - public OnPageChangeListener delegatePageListener; + public ViewPager.OnPageChangeListener delegatePageListener; private LinearLayout tabsContainer; private ViewPager pager; @@ -54,22 +49,57 @@ public interface IconTabProvider { private Paint rectPaint; private Paint dividerPaint; - private int indicatorColor = 0XFFFF7F00;//指示器颜色 - private int underlineColor = 0XFFB9B9B9;//上下横向颜色 - private int dividerColor = 0X00FF7F00;//水平分割线颜色 - private int textDefaultColor = 0XFF999999;//标题默认颜色 - private int textSelectColor = indicatorColor;//标题选中颜色 + /** + * 指示器颜色 + */ + private int indicatorColor = 0XFFFF7F00; + /** + * 上下横向颜色 + */ + private int underlineColor = 0XFFB9B9B9; + /** + * 水平分割线颜色 + */ + private int dividerColor = 0X00FF7F00; + /** + * 标题默认颜色 + */ + private int textDefaultColor = 0XFF999999; + /** + * 标题选中颜色 + */ + private int textSelectColor = indicatorColor; - private boolean shouldExpand = true;//tab是否平均分配在屏幕上 + /** + * tab是否平均分配在屏幕上 + */ + private boolean shouldExpand = true; + /** + * 文字是否大写 + */ private boolean textAllCaps = true; - private boolean bold = false;//是否加粗 - private boolean topLine = true;//是否显示顶部横向 + /** + * 文字是否加粗 + */ + private boolean bold = false; + /** + * 是否显示顶部横向 + */ + private boolean topLine = true; + /** + * 是否显示底部横向 + */ + private boolean bottomLine = true; private int scrollOffset = 52; private int indicatorHeight = 4; private float underlineHeight = 0.5f; private int dividerPadding = 12; - private int tabPadding = 17; + /** + * Tab左右边距 + */ + private int tabPaddingLeft = 0; + private int tabPaddingRight = 0; private int dividerWidth = 1; private int redDotTop = 15; private int redDotWidth = 20; @@ -86,8 +116,7 @@ public PagerSlidingTabStrip(Context context, AttributeSet attrs) { this(context, attrs, 0); } - public PagerSlidingTabStrip(Context context, AttributeSet attrs, - int defStyle) { + public PagerSlidingTabStrip(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setFillViewport(true); @@ -95,75 +124,42 @@ public PagerSlidingTabStrip(Context context, AttributeSet attrs, tabsContainer = new LinearLayout(context); tabsContainer.setOrientation(LinearLayout.HORIZONTAL); - tabsContainer.setLayoutParams(new LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + tabsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); addView(tabsContainer); + //把一些DP SP的值做一个转换 DisplayMetrics dm = getResources().getDisplayMetrics(); - - scrollOffset = (int) TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, scrollOffset, dm); - indicatorHeight = (int) TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, indicatorHeight, dm); - underlineHeight = (int) TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, underlineHeight, dm); - dividerPadding = (int) TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, dividerPadding, dm); - tabPadding = (int) TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, tabPadding, dm); - dividerWidth = (int) TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, dividerWidth, dm); - tabTextSize = (int) TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_SP, tabTextSize, dm); - redDotTop = (int) TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, redDotTop, dm); - redDotWidth = (int) TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, redDotWidth, dm); + scrollOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, scrollOffset, dm); + indicatorHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, indicatorHeight, dm); + underlineHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, underlineHeight, dm); + dividerPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerPadding, dm); + dividerWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerWidth, dm); + tabTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, tabTextSize, dm); + redDotTop = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, redDotTop, dm); + redDotWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, redDotWidth, dm); + tabPaddingLeft = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, tabPaddingLeft, dm); + tabPaddingRight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, tabPaddingRight, dm); + + //获取自定义属性值 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PagerSlidingTabStrip); - - indicatorColor = a.getColor( - R.styleable.PagerSlidingTabStrip_pstsIndicatorColor, - indicatorColor); - textSelectColor = a.getColor( - R.styleable.PagerSlidingTabStrip_pstsTextSelectedColor, - textSelectColor); - textDefaultColor = a.getColor( - R.styleable.PagerSlidingTabStrip_pstsTextDefaultColor, - textDefaultColor); - underlineColor = a.getColor( - R.styleable.PagerSlidingTabStrip_pstsUnderlineColor, - underlineColor); - tabTextSize = a.getDimensionPixelSize( - R.styleable.PagerSlidingTabStrip_pstsTextSize, tabTextSize); - - dividerColor = a - .getColor(R.styleable.PagerSlidingTabStrip_pstsDividerColor, - dividerColor); - indicatorHeight = a.getDimensionPixelSize( - R.styleable.PagerSlidingTabStrip_pstsIndicatorHeight, - indicatorHeight); - bold = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsBold, - bold); - topLine = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsHasTopLine, - topLine); - dividerPadding = a.getDimensionPixelSize( - R.styleable.PagerSlidingTabStrip_pstsDividerPadding, - dividerPadding); - tabPadding = a.getDimensionPixelSize( - R.styleable.PagerSlidingTabStrip_pstsTabPaddingLeftRight, - tabPadding); - tabPadding = a.getDimensionPixelSize( - R.styleable.PagerSlidingTabStrip_pstsTextSize, tabPadding); - - shouldExpand = a - .getBoolean(R.styleable.PagerSlidingTabStrip_pstsShouldExpand, - shouldExpand); - scrollOffset = a - .getDimensionPixelSize( - R.styleable.PagerSlidingTabStrip_pstsScrollOffset, - scrollOffset); - textAllCaps = a.getBoolean( - R.styleable.PagerSlidingTabStrip_pstsTextAllCaps, textAllCaps); + indicatorColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsIndicatorColor, indicatorColor); + textSelectColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsTextSelectedColor, textSelectColor); + textDefaultColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsTextDefaultColor, textDefaultColor); + underlineColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsUnderlineColor, underlineColor); + tabTextSize = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsTextSize, tabTextSize); + + dividerColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsDividerColor, dividerColor); + indicatorHeight = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsIndicatorHeight, indicatorHeight); + bold = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsBold, bold); + topLine = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsHasTopLine, topLine); + bottomLine = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsHasBottomLine, bottomLine); + dividerPadding = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsDividerPadding, dividerPadding); + tabPaddingLeft = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsTabPaddingLeft, tabPaddingLeft); + tabPaddingRight = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsTabPaddingRight, tabPaddingRight); + + shouldExpand = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsShouldExpand, shouldExpand); + scrollOffset = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsScrollOffset, scrollOffset); + textAllCaps = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsTextAllCaps, textAllCaps); a.recycle(); @@ -180,60 +176,29 @@ public PagerSlidingTabStrip(Context context, AttributeSet attrs, } } - - public void setViewPager(ViewPager pager) { - this.pager = pager; - + /** + * 更新页面 + */ + private void notifyDataSetChanged() { if (pager.getAdapter() == null) { - throw new IllegalStateException( - "ViewPager does not have adapter instance."); + throw new IllegalStateException("ViewPager does not have adapter instance."); } - - pager.addOnPageChangeListener(pageListener); - notifyDataSetChanged(); - } - - public void setOnPageChangeListener(OnPageChangeListener listener) { - this.delegatePageListener = listener; - } - - public void notifyDataSetChanged() { - tabsContainer.removeAllViews(); - tabCount = pager.getAdapter().getCount(); - for (int i = 0; i < tabCount; i++) { - if (pager.getAdapter() instanceof IconTabProvider) { - addIconTab(i, - ((IconTabProvider) pager.getAdapter()) - .getPageIconResId(i)); + addIconTab(i, ((IconTabProvider) pager.getAdapter()).getPageIconResId(i)); } else { - String title = pager.getAdapter().getPageTitle(i).toString(); + String title = String.valueOf(pager.getAdapter().getPageTitle(i)); addTextTab(i, title); } - } - updateTabStyles(); - getViewTreeObserver().addOnGlobalLayoutListener( new OnGlobalLayoutListener() { - - @SuppressWarnings("deprecation") - @SuppressLint("NewApi") @Override public void onGlobalLayout() { - - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { - getViewTreeObserver().removeGlobalOnLayoutListener( - this); - } else { - getViewTreeObserver().removeOnGlobalLayoutListener( - this); - } - + getViewTreeObserver().removeOnGlobalLayoutListener(this); currentPosition = pager.getCurrentItem(); scrollToChild(currentPosition, 0); } @@ -241,79 +206,76 @@ public void onGlobalLayout() { } + /** + * 添加文本Tab + * + * @param position 位置 + * @param title tab显示文字 + */ private void addTextTab(final int position, String title) { - - TextView tab = new TextView(getContext()); - tab.setText(title); - tab.setGravity(Gravity.CENTER); - tab.setSingleLine(); + TextView tvTab = new TextView(getContext()); + tvTab.setText(title); + tvTab.setGravity(Gravity.CENTER); + tvTab.setSingleLine(); if (position != 0) { - tab.setTextColor(textDefaultColor); - tab.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL)); + tvTab.setTextColor(textDefaultColor); + tvTab.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL)); } else { if (bold) { - tab.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); + tvTab.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); } - tab.setTextColor(textSelectColor); + tvTab.setTextColor(textSelectColor); } - - addTab(position, tab); + addTab(position, tvTab); } + /** + * 添加IconTab + * + * @param position position + * @param resId icon资源 + */ private void addIconTab(final int position, int resId) { - ImageButton tab = new ImageButton(getContext()); tab.setImageResource(resId); - addTab(position, tab); } - private void addTab(final int position, View tab) { - tab.setFocusable(true); - tab.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - if (onPagerTabClick != null) { - onPagerTabClick.onClick(position); - } - pager.setCurrentItem(position); + /** + * 添加Tab + * + * @param position 位置 + * @param tabView tab + */ + private void addTab(int position, @NotNull View tabView) { + tabView.setFocusable(true); + tabView.setOnClickListener(v -> { + if (onPagerTabClick != null) { + onPagerTabClick.onClick(position); } + pager.setCurrentItem(position); }); - tab.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.MATCH_PARENT)); - + tabView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)); LinearLayout linearLayout = new LinearLayout(getContext()); if (shouldExpand) { - linearLayout.setLayoutParams(new LinearLayout.LayoutParams(0, - LayoutParams.MATCH_PARENT, 1.0f)); + linearLayout.setLayoutParams(new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f)); } else { - linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, - LayoutParams.MATCH_PARENT)); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); + lp.setMargins(0, 0, PxUtils.dp2px(20), 0); + linearLayout.setLayoutParams(lp); } - - linearLayout.setLayoutParams(new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f)); - linearLayout.setPadding(tabPadding, 0, tabPadding, 0); + linearLayout.setPadding(tabPaddingLeft, 0, tabPaddingRight, 0); linearLayout.setGravity(Gravity.CENTER); -// ImageView view = new ImageView(getContext()); -// view.setLayoutParams(new LayoutParams(redDotWidth, redDotWidth)); -// view.setPadding(0, redDotTop, 0, 0); -// view.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.red)); -// view.setVisibility(View.GONE); - linearLayout.addView(tab); -// linearLayout.addView(view); + linearLayout.addView(tabView); tabsContainer.addView(linearLayout); } private void updateTabStyles() { - for (int i = 0; i < tabCount; i++) { - View v = ((ViewGroup) tabsContainer.getChildAt(i)).getChildAt(0); - if (v instanceof TextView) { - TextView tab = (TextView) v; if (i != 0) { tab.setTextColor(textDefaultColor); @@ -325,7 +287,6 @@ private void updateTabStyles() { tab.setTextColor(textSelectColor); } tab.setTextSize(TypedValue.COMPLEX_UNIT_PX, tabTextSize); - tab.setAllCaps(textAllCaps); } } @@ -397,9 +358,10 @@ protected void onDraw(Canvas canvas) { canvas.drawRect(0, 0, tabsContainer.getWidth(), underlineHeight, rectPaint); } - canvas.drawRect(0, height - underlineHeight, tabsContainer.getWidth(), - height, rectPaint); - + if (bottomLine) { + canvas.drawRect(0, height - underlineHeight, tabsContainer.getWidth(), + height, rectPaint); + } // draw divider dividerPaint.setColor(dividerColor); @@ -410,18 +372,17 @@ protected void onDraw(Canvas canvas) { } } - private class PageListener implements OnPageChangeListener { + private class PageListener implements ViewPager.OnPageChangeListener { @Override - public void onPageScrolled(int position, float positionOffset, - int positionOffsetPixels) { + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { currentPosition = position; currentPositionOffset = positionOffset; - if (tabsContainer.getChildAt(position) != null) - scrollToChild(position, (int) (positionOffset * tabsContainer - .getChildAt(position).getWidth())); + if (tabsContainer.getChildAt(position) != null) { + scrollToChild(position, (int) (positionOffset * tabsContainer.getChildAt(position).getWidth())); + } invalidate(); @@ -503,7 +464,7 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(currentPosition); } - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public static final Creator CREATOR = new Creator() { @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); @@ -543,6 +504,25 @@ public void showPosition(int position) { viewGroup.getChildAt(1).setVisibility(View.VISIBLE); } } + /** + * 添加ViewPager + * + * @param pager ViewPager + */ + public void setViewPager(@NotNull ViewPager pager) { + this.pager = pager; + pager.addOnPageChangeListener(pageListener); + notifyDataSetChanged(); + } + + /** + * 设置ViewPager滑动监听 + * + * @param listener 监听器 + */ + public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { + this.delegatePageListener = listener; + } /** * 下方滑动指示器颜色 @@ -683,9 +663,13 @@ public void setTextSelectColorResource(int resId) { /** * 设置item左右padding + * + * @param leftPadding 左边距 + * @param rightPadding 右边距 */ - public void setTabPaddingLeftRight(int paddingPx) { - this.tabPadding = paddingPx; + public void setTabPadding(int leftPadding, int rightPadding) { + this.tabPaddingLeft = leftPadding; + this.tabPaddingRight = rightPadding; updateTabStyles(); } @@ -705,5 +689,17 @@ public void setShowTopLine(boolean show) { invalidate(); } + /** + * IconTab 提供则 + */ + public interface IconTabProvider { + /** + * 返回Icon资源ID + * + * @param position position + * @return icon资源图片 + */ + int getPageIconResId(int position); + } } diff --git a/common_base/src/main/java/com/wss/common/widget/ProgressWebView.java b/common_base/src/main/java/com/wss/common/widget/ProgressWebView.java deleted file mode 100644 index f2bf3f6..0000000 --- a/common_base/src/main/java/com/wss/common/widget/ProgressWebView.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.wss.common.widget; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.os.Build; -import android.os.Handler; -import android.support.annotation.RequiresApi; -import android.text.TextUtils; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; -import android.webkit.WebChromeClient; -import android.webkit.WebSettings; -import android.webkit.WebView; - -import com.wss.common.widget.dialog.LoadingDialog; - - -/** - * 带进度条的WebView - * wtq 2016年6月28日14:09:46 - */ -public class ProgressWebView extends WebView { - private WebViewProgressBar progressBar; - private Handler handler; - private WebView _this; - private LoadingDialog loadingDialog; - - @SuppressLint("SetJavaScriptEnabled") - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - public ProgressWebView(Context context, AttributeSet attrs) { - super(context, attrs); - progressBar = new WebViewProgressBar(context); - progressBar.setLayoutParams(new ViewGroup.LayoutParams - (ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - progressBar.setVisibility(GONE); - addView(progressBar); - handler = new Handler(); - _this = this; - loadingDialog = new LoadingDialog(context); - loadingDialog.setCancelable(true); - - setWebChromeClient(new MyWebChromeClient()); -// setWebViewClient(new MyWebClient()); - - /** - * Webview在安卓5.0之前默认允许其加载混合网络协议内容 - * 在安卓5.0之后,默认不允许加载http与https混合内容,需要设置webview允许其加载混合网络协议内容 - */ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); - - } - - /** 设置webView不显示图片问题 */ - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { - getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); - } - getSettings().setJavaScriptEnabled(true); - getSettings().setBlockNetworkImage(false); - - } - - public void showDialog(String text) { - if (!TextUtils.isEmpty(text)) { - loadingDialog.setTitleText("正在加载···"); - } - loadingDialog.show(); - } - - private class MyWebChromeClient extends WebChromeClient { - @Override - public void onProgressChanged(WebView view, int newProgress) { - if (newProgress == 100) { - progressBar.setProgress(100); - handler.postDelayed(runnable, 200); - } else if (progressBar.getVisibility() == GONE) { - progressBar.setVisibility(VISIBLE); - } - if (newProgress < 5) { - newProgress = 5; - } - progressBar.setProgress(newProgress); - super.onProgressChanged(view, newProgress); - } - } - - - - - private Runnable runnable = new Runnable() { - @Override - public void run() { - dismissDialog(); - } - }; - - /** - * 关闭加载框 - */ - public void dismissDialog() { - if (loadingDialog.isShowing()) { - loadingDialog.dismiss(); - } - progressBar.setVisibility(View.GONE); - } - -} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/widget/SlideBarView.java b/common_base/src/main/java/com/wss/common/widget/SlideBarView.java new file mode 100644 index 0000000..93c3385 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/SlideBarView.java @@ -0,0 +1,128 @@ +package com.wss.common.widget; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Typeface; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +import com.wss.common.utils.PxUtils; + +/** + * Describe:选择城市右边的字母栏,可以滚动选择开头字母 + * Created by 吴天强 on 2018/11/1. + */ +public class SlideBarView extends View { + + + public static final char[] SPECIAL_CHAR_INDEX = {'#', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', + 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; + + private char[] indexs = SPECIAL_CHAR_INDEX; + + Paint mPaint = null; + + public interface OnSlideBarBaseViewFlipListener { + + void onFlip(int index, String mChar); + + void onFlipUp(); + } + + private OnSlideBarBaseViewFlipListener flipListener = null; + + /** + * Set SlideBar OnFlip Listener + */ + public void setFlipListener(OnSlideBarBaseViewFlipListener mListener) { + this.flipListener = mListener; + } + + public SlideBarView(Context context) { + super(context); + init(); + } + + public SlideBarView(Context context, AttributeSet attrs, + int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + public SlideBarView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + private void init() { + mPaint = new Paint(); + mPaint.setTextSize(PxUtils.dp2px(14)); + mPaint.setTextAlign(Paint.Align.CENTER); + mPaint.setColor(Color.parseColor("#FF333333")); + mPaint.setTypeface(Typeface.DEFAULT_BOLD); + mPaint.setAntiAlias(true); + } + + /** + * 設置索引 + */ + public void setIndexs(char[] wantedIndexs) { + this.indexs = wantedIndexs; + invalidate(); + } + + public void setTextColor(int color) { + mPaint.setColor(color); + } + + @Override + protected void onDraw(Canvas canvas) { + //均分 + int itemHeight = getHeight() / SPECIAL_CHAR_INDEX.length; + //计算从哪一个开始 + float startHeight = 0.0f; + if (indexs.length <= SPECIAL_CHAR_INDEX.length) { + startHeight = ((SPECIAL_CHAR_INDEX.length - indexs.length) >> 1) * itemHeight; + } + for (int i = 0; i < indexs.length; i++) { + float xPos = getWidth() >> 1; + float yPos; + if (startHeight == 0) { + //数目超过既定最大数 使用另一种算法 + yPos = (getHeight() >> indexs.length) * i + getHeight() >> indexs.length; + } else { + yPos = startHeight + itemHeight * i; + } + canvas.drawText(String.valueOf(indexs[i]), xPos, yPos, mPaint); + } + super.onDraw(canvas); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + float touchY = event.getY(); + float everyHeight = ((float) getHeight() / indexs.length); + int touchId = (int) (touchY / everyHeight); + if (touchId < 0) { + touchId = 0; + } + if (touchId > indexs.length - 1) { + touchId = indexs.length - 1; + } + if (event.getAction() == MotionEvent.ACTION_DOWN + || event.getAction() == MotionEvent.ACTION_MOVE) { + if (this.flipListener != null) { + this.flipListener.onFlip(touchId, String.valueOf(indexs[touchId])); + } + } else { + if (this.flipListener != null) { + this.flipListener.onFlipUp(); + } + } + return true; + } +} diff --git a/common_base/src/main/java/com/wss/common/widget/SlideLayout.java b/common_base/src/main/java/com/wss/common/widget/SlideLayout.java new file mode 100644 index 0000000..7f8fefe --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/SlideLayout.java @@ -0,0 +1,596 @@ +package com.wss.common.widget; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.TypedArray; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; + +import com.wss.common.base.R; + +import androidx.core.view.MotionEventCompat; +import androidx.core.view.ViewCompat; + + +/** + * Describe:分页滑动Layout 如:商品详情页 滑动查看图文详情 + * Created by 吴天强 on 2018/11/1. + */ +public class SlideLayout extends ViewGroup { + + /** + * Callback for panel OPEN-CLOSE status changed. + */ + public interface OnSlideDetailsListener { + /** + * Called after status changed. + * + * @param status {@link Status} + */ + void onStateChanged(Status status); + } + + public enum Status { + /** + * Panel is closed + */ + CLOSE, + /** + * Panel is opened + */ + OPEN; + + public static Status valueOf(int stats) { + if (0 == stats) { + return CLOSE; + } else if (1 == stats) { + return OPEN; + } else { + return CLOSE; + } + } + } + + private static final float DEFAULT_PERCENT = 0.2f; + private static final int DEFAULT_DURATION = 300; + + private View mFrontView; + private View mBehindView; + + private float mTouchSlop; + private float mInitMotionY; + private float mInitMotionX; + + + private View mTarget; + private float mSlideOffset; + private Status mStatus = Status.CLOSE; + private boolean isFirstShowBehindView = true; + private float mPercent = DEFAULT_PERCENT; + private long mDuration = DEFAULT_DURATION; + private int mDefaultPanel = 0; + + private OnSlideDetailsListener mOnSlideDetailsListener; + + public SlideLayout(Context context) { + this(context, null); + } + + public SlideLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SlideLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SlideLayout, defStyleAttr, 0); + mPercent = a.getFloat(R.styleable.SlideLayout_percent, DEFAULT_PERCENT); + mDuration = a.getInt(R.styleable.SlideLayout_duration, DEFAULT_DURATION); + mDefaultPanel = a.getInt(R.styleable.SlideLayout_default_panel, 0); + a.recycle(); + + mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); + } + + /** + * Set the callback of panel OPEN-CLOSE status. + * + * @param listener {@link OnSlideDetailsListener} + */ + public void setOnSlideDetailsListener(OnSlideDetailsListener listener) { + this.mOnSlideDetailsListener = listener; + } + + /** + * Open pannel smoothly. + * + * @param smooth true, smoothly. false otherwise. + */ + public void smoothOpen(boolean smooth) { + if (mStatus != Status.OPEN) { + mStatus = Status.OPEN; + final float height = -getMeasuredHeight(); + animatorSwitch(0, height, true, smooth ? mDuration : 0); + } + } + + /** + * Close pannel smoothly. + * + * @param smooth true, smoothly. false otherwise. + */ + public void smoothClose(boolean smooth) { + if (mStatus != Status.CLOSE) { + mStatus = Status.CLOSE; + final float height = -getMeasuredHeight(); + animatorSwitch(height, 0, true, smooth ? mDuration : 0); + } + } + + /** + * Set the float value for indicate the moment of switch panel + * + * @param percent (0.0, 1.0) + */ + public void setPercent(float percent) { + this.mPercent = percent; + } + + @Override + protected LayoutParams generateDefaultLayoutParams() { + return new MarginLayoutParams(MarginLayoutParams.WRAP_CONTENT, MarginLayoutParams.WRAP_CONTENT); + } + + @Override + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return new MarginLayoutParams(getContext(), attrs); + } + + @Override + protected LayoutParams generateLayoutParams(LayoutParams p) { + return new MarginLayoutParams(p); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + final int childCount = getChildCount(); + if (1 >= childCount) { + throw new RuntimeException("SlideDetailsLayout only accept childs more than 1!!"); + } + + mFrontView = getChildAt(0); + mBehindView = getChildAt(1); + + // set behindview's visibility to GONE before show. + //mBehindView.setVisibility(GONE); + if (mDefaultPanel == 1) { + post(new Runnable() { + @Override + public void run() { + smoothOpen(false); + } + }); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int pWidth = MeasureSpec.getSize(widthMeasureSpec); + final int pHeight = MeasureSpec.getSize(heightMeasureSpec); + + final int childWidthMeasureSpec = + MeasureSpec.makeMeasureSpec(pWidth, MeasureSpec.EXACTLY); + final int childHeightMeasureSpec = + MeasureSpec.makeMeasureSpec(pHeight, MeasureSpec.EXACTLY); + + View child; + for (int i = 0; i < getChildCount(); i++) { + child = getChildAt(i); + // skip measure if gone + if (child.getVisibility() == GONE) { + continue; + } + + measureChild(child, childWidthMeasureSpec, childHeightMeasureSpec); + } + + setMeasuredDimension(pWidth, pHeight); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + final int left = l; + final int right = r; + int top; + int bottom; + + final int offset = (int) mSlideOffset; + + View child; + for (int i = 0; i < getChildCount(); i++) { + child = getChildAt(i); + + // skip layout + if (child.getVisibility() == GONE) { + continue; + } + + if (child == mBehindView) { + top = b + offset; + bottom = top + b - t; + } else { + top = t + offset; + bottom = b + offset; + } + + child.layout(left, top, right, bottom); + } + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + ensureTarget(); + if (null == mTarget) { + return false; + } + + if (!isEnabled()) { + return false; + } + + final int aciton = MotionEventCompat.getActionMasked(ev); + + boolean shouldIntercept = false; + switch (aciton) { + case MotionEvent.ACTION_DOWN: { + mInitMotionX = ev.getX(); + mInitMotionY = ev.getY(); + shouldIntercept = false; + break; + } + case MotionEvent.ACTION_MOVE: { + final float x = ev.getX(); + final float y = ev.getY(); + + final float xDiff = x - mInitMotionX; + final float yDiff = y - mInitMotionY; + + if (canChildScrollVertically((int) yDiff)) { + shouldIntercept = false; + } else { + final float xDiffabs = Math.abs(xDiff); + final float yDiffabs = Math.abs(yDiff); + + // intercept rules: + // 1. The vertical displacement is larger than the horizontal displacement; + // 2. Panel stauts is CLOSE:slide up + // 3. Panel status is OPEN:slide down + if (yDiffabs > mTouchSlop && yDiffabs >= xDiffabs + && !(mStatus == Status.CLOSE && yDiff > 0 + || mStatus == Status.OPEN && yDiff < 0)) { + shouldIntercept = true; + } + } + break; + } + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: { + shouldIntercept = false; + break; + } + + } + + return shouldIntercept; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + ensureTarget(); + if (null == mTarget) { + return false; + } + + if (!isEnabled()) { + return false; + } + + boolean wantTouch = true; + final int action = MotionEventCompat.getActionMasked(ev); + + switch (action) { + case MotionEvent.ACTION_DOWN: { + // if target is a view, we want the DOWN action. + if (mTarget instanceof View) { + wantTouch = true; + } + break; + } + + case MotionEvent.ACTION_MOVE: { + final float y = ev.getY(); + final float yDiff = y - mInitMotionY; + if (canChildScrollVertically(((int) yDiff))) { + wantTouch = false; + } else { + processTouchEvent(yDiff); + wantTouch = true; + } + break; + } + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: { + finishTouchEvent(); + wantTouch = false; + break; + } + } + return wantTouch; + } + + /** + * @param offset Displacement in vertically. + */ + private void processTouchEvent(final float offset) { + if (Math.abs(offset) < mTouchSlop) { + return; + } + + final float oldOffset = mSlideOffset; + // pull up to open + if (mStatus == Status.CLOSE) { + // reset if pull down + if (offset >= 0) { + mSlideOffset = 0; + } else { + mSlideOffset = offset; + } + + if (mSlideOffset == oldOffset) { + return; + } + + // pull down to close + } else if (mStatus == Status.OPEN) { + final float pHeight = -getMeasuredHeight(); + // reset if pull up + if (offset <= 0) { + mSlideOffset = pHeight; + } else { + final float newOffset = pHeight + offset; + mSlideOffset = newOffset; + } + + if (mSlideOffset == oldOffset) { + return; + } + } + // relayout + requestLayout(); + } + + /** + * Called after gesture is ending. + */ + private void finishTouchEvent() { + final int pHeight = getMeasuredHeight(); + final int percent = (int) (pHeight * mPercent); + final float offset = mSlideOffset; + + boolean changed = false; + + if (Status.CLOSE == mStatus) { + if (offset <= -percent) { + mSlideOffset = -pHeight; + mStatus = Status.OPEN; + changed = true; + } else { + // keep panel closed + mSlideOffset = 0; + } + } else if (Status.OPEN == mStatus) { + if ((offset + pHeight) >= percent) { + mSlideOffset = 0; + mStatus = Status.CLOSE; + changed = true; + } else { + // keep panel opened + mSlideOffset = -pHeight; + } + } + + animatorSwitch(offset, mSlideOffset, changed); + } + + private void animatorSwitch(final float start, final float end) { + animatorSwitch(start, end, true, mDuration); + } + + private void animatorSwitch(final float start, final float end, final long duration) { + animatorSwitch(start, end, true, duration); + } + + private void animatorSwitch(final float start, final float end, final boolean changed) { + animatorSwitch(start, end, changed, mDuration); + } + + private void animatorSwitch(final float start, + final float end, + final boolean changed, + final long duration) { + ValueAnimator animator = ValueAnimator.ofFloat(start, end); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mSlideOffset = (float) animation.getAnimatedValue(); + requestLayout(); + } + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + if (changed) { + if (mStatus == Status.OPEN) { + checkAndFirstOpenPanel(); + } + + if (null != mOnSlideDetailsListener) { + mOnSlideDetailsListener.onStateChanged(mStatus); + } + } + } + }); + animator.setDuration(duration); + animator.start(); + } + + /** + * Whether the closed pannel is opened at first time. + * If open first, we should set the behind view's visibility as VISIBLE. + */ + private void checkAndFirstOpenPanel() { + if (isFirstShowBehindView) { + isFirstShowBehindView = false; + mBehindView.setVisibility(VISIBLE); + } + } + + /** + * When pulling, target view changed by the panel status. If panel opened, the target is behind view. + * Front view is for otherwise. + */ + private void ensureTarget() { + if (mStatus == Status.CLOSE) { + mTarget = mFrontView; + } else { + mTarget = mBehindView; + } + } + + /** + * Check child view can srcollable in vertical direction. + * + * @param direction Negative to check scrolling up, positive to check scrolling down. + * @return true if this view can be scrolled in the specified direction, false otherwise. + */ + protected boolean canChildScrollVertically(int direction) { + if (mTarget instanceof AbsListView) { + return canListViewSroll((AbsListView) mTarget); + } else if (mTarget instanceof FrameLayout || + mTarget instanceof RelativeLayout || + mTarget instanceof LinearLayout) { + View child; + for (int i = 0; i < ((ViewGroup) mTarget).getChildCount(); i++) { + child = ((ViewGroup) mTarget).getChildAt(i); + if (child instanceof AbsListView) { + return canListViewSroll((AbsListView) child); + } + } + } + + if (android.os.Build.VERSION.SDK_INT < 14) { + return ViewCompat.canScrollVertically(mTarget, -direction) || mTarget.getScrollY() > 0; + } else { + return ViewCompat.canScrollVertically(mTarget, -direction); + } + } + + protected boolean canListViewSroll(AbsListView absListView) { + if (mStatus == Status.OPEN) { + return absListView.getChildCount() > 0 + && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0) + .getTop() < + absListView.getPaddingTop()); + } else { + final int count = absListView.getChildCount(); + return count > 0 + && (absListView.getLastVisiblePosition() < count - 1 + || absListView.getChildAt(count - 1) + .getBottom() > absListView.getMeasuredHeight()); + } + } + + @Override + protected Parcelable onSaveInstanceState() { + SavedState ss = new SavedState(super.onSaveInstanceState()); + ss.offset = mSlideOffset; + ss.status = mStatus.ordinal(); + return ss; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + SavedState ss = (SavedState) state; + super.onRestoreInstanceState(ss.getSuperState()); + mSlideOffset = ss.offset; + mStatus = Status.valueOf(ss.status); + + if (mStatus == Status.OPEN) { + mBehindView.setVisibility(VISIBLE); + } + + requestLayout(); + } + + static class SavedState extends BaseSavedState { + + private float offset; + private int status; + + /** + * Constructor used when reading from a parcel. Reads the state of the superclass. + * + * @param source + */ + public SavedState(Parcel source) { + super(source); + offset = source.readFloat(); + status = source.readInt(); + } + + /** + * Constructor called by derived classes when creating their SavedState objects + * + * @param superState The state of the superclass of this view + */ + public SavedState(Parcelable superState) { + super(superState); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + out.writeFloat(offset); + out.writeInt(status); + } + + public static final Creator CREATOR = + new Creator() { + + @Override + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } +} diff --git a/common_base/src/main/java/com/wss/common/widget/StickyNavLayout.java b/common_base/src/main/java/com/wss/common/widget/StickyNavLayout.java new file mode 100644 index 0000000..257c526 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/StickyNavLayout.java @@ -0,0 +1,394 @@ +package com.wss.common.widget; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; + +import java.util.ArrayList; +import java.util.List; + +import androidx.core.widget.NestedScrollView; + +/** + * Describe:滑动粘性ScrollView,设置View的tag = sticky 滑动可吸顶 + * Created by 吴天强 on 2020/05/13. + * Copy from: https://github.com/LidongWen/MultiTypeAdapter + */ +public class StickyNavLayout extends NestedScrollView { + + public interface OnViewStickyListener { + + void onSticky(View view); + + void onUnSticky(View view); + } + + /** + * Tag for views that should stick and have constant drawing. e.g. TextViews, ImageViews etc + */ + public static final String STICKY_TAG = "sticky"; + /** + * Flag for views that should stick and have non-constant drawing. e.g. Buttons, ProgressBars etc + */ + public static final String FLAG_NONCONSTANT = "-nonconstant"; + /** + * Flag for views that have aren't fully opaque + */ + public static final String FLAG_HASTRANSPARENCY = "-hastransparency"; + /** + * Default height of the shadow peeking out below the stuck view. + */ + private static final int DEFAULT_SHADOW_HEIGHT = 10; // dp; + private ArrayList stickyViews; + private View currentlyStickingView; + private float stickyViewTopOffset; + private final Runnable invalidateRunnable = new Runnable() { + @Override + public void run() { + if (currentlyStickingView != null) { + int l = getLeftForViewRelativeOnlyChild(currentlyStickingView); + int t = getBottomForViewRelativeOnlyChild(currentlyStickingView); + int r = getRightForViewRelativeOnlyChild(currentlyStickingView); + int b = (int) (getScrollY() + (currentlyStickingView.getHeight() + stickyViewTopOffset)); + invalidate(l, t, r, b); + } + postDelayed(this, 16); + } + }; + private int stickyViewLeftOffset; + private boolean redirectTouchesToStickyView; + private boolean clippingToPadding; + private boolean clipToPaddingHasBeenSet; + private int mShadowHeight = DEFAULT_SHADOW_HEIGHT; + private Drawable mShadowDrawable; + private boolean hasNotDoneActionDown = true; + + private List mOnViewStickyListeners; + + public StickyNavLayout(Context context) { + this(context, null); + } + + public StickyNavLayout(Context context, AttributeSet attrs) { + this(context, attrs, android.R.attr.scrollViewStyle); + } + + public StickyNavLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setup(); + } + + public void addOnViewStickyListener(OnViewStickyListener stickyListener) { + if (mOnViewStickyListeners == null) + mOnViewStickyListeners = new ArrayList<>(); + mOnViewStickyListeners.add(stickyListener); + } + + public void removeOnViewStickyListener(OnViewStickyListener stickyListener) { + if (mOnViewStickyListeners != null) + mOnViewStickyListeners.remove(stickyListener); + } + + public void clearOnViewStickyListener() { + if (mOnViewStickyListeners != null) + mOnViewStickyListeners.clear(); + } + + public void setShadowHeight(int height) { + mShadowHeight = height; + } + + public void setShadowDrawable(Drawable shadowDrawable) { + mShadowDrawable = shadowDrawable; + } + + public void setup() { + stickyViews = new ArrayList<>(); + } + + private int getLeftForViewRelativeOnlyChild(View v) { + int left = v.getLeft(); + while (v.getParent() != null && v.getParent() != getChildAt(0)) { + v = (View) v.getParent(); + left += v.getLeft(); + } + return left; + } + + private int getTopForViewRelativeOnlyChild(View v) { + int top = v.getTop(); + while (v.getParent() != null && v.getParent() != getChildAt(0)) { + v = (View) v.getParent(); + top += v.getTop(); + } + return top; + } + + private int getRightForViewRelativeOnlyChild(View v) { + int right = v.getRight(); + while (v.getParent() != null && v.getParent() != getChildAt(0)) { + v = (View) v.getParent(); + right += v.getRight(); + } + return right; + } + + private int getBottomForViewRelativeOnlyChild(View v) { + int bottom = v.getBottom(); + while (v.getParent() != null && v.getParent() != getChildAt(0)) { + v = (View) v.getParent(); + bottom += v.getBottom(); + } + return bottom; + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + if (!clipToPaddingHasBeenSet) { + clippingToPadding = true; + } + notifyHierarchyChanged(); + } + + @Override + public void setClipToPadding(boolean clipToPadding) { + super.setClipToPadding(clipToPadding); + clippingToPadding = clipToPadding; + clipToPaddingHasBeenSet = true; + } + + @Override + public void addView(View child) { + super.addView(child); + findStickyViews(child); + } + + @Override + public void addView(View child, int index) { + super.addView(child, index); + findStickyViews(child); + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + super.addView(child, index, params); + findStickyViews(child); + } + + @Override + public void addView(View child, int width, int height) { + super.addView(child, width, height); + findStickyViews(child); + } + + @Override + public void addView(View child, ViewGroup.LayoutParams params) { + super.addView(child, params); + findStickyViews(child); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + if (currentlyStickingView != null) { + canvas.save(); + canvas.translate(getPaddingLeft() + stickyViewLeftOffset, getScrollY() + + stickyViewTopOffset + (clippingToPadding ? getPaddingTop() : 0)); + canvas.clipRect(0, (clippingToPadding ? -stickyViewTopOffset : 0), + getWidth() - stickyViewLeftOffset, + currentlyStickingView.getHeight() + mShadowHeight + 1); + if (mShadowDrawable != null) { + int left = 0; + int top = currentlyStickingView.getHeight(); + int right = currentlyStickingView.getWidth(); + int bottom = currentlyStickingView.getHeight() + mShadowHeight; + mShadowDrawable.setBounds(left, top, right, bottom); + mShadowDrawable.draw(canvas); + } + canvas.clipRect(0, (clippingToPadding ? -stickyViewTopOffset : 0), getWidth(), + currentlyStickingView.getHeight()); + if (getStringTagForView(currentlyStickingView).contains(FLAG_HASTRANSPARENCY)) { + showView(currentlyStickingView); + currentlyStickingView.draw(canvas); + hideView(currentlyStickingView); + } else { + currentlyStickingView.draw(canvas); + } + canvas.restore(); + } + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + redirectTouchesToStickyView = true; + } + if (redirectTouchesToStickyView) { + redirectTouchesToStickyView = currentlyStickingView != null; + if (redirectTouchesToStickyView) { + redirectTouchesToStickyView = + ev.getY() <= (currentlyStickingView.getHeight() + stickyViewTopOffset) && + ev.getX() >= getLeftForViewRelativeOnlyChild(currentlyStickingView) && + ev.getX() <= getRightForViewRelativeOnlyChild(currentlyStickingView); + } + } else if (currentlyStickingView == null) { + redirectTouchesToStickyView = false; + } + if (redirectTouchesToStickyView) { + ev.offsetLocation(0, -1 * ((getScrollY() + stickyViewTopOffset) + - getTopForViewRelativeOnlyChild(currentlyStickingView))); + } + return super.dispatchTouchEvent(ev); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (redirectTouchesToStickyView) { + ev.offsetLocation(0, ((getScrollY() + stickyViewTopOffset) + - getTopForViewRelativeOnlyChild(currentlyStickingView))); + } + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + hasNotDoneActionDown = false; + } + if (hasNotDoneActionDown) { + MotionEvent down = MotionEvent.obtain(ev); + down.setAction(MotionEvent.ACTION_DOWN); + super.onTouchEvent(down); + hasNotDoneActionDown = false; + } + if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) { + hasNotDoneActionDown = true; + } + return super.onTouchEvent(ev); + } + + @Override + protected void onScrollChanged(int l, int t, int oldl, int oldt) { + super.onScrollChanged(l, t, oldl, oldt); + doTheStickyThing(); + } + + private void doTheStickyThing() { + View viewThatShouldStick = null; + View approachingView = null; + for (View v : stickyViews) { + int viewTop = getTopForViewRelativeOnlyChild(v) - getScrollY() + + (clippingToPadding ? 0 : getPaddingTop()); + if (viewTop <= 0) { + if (viewThatShouldStick == null || viewTop > + (getTopForViewRelativeOnlyChild(viewThatShouldStick) + - getScrollY() + (clippingToPadding ? 0 : getPaddingTop()))) { + viewThatShouldStick = v; + } + } else { + if (approachingView == null || viewTop < (getTopForViewRelativeOnlyChild(approachingView) + - getScrollY() + (clippingToPadding ? 0 : getPaddingTop()))) { + approachingView = v; + } + } + } + if (viewThatShouldStick != null) { + stickyViewTopOffset = approachingView == null ? 0 : + Math.min(0, getTopForViewRelativeOnlyChild(approachingView) - getScrollY() + + (clippingToPadding ? 0 : getPaddingTop()) - viewThatShouldStick.getHeight()); + if (viewThatShouldStick != currentlyStickingView) { + if (currentlyStickingView != null) { + if (mOnViewStickyListeners != null) + for (OnViewStickyListener onViewStickyListener : mOnViewStickyListeners) + onViewStickyListener.onUnSticky(currentlyStickingView); + stopStickingCurrentlyStickingView(); + } + // only compute the x offset when we start sticking. + stickyViewLeftOffset = getLeftForViewRelativeOnlyChild(viewThatShouldStick); + startStickingView(viewThatShouldStick); + if (mOnViewStickyListeners != null) + for (OnViewStickyListener onViewStickyListener : mOnViewStickyListeners) + onViewStickyListener.onSticky(currentlyStickingView); + } + } else if (currentlyStickingView != null) { + if (mOnViewStickyListeners != null) + for (OnViewStickyListener onViewStickyListener : mOnViewStickyListeners) + onViewStickyListener.onUnSticky(currentlyStickingView); + stopStickingCurrentlyStickingView(); + } + } + + private void startStickingView(View viewThatShouldStick) { + currentlyStickingView = viewThatShouldStick; + if (currentlyStickingView != null) { + if (getStringTagForView(currentlyStickingView).contains(FLAG_HASTRANSPARENCY)) { + hideView(currentlyStickingView); + } + if (getStringTagForView(currentlyStickingView).contains(FLAG_NONCONSTANT)) { + post(invalidateRunnable); + } + } + } + + private void stopStickingCurrentlyStickingView() { + if (getStringTagForView(currentlyStickingView).contains(FLAG_HASTRANSPARENCY)) { + showView(currentlyStickingView); + } + currentlyStickingView = null; + removeCallbacks(invalidateRunnable); + } + + @Override + protected void onDetachedFromWindow() { + removeCallbacks(invalidateRunnable); + super.onDetachedFromWindow(); + } + + /** + * Notify that the sticky attribute has been added or removed from one or more views in the View hierarchy + */ + public void notifyStickyAttributeChanged() { + notifyHierarchyChanged(); + } + + private void notifyHierarchyChanged() { + if (currentlyStickingView != null) { + stopStickingCurrentlyStickingView(); + } + stickyViews.clear(); + findStickyViews(getChildAt(0)); + doTheStickyThing(); + invalidate(); + } + + private void findStickyViews(View v) { + if (!detainStickyView(v) && (v instanceof ViewGroup)) { + ViewGroup vg = (ViewGroup) v; + for (int i = 0; i < vg.getChildCount(); i++) + findStickyViews(vg.getChildAt(i)); + } + } + + private boolean detainStickyView(View view) { + String tag = getStringTagForView(view); + if (tag.contains(STICKY_TAG)) { + stickyViews.add(view); + return true; + } + return false; + } + + private String getStringTagForView(View v) { + Object tagObject = v.getTag(); + return String.valueOf(tagObject); + } + + private void hideView(View v) { + v.setAlpha(0); + } + + private void showView(View v) { + v.setAlpha(1); + } + +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/widget/StrongRadioGroup.java b/common_base/src/main/java/com/wss/common/widget/StrongRadioGroup.java new file mode 100644 index 0000000..226a45f --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/StrongRadioGroup.java @@ -0,0 +1,377 @@ +package com.wss.common.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CompoundButton; +import android.widget.LinearLayout; +import android.widget.RadioButton; + + +/** + * Describe:可添加任意ViewGroup的RadioGroup + * Created by 吴天强 on 2019/3/18. + */ +public class StrongRadioGroup extends LinearLayout { + private int mCheckedId = -1; + private CompoundButton.OnCheckedChangeListener mChildOnCheckedChangeListener; + private boolean mProtectFromCheckedChange = false; + private OnCheckedChangeListener mOnCheckedChangeListener; + private PassThroughHierarchyChangeListener mPassThroughListener; + + public StrongRadioGroup(Context context) { + super(context); + setOrientation(VERTICAL); + init(); + } + + public StrongRadioGroup(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + private void init() { + mChildOnCheckedChangeListener = new CheckedStateTracker(); + mPassThroughListener = new PassThroughHierarchyChangeListener(); + super.setOnHierarchyChangeListener(mPassThroughListener); + } + + @Override + public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) { + // the user listener is delegated to our pass-through listener + mPassThroughListener.mOnHierarchyChangeListener = listener; + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + // checks the appropriate radio button as requested in the XML file + if (mCheckedId != -1) { + mProtectFromCheckedChange = true; + setCheckedStateForView(mCheckedId, true); + mProtectFromCheckedChange = false; + setCheckedId(mCheckedId); + } + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + if (child instanceof RadioButton) { + final RadioButton button = (RadioButton) child; + if (button.isChecked()) { + mProtectFromCheckedChange = true; + if (mCheckedId != -1) { + setCheckedStateForView(mCheckedId, false); + } + mProtectFromCheckedChange = false; + setCheckedId(button.getId()); + } + } else if (child instanceof ViewGroup) { + //添加部分 + final RadioButton button = findRadioButton((ViewGroup) child); + if (button.isChecked()) { + mProtectFromCheckedChange = true; + if (mCheckedId != -1) { + setCheckedStateForView(mCheckedId, false); + } + mProtectFromCheckedChange = false; + setCheckedId(button.getId()); + } + } + + super.addView(child, index, params); + } + + /** + * 查找radioButton控件 + */ + public RadioButton findRadioButton(ViewGroup group) { + RadioButton resBtn = null; + int len = group.getChildCount(); + for (int i = 0; i < len; i++) { + if (group.getChildAt(i) instanceof RadioButton) { + resBtn = (RadioButton) group.getChildAt(i); + } else if (group.getChildAt(i) instanceof ViewGroup) { + findRadioButton((ViewGroup) group.getChildAt(i)); + } + } + return resBtn; + } + + /** + *

+ * Sets the selection to the radio button whose identifier is passed in + * parameter. Using -1 as the selection identifier clears the selection; + * such an operation is equivalent to invoking {@link #clearCheck()}. + *

+ * + * @param id the unique id of the radio button to select in this group + * @see #getCheckedRadioButtonId() + * @see #clearCheck() + */ + public void check(int id) { + // don't even bother + if (id != -1 && (id == mCheckedId)) { + return; + } + + if (mCheckedId != -1) { + setCheckedStateForView(mCheckedId, false); + } + + if (id != -1) { + setCheckedStateForView(id, true); + } + + setCheckedId(id); + } + + private void setCheckedId(int id) { + mCheckedId = id; + if (mOnCheckedChangeListener != null) { + mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId); + } + } + + private void setCheckedStateForView(int viewId, boolean checked) { + View checkedView = findViewById(viewId); + if (checkedView != null && checkedView instanceof RadioButton) { + ((RadioButton) checkedView).setChecked(checked); + } + } + + /** + *

+ * Returns the identifier of the selected radio button in this group. Upon + * empty selection, the returned value is -1. + *

+ * + * @return the unique id of the selected radio button in this group + * @see #check(int) + * @see #clearCheck() + */ + public int getCheckedRadioButtonId() { + return mCheckedId; + } + + /** + *

+ * Clears the selection. When the selection is cleared, no radio button in + * this group is selected and {@link #getCheckedRadioButtonId()} returns + * null. + *

+ * + * @see #check(int) + * @see #getCheckedRadioButtonId() + */ + public void clearCheck() { + check(-1); + } + + /** + *

+ * Register a callback to be invoked when the checked radio button changes + * in this group. + *

+ * + * @param listener the callback to call on checked state change + */ + public void setOnCheckedChangeListener(OnCheckedChangeListener listener) { + mOnCheckedChangeListener = listener; + } + + /** + * {@inheritDoc} + */ + @Override + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return new LayoutParams(getContext(), attrs); + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return p instanceof LayoutParams; + } + + @Override + protected LinearLayout.LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + } + + /** + *

+ * This set of layout parameters defaults the width and the height of the + * children to {@link #WRAP_CONTENT} when they are not specified in the XML + * file. Otherwise, this class ussed the value read from the XML file. + *

+ *

+ *

+ * Attributes} for a list of all child view attributes that this class + * supports. + *

+ */ + public static class LayoutParams extends LinearLayout.LayoutParams { + /** + * {@inheritDoc} + */ + public LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + } + + /** + * {@inheritDoc} + */ + public LayoutParams(int w, int h) { + super(w, h); + } + + /** + * {@inheritDoc} + */ + public LayoutParams(int w, int h, float initWeight) { + super(w, h, initWeight); + } + + /** + * {@inheritDoc} + */ + public LayoutParams(ViewGroup.LayoutParams p) { + super(p); + } + + /** + * {@inheritDoc} + */ + public LayoutParams(MarginLayoutParams source) { + super(source); + } + + /** + *

+ * Fixes the child's width to + * {@link ViewGroup.LayoutParams#WRAP_CONTENT} and the + * child's height to + * {@link ViewGroup.LayoutParams#WRAP_CONTENT} when not + * specified in the XML file. + *

+ * + * @param a the styled attributes set + * @param widthAttr the width attribute to fetch + * @param heightAttr the height attribute to fetch + */ + @Override + protected void setBaseAttributes(TypedArray a, int widthAttr, + int heightAttr) { + + if (a.hasValue(widthAttr)) { + width = a.getLayoutDimension(widthAttr, "layout_width"); + } else { + width = WRAP_CONTENT; + } + + if (a.hasValue(heightAttr)) { + height = a.getLayoutDimension(heightAttr, "layout_height"); + } else { + height = WRAP_CONTENT; + } + } + } + + /** + *

+ * Interface definition for a callback to be invoked when the checked radio + * button changed in this group. + *

+ */ + public interface OnCheckedChangeListener { + /** + *

+ * Called when the checked radio button has changed. When the selection + * is cleared, checkedId is -1. + *

+ * + * @param group the group in which the checked radio button has changed + * @param checkedId the unique identifier of the newly checked radio button + */ + public void onCheckedChanged(StrongRadioGroup group, int checkedId); + } + + private class CheckedStateTracker implements + CompoundButton.OnCheckedChangeListener { + public void onCheckedChanged(CompoundButton buttonView, + boolean isChecked) { + // prevents from infinite recursion + if (mProtectFromCheckedChange) { + return; + } + + mProtectFromCheckedChange = true; + if (mCheckedId != -1) { + setCheckedStateForView(mCheckedId, false); + } + mProtectFromCheckedChange = false; + + int id = buttonView.getId(); + setCheckedId(id); + } + } + + /** + *

+ * A pass-through listener acts upon the events and dispatches them to + * another listener. This allows the table layout to set its own internal + * hierarchy change listener without preventing the user to setup his. + *

+ */ + private class PassThroughHierarchyChangeListener implements + OnHierarchyChangeListener { + private OnHierarchyChangeListener mOnHierarchyChangeListener; + + public void onChildViewAdded(View parent, View child) { + if (parent == StrongRadioGroup.this && child instanceof RadioButton) { + int id = child.getId(); + // generates an id if it's missing + if (id == View.NO_ID) { + id = child.hashCode(); + child.setId(id); + } + ((RadioButton) child) + .setOnCheckedChangeListener(mChildOnCheckedChangeListener); + } else if (parent == StrongRadioGroup.this && child instanceof ViewGroup) { + //添加部分 + RadioButton btn = findRadioButton((ViewGroup) child); + int id = btn.getId(); + // generates an id if it's missing + if (id == View.NO_ID) { + id = btn.hashCode(); + btn.setId(id); + } + btn.setOnCheckedChangeListener(mChildOnCheckedChangeListener); + } + + if (mOnHierarchyChangeListener != null) { + mOnHierarchyChangeListener.onChildViewAdded(parent, child); + } + } + + public void onChildViewRemoved(View parent, View child) { + if (parent == StrongRadioGroup.this && child instanceof RadioButton) { + ((RadioButton) child).setOnCheckedChangeListener(null); + } else if (parent == StrongRadioGroup.this && child instanceof ViewGroup) { + //添加部分 + findRadioButton((ViewGroup) child).setOnCheckedChangeListener( + null); + } + if (mOnHierarchyChangeListener != null) { + mOnHierarchyChangeListener.onChildViewRemoved(parent, child); + } + } + } +} diff --git a/common_base/src/main/java/com/wss/common/widget/SwipeItemLayout.java b/common_base/src/main/java/com/wss/common/widget/SwipeItemLayout.java new file mode 100644 index 0000000..dada519 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/SwipeItemLayout.java @@ -0,0 +1,795 @@ +package com.wss.common.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.view.animation.Interpolator; +import android.widget.Scroller; + +import androidx.core.view.ViewCompat; +import androidx.recyclerview.widget.RecyclerView; + +/** + * Describe:可侧滑ViewGroup,其中子ViewGroup有且只能有2个 + * Created by 吴天强 on 2020/4/20. + * Copy from: https://github.com/fornana/swipeitemlayout + */ +public class SwipeItemLayout extends ViewGroup { + enum Mode { + RESET, DRAG, FLING, TAP + } + + private Mode mTouchMode; + + private ViewGroup mMainView; + private ViewGroup mSideView; + + private ScrollRunnable mScrollRunnable; + private int mScrollOffset; + private int mMaxScrollOffset; + + private boolean mInLayout; + private boolean mIsLaidOut; + private OnItemSwipeListener onItemSwipeListener; + + public SwipeItemLayout(Context context) { + this(context, null); + } + + public SwipeItemLayout(Context context, AttributeSet attrs) { + super(context, attrs); + + mTouchMode = Mode.RESET; + mScrollOffset = 0; + mIsLaidOut = false; + + mScrollRunnable = new ScrollRunnable(context); + } + + public boolean isOpen() { + return mScrollOffset != 0; + } + + Mode getTouchMode() { + return mTouchMode; + } + + void setTouchMode(Mode mode) { + switch (mTouchMode) { + case FLING: + mScrollRunnable.abort(); + break; + case RESET: + break; + } + + mTouchMode = mode; + } + + public void open() { + if (mScrollOffset != -mMaxScrollOffset) { + //正在open,不需要处理 + if (mTouchMode == Mode.FLING && mScrollRunnable.isScrollToLeft()) + return; + + //当前正在向右滑,abort + if (mTouchMode == Mode.FLING /*&& !mScrollRunnable.mScrollToLeft*/) + mScrollRunnable.abort(); + + mScrollRunnable.startScroll(mScrollOffset, -mMaxScrollOffset); + } + } + + public void close() { + if (mScrollOffset != 0) { + //正在close,不需要处理 + if (mTouchMode == Mode.FLING && !mScrollRunnable.isScrollToLeft()) + return; + + //当前正向左滑,abort + if (mTouchMode == Mode.FLING /*&& mScrollRunnable.mScrollToLeft*/) + mScrollRunnable.abort(); + + mScrollRunnable.startScroll(mScrollOffset, 0); + } + } + + public void setOnItemSwipeListener(OnItemSwipeListener listener) { + this.onItemSwipeListener = listener; + } + + void fling(int xVel) { + mScrollRunnable.startFling(mScrollOffset, xVel); + } + + void revise() { + if (mScrollOffset < -mMaxScrollOffset / 2) + open(); + else + close(); + } + + boolean trackMotionScroll(int deltaX) { + if (deltaX == 0) + return false; + + boolean over = false; + int newLeft = mScrollOffset + deltaX; + if ((deltaX > 0 && newLeft > 0) || (deltaX < 0 && newLeft < -mMaxScrollOffset)) { + over = true; + newLeft = Math.min(newLeft, 0); + newLeft = Math.max(newLeft, -mMaxScrollOffset); + } + + offsetChildrenLeftAndRight(newLeft - mScrollOffset); + mScrollOffset = newLeft; + return over; + } + + private boolean ensureChildren() { + int childCount = getChildCount(); + + if (childCount != 2) + return false; + + View childView = getChildAt(0); + if (!(childView instanceof ViewGroup)) + return false; + mMainView = (ViewGroup) childView; + + childView = getChildAt(1); + if (!(childView instanceof ViewGroup)) + return false; + mSideView = (ViewGroup) childView; + return true; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (!ensureChildren()) + throw new RuntimeException("SwipeItemLayout的子视图不符合规定"); + + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + + MarginLayoutParams lp = null; + int horizontalMargin, verticalMargin; + int horizontalPadding = getPaddingLeft() + getPaddingRight(); + int verticalPadding = getPaddingTop() + getPaddingBottom(); + + lp = (MarginLayoutParams) mMainView.getLayoutParams(); + horizontalMargin = lp.leftMargin + lp.rightMargin; + verticalMargin = lp.topMargin + lp.bottomMargin; + measureChildWithMargins(mMainView, + widthMeasureSpec, horizontalMargin + horizontalPadding, + heightMeasureSpec, verticalMargin + verticalPadding); + + if (widthMode == MeasureSpec.AT_MOST) + widthSize = Math.min(widthSize, mMainView.getMeasuredWidth() + horizontalMargin + horizontalPadding); + else if (widthMode == MeasureSpec.UNSPECIFIED) + widthSize = mMainView.getMeasuredWidth() + horizontalMargin + horizontalPadding; + + if (heightMode == MeasureSpec.AT_MOST) + heightSize = Math.min(heightSize, mMainView.getMeasuredHeight() + verticalMargin + verticalPadding); + else if (heightMode == MeasureSpec.UNSPECIFIED) + heightSize = mMainView.getMeasuredHeight() + verticalMargin + verticalPadding; + + setMeasuredDimension(widthSize, heightSize); + + //side layout大小为自身实际大小 + lp = (MarginLayoutParams) mSideView.getLayoutParams(); + verticalMargin = lp.topMargin + lp.bottomMargin; + mSideView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), + MeasureSpec.makeMeasureSpec(getMeasuredHeight() - verticalMargin - verticalPadding, MeasureSpec.EXACTLY)); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + if (!ensureChildren()) + throw new RuntimeException("SwipeItemLayout的子视图不符合规定"); + + mInLayout = true; + + int pl = getPaddingLeft(); + int pt = getPaddingTop(); + int pr = getPaddingRight(); + int pb = getPaddingBottom(); + + MarginLayoutParams mainLp = (MarginLayoutParams) mMainView.getLayoutParams(); + MarginLayoutParams sideParams = (MarginLayoutParams) mSideView.getLayoutParams(); + + int childLeft = pl + mainLp.leftMargin; + int childTop = pt + mainLp.topMargin; + int childRight = getWidth() - (pr + mainLp.rightMargin); + int childBottom = getHeight() - (mainLp.bottomMargin + pb); + mMainView.layout(childLeft, childTop, childRight, childBottom); + + childLeft = childRight + sideParams.leftMargin; + childTop = pt + sideParams.topMargin; + childRight = childLeft + sideParams.leftMargin + sideParams.rightMargin + mSideView.getMeasuredWidth(); + childBottom = getHeight() - (sideParams.bottomMargin + pb); + mSideView.layout(childLeft, childTop, childRight, childBottom); + + mMaxScrollOffset = mSideView.getWidth() + sideParams.leftMargin + sideParams.rightMargin; + mScrollOffset = mScrollOffset < -mMaxScrollOffset / 2 ? -mMaxScrollOffset : 0; + + offsetChildrenLeftAndRight(mScrollOffset); + mInLayout = false; + mIsLaidOut = true; + } + + void offsetChildrenLeftAndRight(int delta) { + ViewCompat.offsetLeftAndRight(mMainView, delta); + ViewCompat.offsetLeftAndRight(mSideView, delta); + } + + @Override + public void requestLayout() { + if (!mInLayout) { + super.requestLayout(); + } + } + + @Override + protected LayoutParams generateDefaultLayoutParams() { + return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + } + + @Override + protected LayoutParams generateLayoutParams(LayoutParams p) { + return p instanceof MarginLayoutParams ? p : new MarginLayoutParams(p); + } + + @Override + protected boolean checkLayoutParams(LayoutParams p) { + return p instanceof MarginLayoutParams && super.checkLayoutParams(p); + } + + @Override + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return new MarginLayoutParams(getContext(), attrs); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mScrollOffset != 0 && mIsLaidOut) { + offsetChildrenLeftAndRight(-mScrollOffset); + mScrollOffset = 0; + } else + mScrollOffset = 0; + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + if (mScrollOffset != 0 && mIsLaidOut) { + offsetChildrenLeftAndRight(-mScrollOffset); + mScrollOffset = 0; + } else + mScrollOffset = 0; + removeCallbacks(mScrollRunnable); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + final int action = ev.getActionMasked(); + //click main view,但是它处于open状态,所以,不需要点击效果,直接拦截不调用click listener + switch (action) { + case MotionEvent.ACTION_DOWN: { + final int x = (int) ev.getX(); + final int y = (int) ev.getY(); + View pointView = findTopChildUnder(this, x, y); + if (pointView != null && pointView == mMainView && mScrollOffset != 0) + return true; + break; + } + + case MotionEvent.ACTION_MOVE: + case MotionEvent.ACTION_CANCEL: + break; + + case MotionEvent.ACTION_UP: { + final int x = (int) ev.getX(); + final int y = (int) ev.getY(); + View pointView = findTopChildUnder(this, x, y); + if (pointView != null && pointView == mMainView && mTouchMode == Mode.TAP && mScrollOffset != 0) + return true; + } + } + + return false; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + final int action = ev.getActionMasked(); + //click main view,但是它处于open状态,所以,不需要点击效果,直接拦截不调用click listener + switch (action) { + case MotionEvent.ACTION_DOWN: { + final int x = (int) ev.getX(); + final int y = (int) ev.getY(); + View pointView = findTopChildUnder(this, x, y); + if (pointView != null && pointView == mMainView && mScrollOffset != 0) + return true; + break; + } + + case MotionEvent.ACTION_MOVE: + case MotionEvent.ACTION_CANCEL: + break; + + case MotionEvent.ACTION_UP: { + final int x = (int) ev.getX(); + final int y = (int) ev.getY(); + View pointView = findTopChildUnder(this, x, y); + if (pointView != null && pointView == mMainView && mTouchMode == Mode.TAP && mScrollOffset != 0) { + close(); + return true; + } + } + } + + return false; + } + + @Override + protected void onVisibilityChanged(View changedView, int visibility) { + super.onVisibilityChanged(changedView, visibility); + if (getVisibility() != View.VISIBLE) { + mScrollOffset = 0; + invalidate(); + } + } + + private static final Interpolator sInterpolator = new Interpolator() { + @Override + public float getInterpolation(float t) { + t -= 1.0f; + return t * t * t * t * t + 1.0f; + } + }; + + class ScrollRunnable implements Runnable { + private static final int FLING_DURATION = 200; + private Scroller mScroller; + private boolean mAbort; + private int mMinVelocity; + private boolean mScrollToLeft; + + ScrollRunnable(Context context) { + mScroller = new Scroller(context, sInterpolator); + mAbort = false; + mScrollToLeft = false; + + ViewConfiguration configuration = ViewConfiguration.get(context); + mMinVelocity = configuration.getScaledMinimumFlingVelocity(); + } + + void startScroll(int startX, int endX) { + if (startX != endX) { + Log.e("scroll - startX - endX", "" + startX + " " + endX); + setTouchMode(Mode.FLING); + mAbort = false; + mScrollToLeft = endX < startX; + mScroller.startScroll(startX, 0, endX - startX, 0, 400); + ViewCompat.postOnAnimation(SwipeItemLayout.this, this); + if (onItemSwipeListener != null) { + onItemSwipeListener.onItemSwipe(mScrollToLeft); + } + } + } + + void startFling(int startX, int xVel) { + Log.e("fling - startX", "" + startX); + + if (xVel > mMinVelocity && startX != 0) { + startScroll(startX, 0); + return; + } + + if (xVel < -mMinVelocity && startX != -mMaxScrollOffset) { + startScroll(startX, -mMaxScrollOffset); + return; + } + + startScroll(startX, startX > -mMaxScrollOffset / 2 ? 0 : -mMaxScrollOffset); + } + + void abort() { + if (!mAbort) { + mAbort = true; + if (!mScroller.isFinished()) { + mScroller.abortAnimation(); + removeCallbacks(this); + } + } + } + + //是否正在滑动需要另外判断 + boolean isScrollToLeft() { + return mScrollToLeft; + } + + @Override + public void run() { + Log.e("abort", Boolean.toString(mAbort)); + if (!mAbort) { + boolean more = mScroller.computeScrollOffset(); + int curX = mScroller.getCurrX(); + Log.e("curX", "" + curX); + + boolean atEdge = trackMotionScroll(curX - mScrollOffset); + if (more && !atEdge) { + ViewCompat.postOnAnimation(SwipeItemLayout.this, this); + return; + } + + if (atEdge) { + removeCallbacks(this); + if (!mScroller.isFinished()) + mScroller.abortAnimation(); + setTouchMode(Mode.RESET); + } + + if (!more) { + setTouchMode(Mode.RESET); + //绝对不会出现这种意外的!!!可以注释掉 + if (mScrollOffset != 0) { + if (Math.abs(mScrollOffset) > mMaxScrollOffset / 2) + mScrollOffset = -mMaxScrollOffset; + else + mScrollOffset = 0; + ViewCompat.postOnAnimation(SwipeItemLayout.this, this); + } + } + } + } + } + + public static class OnSwipeItemTouchListener implements RecyclerView.OnItemTouchListener { + private SwipeItemLayout mCaptureItem; + private float mLastMotionX; + private float mLastMotionY; + private VelocityTracker mVelocityTracker; + + private int mActivePointerId; + + private int mTouchSlop; + private int mMaximumVelocity; + + private boolean mDealByParent; + private boolean mIsProbeParent; + + public OnSwipeItemTouchListener(Context context) { + ViewConfiguration configuration = ViewConfiguration.get(context); + mTouchSlop = configuration.getScaledTouchSlop(); + mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); + mActivePointerId = -1; + mDealByParent = false; + mIsProbeParent = false; + } + + @Override + public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) { + if (mIsProbeParent) + return false; + + boolean intercept = false; + final int action = ev.getActionMasked(); + + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + mVelocityTracker.addMovement(ev); + + switch (action) { + case MotionEvent.ACTION_DOWN: { + mActivePointerId = ev.getPointerId(0); + final float x = ev.getX(); + final float y = ev.getY(); + mLastMotionX = x; + mLastMotionY = y; + + boolean pointOther = false; + SwipeItemLayout pointItem = null; + //首先知道ev针对的是哪个item + View pointView = findTopChildUnder(rv, (int) x, (int) y); + if (pointView == null || !(pointView instanceof SwipeItemLayout)) { + //可能是head view或bottom view + pointOther = true; + } else + pointItem = (SwipeItemLayout) pointView; + + //此时的pointOther=true,意味着点击的view为空或者点击的不是item + //还没有把点击的是item但是不是capture item给过滤出来 + if (!pointOther && (mCaptureItem == null || mCaptureItem != pointItem)) + pointOther = true; + + //点击的是capture item + if (!pointOther) { + Mode touchMode = mCaptureItem.getTouchMode(); + + //如果它在fling,就转为drag + //需要拦截,并且requestDisallowInterceptTouchEvent + boolean disallowIntercept = false; + if (touchMode == Mode.FLING) { + mCaptureItem.setTouchMode(Mode.DRAG); + disallowIntercept = true; + intercept = true; + } else {//如果是expand的,就不允许parent拦截 + mCaptureItem.setTouchMode(Mode.TAP); + if (mCaptureItem.isOpen()) + disallowIntercept = true; + } + + if (disallowIntercept) { + final ViewParent parent = rv.getParent(); + if (parent != null) + parent.requestDisallowInterceptTouchEvent(true); + } + } else {//capture item为null或者与point item不一样 + //直接将其close掉 + if (mCaptureItem != null && mCaptureItem.isOpen()) { + mCaptureItem.close(); + mCaptureItem = null; + intercept = true; + } + + if (pointItem != null) { + mCaptureItem = pointItem; + mCaptureItem.setTouchMode(Mode.TAP); + } else + mCaptureItem = null; + } + + //如果parent处于fling状态,此时,parent就会转为drag。此时,应该将后续move都交给parent处理 + mIsProbeParent = true; + mDealByParent = rv.onInterceptTouchEvent(ev); + mIsProbeParent = false; + if (mDealByParent) + intercept = false; + break; + } + + case MotionEvent.ACTION_POINTER_DOWN: { + final int actionIndex = ev.getActionIndex(); + mActivePointerId = ev.getPointerId(actionIndex); + + mLastMotionX = ev.getX(actionIndex); + mLastMotionY = ev.getY(actionIndex); + break; + } + + case MotionEvent.ACTION_POINTER_UP: { + final int actionIndex = ev.getActionIndex(); + final int pointerId = ev.getPointerId(actionIndex); + if (pointerId == mActivePointerId) { + final int newIndex = actionIndex == 0 ? 1 : 0; + mActivePointerId = ev.getPointerId(newIndex); + + mLastMotionX = ev.getX(newIndex); + mLastMotionY = ev.getY(newIndex); + } + break; + } + + //down时,已经将capture item定下来了。所以,后面可以安心考虑event处理 + case MotionEvent.ACTION_MOVE: { + final int activePointerIndex = ev.findPointerIndex(mActivePointerId); + if (activePointerIndex == -1) + break; + + //在down时,就被认定为parent的drag,所以,直接交给parent处理即可 + if (mDealByParent) { + if (mCaptureItem != null && mCaptureItem.isOpen()) + mCaptureItem.close(); + return false; + } + + final int x = (int) (ev.getX(activePointerIndex) + .5f); + final int y = (int) ((int) ev.getY(activePointerIndex) + .5f); + + int deltaX = (int) (x - mLastMotionX); + int deltaY = (int) (y - mLastMotionY); + final int xDiff = Math.abs(deltaX); + final int yDiff = Math.abs(deltaY); + + if (mCaptureItem != null && !mDealByParent) { + Mode touchMode = mCaptureItem.getTouchMode(); + + if (touchMode == Mode.TAP) { + //如果capture item是open的,下拉有两种处理方式: + // 1、下拉后,直接close item + // 2、只要是open的,就拦截所有它的消息,这样如果点击open的,就只能滑动该capture item + //网易邮箱,在open的情况下,下拉直接close + //QQ,在open的情况下,下拉也是close。但是,做的不够好,没有达到该效果。 + if (xDiff > mTouchSlop && xDiff > yDiff) { + mCaptureItem.setTouchMode(Mode.DRAG); + final ViewParent parent = rv.getParent(); + parent.requestDisallowInterceptTouchEvent(true); + + deltaX = deltaX > 0 ? deltaX - mTouchSlop : deltaX + mTouchSlop; + } else {// if(yDiff>mTouchSlop){ + mIsProbeParent = true; + boolean isParentConsume = rv.onInterceptTouchEvent(ev); + mIsProbeParent = false; + if (isParentConsume) { + //表明不是水平滑动,即不判定为SwipeItemLayout的滑动 + //但是,可能是下拉刷新SwipeRefreshLayout或者RecyclerView的滑动 + //一般的下拉判定,都是yDiff>mTouchSlop,所以,此处这么写不会出问题 + //这里这么做以后,如果判定为下拉,就直接close + mDealByParent = true; + mCaptureItem.close(); + } + } + } + + touchMode = mCaptureItem.getTouchMode(); + if (touchMode == Mode.DRAG) { + intercept = true; + mLastMotionX = x; + mLastMotionY = y; + + //对capture item进行拖拽 + mCaptureItem.trackMotionScroll(deltaX); + } + } + break; + } + + case MotionEvent.ACTION_UP: + if (mCaptureItem != null) { + Mode touchMode = mCaptureItem.getTouchMode(); + if (touchMode == Mode.DRAG) { + final VelocityTracker velocityTracker = mVelocityTracker; + velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); + int xVel = (int) velocityTracker.getXVelocity(mActivePointerId); + mCaptureItem.fling(xVel); + + intercept = true; + } + } + cancel(); + break; + + case MotionEvent.ACTION_CANCEL: + if (mCaptureItem != null) + mCaptureItem.revise(); + cancel(); + break; + } + + return intercept; + } + + @Override + public void onTouchEvent(RecyclerView rv, MotionEvent ev) { + final int action = ev.getActionMasked(); + final int actionIndex = ev.getActionIndex(); + + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + mVelocityTracker.addMovement(ev); + + switch (action) { + case MotionEvent.ACTION_POINTER_DOWN: + mActivePointerId = ev.getPointerId(actionIndex); + + mLastMotionX = ev.getX(actionIndex); + mLastMotionY = ev.getY(actionIndex); + break; + + case MotionEvent.ACTION_POINTER_UP: + final int pointerId = ev.getPointerId(actionIndex); + if (pointerId == mActivePointerId) { + final int newIndex = actionIndex == 0 ? 1 : 0; + mActivePointerId = ev.getPointerId(newIndex); + + mLastMotionX = ev.getX(newIndex); + mLastMotionY = ev.getY(newIndex); + } + break; + + //down时,已经将capture item定下来了。所以,后面可以安心考虑event处理 + case MotionEvent.ACTION_MOVE: { + final int activePointerIndex = ev.findPointerIndex(mActivePointerId); + if (activePointerIndex == -1) + break; + + final float x = ev.getX(activePointerIndex); + final float y = (int) ev.getY(activePointerIndex); + + int deltaX = (int) (x - mLastMotionX); + + if (mCaptureItem != null && mCaptureItem.getTouchMode() == Mode.DRAG) { + mLastMotionX = x; + mLastMotionY = y; + + //对capture item进行拖拽 + mCaptureItem.trackMotionScroll(deltaX); + } + break; + } + + case MotionEvent.ACTION_UP: + if (mCaptureItem != null) { + Mode touchMode = mCaptureItem.getTouchMode(); + if (touchMode == Mode.DRAG) { + final VelocityTracker velocityTracker = mVelocityTracker; + velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); + int xVel = (int) velocityTracker.getXVelocity(mActivePointerId); + mCaptureItem.fling(xVel); + } + } + cancel(); + break; + + case MotionEvent.ACTION_CANCEL: + if (mCaptureItem != null) + mCaptureItem.revise(); + + cancel(); + break; + + } + } + + @Override + public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { + } + + void cancel() { + mDealByParent = false; + mActivePointerId = -1; + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + } + + } + + + public interface OnItemSwipeListener { + + void onItemSwipe(boolean isOpen); + } + + + static View findTopChildUnder(ViewGroup parent, int x, int y) { + final int childCount = parent.getChildCount(); + for (int i = childCount - 1; i >= 0; i--) { + final View child = parent.getChildAt(i); + if (x >= child.getLeft() && x < child.getRight() + && y >= child.getTop() && y < child.getBottom()) { + return child; + } + } + return null; + } + + public static void closeAllItems(RecyclerView recyclerView) { + for (int i = 0; i < recyclerView.getChildCount(); i++) { + View child = recyclerView.getChildAt(i); + if (child instanceof SwipeItemLayout) { + SwipeItemLayout swipeItemLayout = (SwipeItemLayout) child; + if (swipeItemLayout.isOpen()) + swipeItemLayout.close(); + } + } + } + + +} diff --git a/common_base/src/main/java/com/wss/common/widget/ViewPagerForScrollView.java b/common_base/src/main/java/com/wss/common/widget/ViewPagerForScrollView.java new file mode 100644 index 0000000..56897e5 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/ViewPagerForScrollView.java @@ -0,0 +1,126 @@ +package com.wss.common.widget; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; + +import com.wss.common.utils.PxUtils; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.LinkedHashMap; + +import androidx.viewpager.widget.ViewPager; + +/** + * ViewPager和ScrollView嵌套空白问题 + * + * @author 杨伟 + * create by 2020-4-17 + */ +public class ViewPagerForScrollView extends ViewPager implements Serializable { + + private static final long serialVersionUID = 1L; + + + private int current; + private int height = 0; + /** + * 保存position与对于的View + */ + private HashMap mChildrenViews = new LinkedHashMap<>(); + private boolean scroll = true; + /** + * 最小高度 + */ + private int minHeight; + + public ViewPagerForScrollView(Context context) { + super(context); + } + + public ViewPagerForScrollView(Context context, AttributeSet attrs) { + super(context, attrs); + minHeight = PxUtils.getScreenHeight(context); + } + + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + try { + if (mChildrenViews.size() > current) { + View child = mChildrenViews.get(current); + if (child != null) { + child.measure(widthMeasureSpec, View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); + height = child.getMeasuredHeight(); + //如果计算出ViewPager的高度小于最小高度,则使用最小高度作为ViewPager高度 + if (height < minHeight) { + height = minHeight; + } + } + } + heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 重置ViewPager的高度 + * + * @param current 第几页的 + */ + public void resetHeight(int current) { + this.current = current; + if (mChildrenViews.size() > current) { + ViewGroup.LayoutParams layoutParams = getLayoutParams(); + if (layoutParams == null) { + layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, height); + } else { + layoutParams.height = height; + } + + setLayoutParams(layoutParams); + } + } + + /** + * 保存position与对于的View + */ + public void setObjectForPosition(View view, int position) { + mChildrenViews.put(position, view); + } + + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (!scroll) { + return true; + } + return super.onTouchEvent(ev); + } + + /** + * 设置ViewPager显示的最小高度 + * + * @param minHeight 最小高度 + */ + public void setMinHeight(int minHeight) { + this.minHeight = minHeight; + } + + /** + * 设置是否可滚动 + * + * @param scroll 滚动 + */ + public void setScroll(boolean scroll) { + this.scroll = scroll; + } + +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/widget/WebViewProgressBar.java b/common_base/src/main/java/com/wss/common/widget/WebViewProgressBar.java deleted file mode 100644 index f9e53c7..0000000 --- a/common_base/src/main/java/com/wss/common/widget/WebViewProgressBar.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.wss.common.widget; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.util.AttributeSet; -import android.view.View; - -import com.wss.common.base.R; - - -/** - * WebView进度条 - * Created by wtq on 2016/12/15. - */ -public class WebViewProgressBar extends View { - private int progress = 1; - private final static int HEIGHT = 5; - private Paint paint; - private final static int colors[] = new int[]{}; - - public WebViewProgressBar(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public WebViewProgressBar(Context context) { - super(context); - paint = new Paint(Paint.DITHER_FLAG); - paint.setStyle(Paint.Style.STROKE); - paint.setStrokeWidth(HEIGHT); - paint.setAntiAlias(true); - paint.setColor(context.getResources().getColor(R.color.theme)); - } - - public void setProgress(int progress) { - this.progress = progress; - invalidate(); - } - - @Override - protected void onDraw(Canvas canvas) { - canvas.drawRect(0, 0, getWidth() * progress / 100, HEIGHT, paint); - } -} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/widget/dialog/AppDialog.java b/common_base/src/main/java/com/wss/common/widget/dialog/AppDialog.java index b6903f0..33a883c 100644 --- a/common_base/src/main/java/com/wss/common/widget/dialog/AppDialog.java +++ b/common_base/src/main/java/com/wss/common/widget/dialog/AppDialog.java @@ -2,8 +2,9 @@ import android.app.Dialog; import android.content.Context; -import android.text.Editable; -import android.text.TextUtils; +import android.content.DialogInterface; +import android.graphics.Color; +import android.text.InputType; import android.text.TextWatcher; import android.view.Gravity; import android.view.View; @@ -11,7 +12,6 @@ import android.view.WindowManager; import android.widget.Button; import android.widget.EditText; -import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; @@ -20,14 +20,19 @@ import com.wss.common.base.R2; import com.wss.common.utils.KeyboardUtils; import com.wss.common.utils.PxUtils; -import com.wss.common.widget.CountClickView; +import com.wss.common.utils.ValidUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; import java.util.List; +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; - +import butterknife.Unbinder; /** * Describe:自定义对话框 @@ -35,78 +40,105 @@ */ public class AppDialog { + private static final int MAX_ITEM = 7; + /** + * 对话框的顶级父类 + */ @BindView(R2.id.dialog_layout) - LinearLayout dialogLayout;//对话框的顶级父类 + LinearLayout dialogLayout; + /** + * 中间显示的Dialog 父View + */ @BindView(R2.id.ll_center) - LinearLayout llCenter;//中间显示的Dialog 父View + LinearLayout llCenter; @BindView(R2.id.tv_title) TextView tvTitle; @BindView(R2.id.tv_content) TextView tvContent; @BindView(R2.id.edt_input) EditText edtInput; - @BindView(R2.id.iv_minus) - ImageView ivMinus; - @BindView(R2.id.edt_count) - EditText edtCount; - @BindView(R2.id.iv_plus) - ImageView ivPlus; - @BindView(R2.id.ll_count_view) - LinearLayout llCountView; + @BindView(R2.id.btn_left) Button btnLeft; @BindView(R2.id.btn_right) Button btnRight; @BindView(R2.id.btn_line) View btnLine; - @BindView(R2.id.ll_content_layout) - LinearLayout contentLayout;//中间弹出对话框的View + /** + * 中间弹出对话框的Content View + */ + @BindView(R2.id.ll_content_layout) + LinearLayout contentLayout; + @BindView(R2.id.center_scroll_view) + ScrollView centerScrollView; + /** + * 底部弹出的Dialog 父View + */ @BindView(R2.id.ll_bottom) - LinearLayout llBottom;//底部弹出的Dialog 父View - @BindView(R2.id.tv_bottom_title) - TextView tvBottomTitle; + LinearLayout llBottom; @BindView(R2.id.ll_context) LinearLayout llContext; - @BindView(R2.id.sLayout_content) - ScrollView sLayoutContent; + @BindView(R2.id.bottom_scroll_view) + ScrollView bottomScrollView; @BindView(R2.id.tv_cancel) TextView tvCancel; - @DialogType.Type - private int type; - private int maxCount = CountClickView.MAX_COUNT; - private int minCount = CountClickView.MIN_COUNT; private Dialog dialog; - private Context mContext; + private Unbinder butterKnifeBinder; + private Builder builder; + + private AppDialog() { + + } + + private AppDialog(Builder builder) { + this.builder = builder; + } - private OnButtonClickListener leftListener; - private OnButtonClickListener rightListener; - private OnItemClickListener itemClickListener; - private String title;//标题 + /** + * 显示对话框 + */ + public void show() { + initView(); + dialog.show(); + } - public AppDialog(Context context) { - this(context, DialogType.DEFAULT); + public boolean isShowing() { + return dialog != null && dialog.isShowing(); } - public AppDialog(Context context, @DialogType.Type int type) { - this.mContext = context; - this.type = type; - View dialogView = View.inflate(mContext, R.layout.dialog_app, null); - ButterKnife.bind(this, dialogView); + /** + * 关闭对话框 + */ + public void dismiss() { + if (dialog != null) { + dialog.dismiss(); + } + if (butterKnifeBinder != null) { + butterKnifeBinder.unbind(); + } + } + + /** + * 初始化View + */ + private void initView() { + View dialogView = View.inflate(builder.context, R.layout.dialog_app, null); + butterKnifeBinder = ButterKnife.bind(this, dialogView); int themeResId = R.style.DialogStyle; - if (type == DialogType.BOTTOM_IN) { + if (builder.type == DialogType.BOTTOM_IN) { themeResId = R.style.ActionSheetDialogStyle; - dialogView.setMinimumWidth(PxUtils.getScreenWidth(mContext)); + dialogView.setMinimumWidth(PxUtils.getScreenWidth(builder.context)); } - dialog = new Dialog(mContext, themeResId); + dialog = new Dialog(builder.context, themeResId); dialog.setContentView(dialogView); Window window = dialog.getWindow(); if (window != null) { - if (type == DialogType.BOTTOM_IN) { + if (builder.type == DialogType.BOTTOM_IN) { window.getAttributes().gravity = Gravity.BOTTOM; WindowManager.LayoutParams lp = window.getAttributes(); lp.x = 0; @@ -116,499 +148,827 @@ public AppDialog(Context context, @DialogType.Type int type) { window.getAttributes().gravity = Gravity.CENTER; } } - dialog.setCanceledOnTouchOutside(false); - } - - private void initView() { - switch (type) { - case DialogType.DEFAULT: - setTitleText(); - break; + dialog.setCanceledOnTouchOutside(builder.canceledOnTouchOutside); + dialog.setCancelable(builder.cancelable); + dialog.setOnDismissListener(builder.dialogDismissListener); + switch (builder.type) { case DialogType.INPUT: - setTitleText(); - edtInput.setVisibility(View.VISIBLE); - tvContent.setVisibility(View.GONE); - llCountView.setVisibility(View.GONE); - dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); - break; - case DialogType.COUNT: - setTitleText(); - edtInput.setVisibility(View.GONE); - tvContent.setVisibility(View.GONE); - llCountView.setVisibility(View.VISIBLE); - dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); + //带文本框输入 + setInputSetting(); break; case DialogType.NO_TITLE: tvTitle.setVisibility(View.GONE); + setCommonSetting(); break; case DialogType.BOTTOM_IN: //底部弹出对话框 - tvBottomTitle.setText(title); - llCenter.setVisibility(View.GONE); + setBottomSetting(); + break; + case DialogType.REPLACE_ALL: + //替换全部View + dialogLayout.removeAllViews(); + dialogLayout.addView(builder.allDialogView); + break; + case DialogType.REPLACE_CONTENT: + //替换Content View + contentLayout.removeAllViews(); + contentLayout.addView(builder.contentView); + break; + case DialogType.REPLACE_BOTTOM: + //替换底部View llBottom.setVisibility(View.VISIBLE); + llBottom.removeAllViews(); + llBottom.addView(builder.bottomView); break; + case DialogType.DEFAULT: + //默认对话框 default: + setCommonSetting(); break; } } - private void setTitleText() { - if (!TextUtils.isEmpty(title)) { - tvTitle.setText(title); - } - } - /** - * 设置Title + * 常规对话框相关设置 */ - public AppDialog setTitle(String title) { - this.title = title; - return this; - } - - /** - * 设置显示内容 - */ - public AppDialog setContent(String content) { - tvContent.setText(content); - return this; + private void setCommonSetting() { + llCenter.setVisibility(View.VISIBLE); + tvContent.setTextSize(builder.contentTextSize); + btnLeft.setTextSize(builder.leftButtonTextSize); + btnRight.setTextSize(builder.rightButtonTextSize); + tvContent.setTextColor(builder.contentTextColor); + btnLeft.setTextColor(builder.leftButtonTextColor); + btnRight.setTextColor(builder.rightButtonTextColor); + if (ValidUtils.isValid(builder.title)) { + tvTitle.setText(builder.title); + } + if (ValidUtils.isValid(builder.letButtonText)) { + btnLeft.setText(builder.letButtonText); + } + if (ValidUtils.isValid(builder.rightButtonText)) { + btnRight.setText(builder.rightButtonText); + } + tvContent.setText(builder.content); + centerScrollView.post(() -> { + int height = centerScrollView.getHeight(); + int max = PxUtils.getScreenHeight(builder.context) * 3 / 5; + if (height > max) { + LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) bottomScrollView.getLayoutParams(); + params.height = max; + centerScrollView.setLayoutParams(params); + } + }); + if (builder.isSingleButton) { + //单按钮 + btnLeft.setVisibility(View.GONE); + btnLine.setVisibility(View.GONE); + btnRight.setBackgroundResource(R.drawable.corners_white_gray_selecter); + } } /** - * 加减输入模式下 设置的数字 - * - * @param minCount minCount - * @param maxCount maxCount - * @param current current + * 文本框输入类型相关设置 */ - public AppDialog setNumber(int minCount, int maxCount, int current) { - this.minCount = minCount; - this.maxCount = maxCount; - edtCount.setText(String.valueOf(current)); - edtCount.setSelection(edtCount.getText().length()); - edtCount.addTextChangedListener(textWatcher); - judgeTheViews(current); - return this; + private void setInputSetting() { + setCommonSetting(); + edtInput.setVisibility(View.VISIBLE); + tvContent.setVisibility(View.GONE); + if (dialog.getWindow() != null) { + dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); + } + edtInput.setTextSize(builder.inputTextSize); + edtInput.setTextColor(builder.inputTextColor); + edtInput.setHintTextColor(builder.inputHintTextColor); + edtInput.setInputType(builder.inputType); + if (builder.textWatcher != null) { + edtInput.addTextChangedListener(builder.textWatcher); + } + if (ValidUtils.isValid(builder.inputHintText)) { + edtInput.setHint(builder.inputHintText); + } + if (ValidUtils.isValid(builder.inputDefaultText)) { + edtInput.setText(builder.inputDefaultText); + edtInput.setSelection(builder.inputDefaultText.length()); + } } /** - * 显示对话框 + * 底部弹出类型相关设置 */ - public void show() { - initView(); - dialog.show(); + private void setBottomSetting() { + llBottom.setVisibility(View.VISIBLE); + tvCancel.setTextSize(builder.bottomCancelTextSize); + tvCancel.setTextColor(builder.bottomCancelTextColor); + if (ValidUtils.isValid(builder.bottomTitleText)) { + tvCancel.setText(builder.bottomCancelText); + } + int size = ValidUtils.isValid(builder.bottomItems) ? builder.bottomItems.size() : 0; + setItemScrollViewHeight(size); + llContext.removeAllViews(); + for (int i = 0; i < size; i++) { + View v = View.inflate(builder.context, R.layout.layout_item_of_dialog_bottom_in, null); + TextView item = v.findViewById(R.id.tv_text); + item.setText(builder.bottomItems.get(i)); + item.setTag(i); + item.setTextColor(builder.bottomItemTextColor); + item.setTextSize(builder.bottomItemTextSize); + item.setOnClickListener(v1 -> { + if (builder.itemClickListener != null) { + builder.itemClickListener.onItemClick((int) v1.getTag(), ((TextView) v1).getText().toString()); + } + dismiss(); + }); + llContext.addView(v); + } } - /** - * 关闭对话框 - */ - public void dismiss() { - if (dialog != null) { - dialog.dismiss(); + @OnClick({R2.id.btn_left, R2.id.btn_right, R2.id.tv_cancel}) + public void onClick(@NonNull View v) { + if (v.getId() == R.id.btn_left) { + onButtonClick(builder.letButtonListener); + } else if (v.getId() == R.id.btn_right) { + onButtonClick(builder.rightButtonListener); + } else if (v.getId() == R.id.tv_cancel) { + dismiss(); } } + /** - * 给左边按钮设置文字 事件 + * 给点击事件设置数据 * - * @param text 文字 - * @param listener 事件 - * @return AppDialog + * @param listener listener */ - public AppDialog setLeftButton(String text, OnButtonClickListener listener) { - this.leftListener = listener; - if (!TextUtils.isEmpty(text)) { - btnLeft.setText(text); + private void onButtonClick(OnButtonClickListener listener) { + if (builder.type == DialogType.INPUT) { + if (listener != null) { + listener.onClick(edtInput.getText().toString().trim()); + } + KeyboardUtils.hideKeyboard(edtInput); + } else { + if (listener != null) { + listener.onClick(tvContent.getText().toString()); + } } - return this; + dismiss(); } /** - * 给左边按钮设置文字 事件 + * 设置底部弹出带条目的ScrollView的高度 * - * @param listener 事件 - * @return AppDialog + * @param size 条目数量 */ - public AppDialog setLeftButton(OnButtonClickListener listener) { - return setLeftButton(null, listener); + private void setItemScrollViewHeight(int size) { + // 添加条目过多的时候控制高度 + if (size >= MAX_ITEM) { + LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) bottomScrollView.getLayoutParams(); + params.height = PxUtils.getScreenHeight(builder.context) / 2; + bottomScrollView.setLayoutParams(params); + } } + /** - * 给左边按钮设置文字 事件 + * 相机的Item * - * @param text text - * @return AppDialog - */ - public AppDialog setLeftButton(String text) { - return setLeftButton(text, null); - } + * @param context context + * @return List + */ + @NotNull + public static List getPhotoItem(@NotNull Context context) { + List items = new ArrayList<>(); + items.add(context.getString(R.string.camera)); + items.add(context.getString(R.string.album)); + return items; + } + + /** + * Describe:对话框建造者 + * Created by 吴天强 on 2018年9月27日 + */ + public static class Builder { + Context context; + /** + * 对话框类型 + */ + @DialogType.Type + int type = DialogType.DEFAULT; + + /** + * 文本框输入类型 + */ + int inputType = InputType.TYPE_CLASS_TEXT; + /** + * 对话框Title + */ + String title; + /** + * 显示内容文字颜色 + */ + int contentTextColor = Color.BLACK; + /** + * 显示内容文字大小 + */ + float contentTextSize = 16; + /** + * 对话框内容 + */ + String content; + /** + * 左按钮文字 + */ + String letButtonText; + /** + * 右按钮文字 + */ + String rightButtonText; + /** + * 左按钮事件 + */ + OnButtonClickListener letButtonListener; + /** + * 右按钮事件 + */ + OnButtonClickListener rightButtonListener; + + /** + * 左按钮文字颜色 + */ + int leftButtonTextColor = Color.GRAY; + /** + * 右按钮文字颜色 + */ + int rightButtonTextColor = Color.parseColor("#0099FF"); + /** + * 左按钮文字大小 + */ + float leftButtonTextSize = 18; + /** + * 右按钮文字大小 + */ + float rightButtonTextSize = 18; + + /** + * 底部弹出多条目内容 + */ + List bottomItems; + /** + * 底部多条目点击事件 + */ + OnItemClickListener itemClickListener; + /** + * 底部弹出对话框标题文字 + */ + String bottomTitleText; + /** + * 底部弹出对话框取消按钮文字 + */ + String bottomCancelText; + /** + * 底部弹出对话框取消按钮文字颜色 + */ + int bottomCancelTextColor = Color.GRAY; + /** + * 底部弹出对话框Item文字颜色 + */ + int bottomItemTextColor = Color.BLACK; + /** + * 底部弹出对话框取消按钮文字大小 + */ + float bottomCancelTextSize = 18; + /** + * 底部弹出对话框Item文字大小 + */ + float bottomItemTextSize = 16; + + /** + * 输入框默认文字 + */ + String inputDefaultText; + /** + * 输入框hint文字 + */ + String inputHintText; + /** + * 输入框文字颜色 + */ + int inputTextColor = Color.BLACK; + /** + * 输入框Hint文字颜色 + */ + int inputHintTextColor = Color.GRAY; + /** + * 输入框文字大小 + */ + float inputTextSize = 16; + /** + * 输入框输入监听 + */ + TextWatcher textWatcher; + /** + * 替换整个Dialog内容的View + */ + View allDialogView; + /** + * 替换中间部分View + */ + View contentView; + /** + * 替换底部弹出View + */ + View bottomView; + + /** + * 是否单按钮 + */ + boolean isSingleButton; + + /** + * 是否可以取消 + */ + boolean cancelable = true; + + /** + * 点击对话框周围是否可取消 + */ + boolean canceledOnTouchOutside = true; + + /** + * 对话框消失监听 + */ + DialogInterface.OnDismissListener dialogDismissListener; + + /** + * 初始化 + * + * @param context 上下文 + */ + public Builder(Context context) { + this.context = context; + } + /** + * 设置对话框类型 + * + * @param type DialogType + * @return Builder + */ + public Builder setDialogType(@DialogType.Type int type) { + this.type = type; + return this; + } - /** - * 给右边按钮设置文字 事件 - * - * @param text 文字 - * @param listener 事件 - * @return AppDialog - */ - public AppDialog setRightButton(String text, OnButtonClickListener listener) { - this.rightListener = listener; - if (!TextUtils.isEmpty(text)) { - btnRight.setText(text); + /** + * 设置文本框输入类型 + * + * @param inputType 输入类型 + * @return Builder + */ + public Builder setInputType(int inputType) { + this.inputType = inputType; + return this; } - return this; - } - /** - * 给右边按钮设置文字 事件 - * - * @param listener 事件 - * @return AppDialog - */ - public AppDialog setRightButton(OnButtonClickListener listener) { - return setRightButton(null, listener); - } + /** + * 设置对话框标题 + * + * @param title 标题 + * @return Builder + */ + public Builder setTitle(String title) { + this.title = title; + return this; + } - /** - * 给右边按钮设置文字 事件 - * - * @param text text - * @return AppDialog - */ - public AppDialog setRightButton(String text) { - return setRightButton(text, null); - } + /** + * 设置对话框内容 + * + * @param content 内容 + * @return Builder + */ + public Builder setContent(String content) { + this.content = content; + return this; + } - /** - * 左按钮文字颜色 - * - * @param color color - * @return AppDialog - */ - public AppDialog setLeftButtonTextColor(int color) { - btnLeft.setTextSize(mContext.getResources().getColor(color)); - return this; - } + /** + * 设置显示内容文字颜色 + * + * @param color 颜色 + * @return Builder + */ + public Builder setContentTextColor(@ColorInt int color) { + this.contentTextColor = color; + return this; + } - /** - * 右按钮文字大小 - * - * @param color color - * @return AppDialog - */ - public AppDialog setRightButtonTextColor(int color) { - btnRight.setTextSize(mContext.getResources().getColor(color)); - return this; - } + /** + * 设置显示内容文字大小 + * + * @param contentTextSize 大小 + * @return Builder + */ + public Builder setContentTextSize(float contentTextSize) { + this.contentTextSize = contentTextSize; + return this; + } + ////////////////////////////////////////左按钮设置//////////////////////////////////////////// + + /** + * 设置左按钮 + * + * @param text 显示文字 + * @return Builder + */ + public Builder setLeftButton(String text) { + return setLeftButton(text, null); + } - /** - * 单按钮文字大小 - * - * @param color color - * @return AppDialog - */ - public AppDialog setSingleButtonTextColor(int color) { - return setRightButtonTextColor(color); - } + /** + * 设置左按钮 + * + * @param listener 监听事件 + * @return Builder + */ + public Builder setLeftButton(OnButtonClickListener listener) { + return setLeftButton(null, listener); + } + /** + * 设置左按钮 + * + * @param text 显示文字 + * @param listener 监听事件 + * @return Builder + */ + public Builder setLeftButton(String text, OnButtonClickListener listener) { + this.letButtonText = text; + this.letButtonListener = listener; + return this; + } - /** - * 单按钮 - * - * @param text text - * @param listener listener - * @return AppDialog - */ - public AppDialog setSingleButton(String text, OnButtonClickListener listener) { - this.rightListener = listener; - if (!TextUtils.isEmpty(text)) { - btnRight.setText(text); - } - btnLeft.setVisibility(View.GONE); - btnLine.setVisibility(View.GONE); - btnRight.setBackgroundResource(R.drawable.corners_white_gray_selecter); - return this; - } + /** + * 设置左按钮文字颜色 + * + * @param color 颜色 + * @return Builder + */ + public Builder setLeftButtonTextColor(@ColorInt int color) { + this.leftButtonTextColor = color; + return this; + } - /** - * 单按钮 - * - * @param listener listener - * @return AppDialog - */ - public AppDialog setSingleButton(OnButtonClickListener listener) { - return setSingleButton(null, listener); - } + /** + * 设置左按钮文字大小 + * + * @param size 大小 + * @return Builder + */ + public Builder setLeftButtonTextSize(float size) { + this.leftButtonTextSize = size; + return this; + } - /** - * 单按钮 - * - * @param text listener - * @return AppDialog - */ - public AppDialog setSingleButton(String text) { - return setSingleButton(text, null); - } + ////////////////////////////////////////右按钮设置//////////////////////////////////////////// - /** - * 单按钮 - * - * @return AppDialog - */ - public AppDialog setSingleButton() { - return setSingleButton(null, null); - } + /** + * 设置右按钮 + * + * @param text 显示文字 + * @return Builder + */ + public Builder setRightButton(String text) { + return setRightButton(text, null); + } - /** - * 设置底部弹出对话框的条目 - * - * @param items 条目名称 - * @param listener listener - * @return AppDialog - */ - public AppDialog setBottomItems(List items, OnItemClickListener listener) { - this.itemClickListener = listener; - setItems(items); - return this; - } + /** + * 设置右按钮 + * + * @param listener 监听事件 + * @return Builder + */ + public Builder setRightButton(OnButtonClickListener listener) { + return setRightButton(null, listener); + } - /** - * 设置底部弹出对话框的取消文字 - * - * @param text text - * @return AppDialog - */ - public AppDialog setBottomCancelText(String text) { - if (!TextUtils.isEmpty(text)) { - tvCancel.setText(text); + /** + * 设置右按钮 + * + * @param text 显示文字 + * @param listener 监听事件 + * @return Builder + */ + public Builder setRightButton(String text, OnButtonClickListener listener) { + this.rightButtonText = text; + this.rightButtonListener = listener; + return this; } - return this; - } - /** - * 设置底部弹出对话框的取消文字颜色 - * - * @param color text - * @return AppDialog - */ - public AppDialog setBottomCancelTextColor(int color) { - tvCancel.setTextColor(mContext.getResources().getColor(color)); - return this; - } + /** + * 设置右按钮文字颜色 + * + * @param color 颜色 + * @return Builder + */ + public Builder setRightButtonTextColor(@ColorInt int color) { + this.rightButtonTextColor = color; + return this; + } + /** + * 设置右按钮文字大小 + * + * @param size 大小 + * @return Builder + */ + public Builder setRightButtonTextSize(float size) { + this.rightButtonTextSize = size; + return this; + } + ////////////////////////////////////////单按钮设置//////////////////////////////////////////// + + /** + * 单按钮设置 + * + * @param text 显示文字 + * @return Builder + */ + public Builder setSingleButton(String text) { + return setSingleButton(text, null); + } - /** - * 给整个Dialog添加View - * - * @param view view - * @return AppDialog - */ - public AppDialog addDialogView(View view) { - dialogLayout.removeAllViews(); - dialogLayout.addView(view); - return this; - } + /** + * 单按钮设置 + * + * @param listener 事件监听 + * @return Builder + */ + public Builder setSingleButton(OnButtonClickListener listener) { + return setSingleButton(null, listener); + } - /** - * 中间弹出对话框添加View - * - * @param view view - * @return AppDialog - */ - public AppDialog addContentView(View view) { - contentLayout.removeAllViews(); - contentLayout.addView(view); - return this; - } + /** + * 单按钮设置 + * + * @param text 显示文字 + * @param listener 事件监听 + * @return Builder + */ + public Builder setSingleButton(String text, OnButtonClickListener listener) { + this.isSingleButton = true; + return setRightButton(text, listener); + } - /** - * 底部弹出对话框添加View - * - * @param view view - * @param itemSize 条目数量 - * @return AppDialog - */ - public AppDialog addItemView(View view, int itemSize) { - llContext.removeAllViews(); - llContext.addView(view); - setItemScrollViewHeight(itemSize); - return this; - } + /** + * 设置单按钮文字颜色 + * + * @param color 颜色 + * @return Builder + */ + public Builder setSingleButtonTextColor(@ColorInt int color) { + return setRightButtonTextColor(color); + } + /** + * 设置单按钮文字大小 + * + * @param size 大小 + * @return Builder + */ + public Builder setSingleButtonTextSize(float size) { + return setRightButtonTextSize(size); + } - @OnClick({R2.id.btn_left, R2.id.btn_right, R2.id.minus, R2.id.plus, R2.id.tv_cancel}) - public void onClick(View v) { - if (v.getId() == R.id.btn_left) { - onButtonClick(leftListener); - } else if (v.getId() == R.id.btn_right) { - onButtonClick(rightListener); - } else if (v.getId() == R.id.minus) { - //减少 - int count = getCountEdtValue(); - if (getCountEdtValue() > minCount) { - edtCount.setText(String.valueOf(--count)); - } - judgeTheViews(count); - } else if (v.getId() == R.id.plus) { - //增加 - int count = getCountEdtValue(); - if (count < maxCount) { - edtCount.setText(String.valueOf(++count)); - } - judgeTheViews(count); - } else if (v.getId() == R.id.tv_cancel) { - dismiss(); + ////////////////////////////////////////多条目设置//////////////////////////////////////////// + + /** + * 当type = 底部弹出的时候,设置弹出条目及监听 + * + * @param items 弹出条目 + * @param listener 监听事件 + * @return Builder + */ + public Builder setBottomItems(List items, OnItemClickListener listener) { + this.bottomItems = items; + this.itemClickListener = listener; + return this; } - } - /** - * 加减模式下点击结束结算值 - */ - private void judgeTheViews(int count) { - if (count == minCount) { - ivMinus.setImageResource(R.drawable.input_minus_disabled); - } else { - ivMinus.setImageResource(R.drawable.input_minus_default); + /** + * 设置底部弹出对话框取消按钮文字 + * + * @param text 显示文字 + * @return Builder + */ + public Builder setBottomCancelText(String text) { + this.bottomCancelText = text; + return this; } - if (count == maxCount) { - ivPlus.setImageResource(R.drawable.input_add_disabled); - } else { - ivPlus.setImageResource(R.drawable.input_add_default); + + /** + * 设置底部弹出对话框标题文字 + * + * @param text 显示文字 + * @return Builder + */ + public Builder setBottomTitleText(String text) { + this.bottomTitleText = text; + return this; } - } - /** - * 给点击事件设置数据 - * - * @param listener listener - */ - private void onButtonClick(OnButtonClickListener listener) { + /** + * 设置底部弹出对话框取消按钮文字颜色 + * + * @param color 颜色 + * @return Builder + */ + public Builder setBottomCancelTextColor(@ColorInt int color) { + this.bottomCancelTextColor = color; + return this; + } - if (type == DialogType.COUNT) { - if (getCountEdtValue() >= minCount) { - if (listener != null) { - listener.onClick(String.valueOf(getCountEdtValue())); - } - } - KeyboardUtils.hideKeyboard(edtCount); - } else if (type == DialogType.INPUT) { - if (listener != null) { - listener.onClick(edtInput.getText().toString().trim()); - } - KeyboardUtils.hideKeyboard(edtInput); - } else { - if (listener != null) { - listener.onClick(tvContent.getText().toString()); - } + /** + * 设置底部弹出对话框Item文字颜色 + * + * @param color 颜色 + * @return Builder + */ + public Builder setBottomItemTextColor(@ColorInt int color) { + this.bottomItemTextColor = color; + return this; } - dismiss(); - } - /** - * 底部弹出对话框添加条目 - * - * @param items items - */ - private void setItems(List items) { - if (items != null && items.size() > 0) { - int size = items.size(); - setItemScrollViewHeight(size); - // 循环添加条目 - for (int i = 0; i <= size - 1; i++) { - View v = View.inflate(mContext, R.layout.layout_item_of_dialog_bottom_in, null); - TextView item = v.findViewById(R.id.tv_text); - item.setText(items.get(i)); - item.setTag(i); - item.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (itemClickListener != null) { - itemClickListener.onItemClick((int) v.getTag()); - } - dismiss(); - } - }); - llContext.addView(v); - } + /** + * 设置底部弹出对话框取消按钮文字大小 + * + * @param size 大小 + * @return Builder + */ + public Builder setBottomCancelTextSize(float size) { + this.bottomCancelTextSize = size; + return this; } - } - /** - * 设置底部弹出带条目的ScrollView的高度 - * - * @param size 条目数量 - */ - private void setItemScrollViewHeight(int size) { - // 添加条目过多的时候控制高度 - if (size >= 7) { - LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) sLayoutContent.getLayoutParams(); - params.height = PxUtils.getScreenHeight(mContext) / 2; - sLayoutContent.setLayoutParams(params); + /** + * 设置底部弹出对话框item文字大小 + * + * @param size 大小 + * @return Builder + */ + public Builder setBottomItemTextSize(float size) { + this.bottomItemTextSize = size; + return this; } - } - /** - * 得到 加减模式下 输入框的值 - * - * @return int - */ - private int getCountEdtValue() { - int count = 0; - String text = edtCount.getText().toString().trim(); - if (!TextUtils.isEmpty(text)) { - count = Integer.parseInt(text); + ////////////////////////////////////////输入框设置//////////////////////////////////////////// + + /** + * 设置输入框默认文字 + * + * @param text 默认文字 + * @return Builder + */ + public Builder setInputDefaultText(String text) { + this.inputDefaultText = text; + return this; } - return count; - } - /** - * 加减模式下输入框的监听 - */ - private TextWatcher textWatcher = new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { + /** + * 设置Hint文字 + * + * @param text Hint 文字 + * @return Builder + */ + public Builder setInputHintText(String text) { + this.inputHintText = text; + return this; + } + /** + * 设置输入框文字颜色 + * + * @param color 文字颜色 + * @return Builder + */ + public Builder setInputTextColor(@ColorInt int color) { + this.inputTextColor = color; + return this; } - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { + /** + * 设置输入框Hint文字颜色 + * + * @param color Hint文字颜色 + * @return Builder + */ + public Builder setInputHintTextColor(@ColorInt int color) { + this.inputHintTextColor = color; + return this; + } + /** + * 设置输入框文字大小 + * + * @param size 大小 + * @return Builder + */ + public Builder setInputTextSize(float size) { + this.inputTextSize = size; + return this; } - @Override - public void afterTextChanged(Editable s) { - if (!TextUtils.isEmpty(s.toString().trim())) { - edtCount.removeTextChangedListener(textWatcher); - int text = Integer.parseInt(s.toString()); - if (text < minCount) { - edtCount.setText(String.valueOf(minCount)); - } else if (text > maxCount) { - edtCount.setText(String.valueOf(maxCount)); - } - edtCount.setSelection(edtCount.getText().length()); - edtCount.addTextChangedListener(textWatcher); - judgeTheViews(Integer.parseInt(edtCount.getText().toString().trim())); - } else { - //删光了 减 按钮不可点击 - ivMinus.setImageResource(R.drawable.input_minus_disabled); - } + /** + * 设置输入框输入监听 + * + * @param textWatcher 监听 + * @return Builder + */ + public Builder setTextWatcher(TextWatcher textWatcher) { + this.textWatcher = textWatcher; + return this; + } + ////////////////////////////////////////其他设置//////////////////////////////////////////// + + /** + * 替换整个Dialog 内容 + * + * @param view view + * @return Builder + */ + public Builder addAllDialogView(View view) { + this.allDialogView = view; + this.type = DialogType.REPLACE_ALL; + return this; } - }; - /** - * 按钮被点击事件回调 - */ - public interface OnButtonClickListener { - void onClick(String val); - } + /** + * 替换中间部分View + * + * @param view view + * @return Builder + */ + public Builder addContentView(View view) { + this.contentView = view; + this.type = DialogType.REPLACE_CONTENT; + return this; + } - /** - * 底部弹出对话框条目被点击 - */ - public interface OnItemClickListener { - void onItemClick(int position); + /** + * 替换底部弹出的View + * + * @param view view + * @return Builder + */ + public Builder setBottomView(View view) { + this.bottomView = view; + this.type = DialogType.REPLACE_BOTTOM; + return this; + } + + /** + * 是否可取消 + * + * @param cancelable 可取消 + * @return Builder + */ + public Builder setCancelable(boolean cancelable) { + this.cancelable = cancelable; + return this; + } + + /** + * 点击对话框周围是否可取消 + * + * @param canceledOnTouchOutside 可取消 + * @return Builder + */ + public Builder setCanceledOnTouchOutside(boolean canceledOnTouchOutside) { + this.canceledOnTouchOutside = canceledOnTouchOutside; + return this; + } + + /** + * 对话框消失监听 + * + * @param dialogDismissListener 监听 + * @return Builder + */ + public Builder setDialogDismissListener(DialogInterface.OnDismissListener dialogDismissListener) { + this.dialogDismissListener = dialogDismissListener; + return this; + } + + /** + * 创建对话框 + * + * @return AppDialog + */ + public AppDialog create() { + return new AppDialog(this); + } } + } \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/widget/dialog/DateDialog.java b/common_base/src/main/java/com/wss/common/widget/dialog/DateDialog.java new file mode 100644 index 0000000..4587935 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/dialog/DateDialog.java @@ -0,0 +1,261 @@ +package com.wss.common.widget.dialog; + +import android.annotation.SuppressLint; +import android.app.Dialog; +import android.content.Context; +import android.view.Gravity; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; + +import com.bigkoo.pickerview.adapter.ArrayWheelAdapter; +import com.contrarywind.view.WheelView; +import com.wss.common.base.R; +import com.wss.common.base.R2; +import com.wss.common.utils.DateUtils; +import com.wss.common.utils.PxUtils; +import com.wss.common.utils.ValidUtils; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; + + +/** + * Describe:自定义选择日期对话框,适用于在PopupWindow之上弹出 + * Created by 吴天强 on 2019年7月27日18:13:09 + */ +@SuppressLint("DefaultLocale") +public class DateDialog { + + + private static final int DEFAULT_START_YEAR = 1900; + private static final int DEFAULT_END_YEAR = 2100; + + + @BindView(R2.id.wv_year) + WheelView wvYear; + + @BindView(R2.id.wv_month) + WheelView wvMonth; + + @BindView(R2.id.wv_day) + WheelView wvDay; + + private Dialog dialog; + private int startYear = DEFAULT_START_YEAR; + private int endYear = DEFAULT_END_YEAR; + private List yearList = new ArrayList<>(); + private List monthList = new ArrayList<>(); + private List dayList = new ArrayList<>(); + /** + * 当前年份下标 + */ + private int yearPosition = 0; + /** + * 当前月份下标 + */ + private int monthPosition = 0; + /** + * 当前日下标 + */ + private int dayPosition = 0; + /** + * 默认显示的日期 yyyy-MM-dd + */ + private String defaultDate = DateUtils.getCurrentDateStr(); + private OnDateSelectListener dateSelectListener; + /** + * 切换过日期 + */ + private boolean changeDay = false; + + public DateDialog(Context context) { + View dialogView = View.inflate(context, R.layout.dailog_date, null); + ButterKnife.bind(this, dialogView); + //底部弹出对话框 + dialog = new Dialog(context, R.style.ActionSheetDialogStyle); + dialogView.setMinimumWidth(PxUtils.getScreenWidth(context)); + dialog.setContentView(dialogView); + Window window = dialog.getWindow(); + if (window != null) { + window.getAttributes().gravity = Gravity.BOTTOM; + WindowManager.LayoutParams lp = window.getAttributes(); + lp.x = 0; + lp.y = 0; + window.setAttributes(lp); + } + } + + /** + * 日期选择回调 + */ + public DateDialog setOnDateSelectListener(OnDateSelectListener dateSelectListener) { + this.dateSelectListener = dateSelectListener; + return this; + } + + /** + * 设置开始 结束年份 + */ + public DateDialog setYearStartEnd(int startYear, int endYear) { + this.startYear = startYear; + this.endYear = endYear; + return this; + } + + /** + * 设置当前日期 格式yyyy-MM-dd + */ + public DateDialog setCurrentDate(String date) { + if (ValidUtils.isValid(date)) { + this.defaultDate = date; + } + return this; + } + + private void initView() { + initData(); + //设置"年"的显示数据 + wvYear.setAdapter(new ArrayWheelAdapter<>(yearList)); + // 初始化时显示的数据 + wvYear.setCurrentItem(yearPosition); + wvYear.setOnItemSelectedListener(index -> { + yearPosition = index; + initDayDate(); + }); + wvMonth.setAdapter(new ArrayWheelAdapter<>(monthList)); + wvMonth.setCurrentItem(monthPosition); + wvMonth.setOnItemSelectedListener(index -> { + monthPosition = index; + initDayDate(); + }); + initDayDate(); + } + + /** + * 初始化年份 月份数据 + */ + private void initData() { + yearList.clear(); + monthList.clear(); + String year = DateUtils.getFormatDate(defaultDate, "yyyy"); + String month = DateUtils.getFormatDate(defaultDate, "MM"); + int yearIndex = 0; + for (int i = startYear; i < endYear + 1; i++) { + yearList.add(String.format("%s年", i)); + if (i == Integer.parseInt(year)) { + yearPosition = yearIndex; + } + yearIndex++; + } + for (int i = 1; i < 12 + 1; i++) { + monthList.add(String.format("%02d月", i)); + if (i == Integer.parseInt(month)) { + monthPosition = i - 1; + } + } + } + + /** + * 初始化日数据 + */ + private void initDayDate() { + dayList.clear(); + int maxDay = 31; + String monthString = monthList.get(monthPosition); + switch (Integer.parseInt(monthString.substring(0, monthString.length() - 1))) { + case 2: + String yearString = yearList.get(yearPosition); + int year = Integer.parseInt(yearString.substring(0, yearString.length() - 1)); + maxDay = isLeapYear(year) ? 28 : 29; + break; + case 4: + case 5: + case 6: + case 8: + case 9: + case 11: + maxDay = 30; + break; + default: + break; + } + String day = DateUtils.getFormatDate(defaultDate, "dd"); + for (int i = 1; i < maxDay + 1; i++) { + dayList.add(String.format("%02d日", i)); + if (!changeDay && i == Integer.parseInt(day)) { + dayPosition = i - 1; + } + } + wvDay.setAdapter(new ArrayWheelAdapter<>(dayList)); + wvDay.setCurrentItem(dayPosition); + wvDay.setOnItemSelectedListener(index -> { + changeDay = true; + dayPosition = index; + }); + } + + /** + * 是否闰年 + * + * @param year 年份 + * @return boolean + */ + @Contract(pure = true) + private boolean isLeapYear(int year) { + return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; + } + + private boolean isShowing() { + return dialog != null && dialog.isShowing(); + } + + /** + * 显示对话框 + */ + public void show() { + if (!isShowing()) { + initView(); + dialog.show(); + } + } + + + @OnClick({R2.id.btnCancel, R2.id.btnSubmit}) + public void onViewClicked(@NotNull View view) { + if (view.getId() == R.id.btnSubmit) { + if (dateSelectListener != null) { + String yearString = yearList.get(yearPosition); + String monthString = monthList.get(monthPosition); + String dayString = dayList.get(dayPosition); + dateSelectListener.selectDate( + yearString.substring(0, yearString.length() - 1), + monthString.substring(0, monthString.length() - 1), + dayString.substring(0, dayString.length() - 1)); + } + + } + dialog.dismiss(); + } + + /** + * 日期选择监听器 + */ + public interface OnDateSelectListener { + /** + * 日期选择了 + * + * @param year 年 + * @param month 月 + * @param day 日 + */ + void selectDate(String year, String month, String day); + } +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/widget/dialog/DialogType.java b/common_base/src/main/java/com/wss/common/widget/dialog/DialogType.java index 05bbb0b..cd8de09 100644 --- a/common_base/src/main/java/com/wss/common/widget/dialog/DialogType.java +++ b/common_base/src/main/java/com/wss/common/widget/dialog/DialogType.java @@ -1,25 +1,48 @@ package com.wss.common.widget.dialog; -import android.support.annotation.IntDef; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import androidx.annotation.IntDef; + /** * Describe:对话框类型 * Created by 吴天强 on 2018/10/26. */ - public interface DialogType { - @IntDef({DEFAULT, INPUT, COUNT, NO_TITLE, BOTTOM_IN}) + @IntDef({DEFAULT, NO_TITLE, INPUT, BOTTOM_IN, REPLACE_ALL, REPLACE_CONTENT, REPLACE_BOTTOM}) @Retention(RetentionPolicy.SOURCE) @interface Type { } + /** + * 常规 + */ int DEFAULT = 0; - int INPUT = 1; - int COUNT = 2; - int NO_TITLE = 3; - int BOTTOM_IN = 4; + /** + * 没有Title + */ + int NO_TITLE = 1; + /** + * 带输入框 + */ + int INPUT = 2; + /** + * 底部进来 + */ + int BOTTOM_IN = 3; + /** + * 替换全部 + */ + int REPLACE_ALL = 4; + /** + * 替换content + */ + int REPLACE_CONTENT = 5; + /** + * 替换底部弹出 + */ + int REPLACE_BOTTOM = 6; } diff --git a/common_base/src/main/java/com/wss/common/widget/dialog/LoadingDialog.java b/common_base/src/main/java/com/wss/common/widget/dialog/LoadingDialog.java index f29a77b..0ed0be5 100644 --- a/common_base/src/main/java/com/wss/common/widget/dialog/LoadingDialog.java +++ b/common_base/src/main/java/com/wss/common/widget/dialog/LoadingDialog.java @@ -11,7 +11,7 @@ /** - * 等待对话框 + * 加载框 * Created by wtq on 2016/3/14. */ public class LoadingDialog { @@ -19,7 +19,7 @@ public class LoadingDialog { private LVCircularRing mLoadingView; private Dialog mLoadingDialog; private Context context; - private String msg = "加载中···"; + private String msg = "加载中…"; private boolean cancelable = true; private boolean isShow; @@ -29,6 +29,9 @@ public LoadingDialog(Context context) { /** * 设置提示信息 + * + * @param msg 提示信息 + * @return dialog */ public LoadingDialog setTitleText(String msg) { this.msg = msg; @@ -36,7 +39,10 @@ public LoadingDialog setTitleText(String msg) { } /** - * 返回键是否可用 + * 设置dialog是否可以取消 + * + * @param cancelable 是否可取消 + * @return dialog */ public LoadingDialog setCancelable(boolean cancelable) { this.cancelable = cancelable; diff --git a/common_base/src/main/java/com/wss/common/widget/dialog/OnButtonClickListener.java b/common_base/src/main/java/com/wss/common/widget/dialog/OnButtonClickListener.java new file mode 100644 index 0000000..4f30a70 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/dialog/OnButtonClickListener.java @@ -0,0 +1,15 @@ +package com.wss.common.widget.dialog; + +/** + * Describe:对话框按钮被点击事件 + * Created by 吴天强 on 2019/3/31. + */ +public interface OnButtonClickListener { + + /** + * 点击事件 + * + * @param val 值 + */ + void onClick(String val); +} diff --git a/common_base/src/main/java/com/wss/common/widget/dialog/OnItemClickListener.java b/common_base/src/main/java/com/wss/common/widget/dialog/OnItemClickListener.java new file mode 100644 index 0000000..7047091 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/dialog/OnItemClickListener.java @@ -0,0 +1,16 @@ +package com.wss.common.widget.dialog; + +/** + * Describe:多条目对话框Item被点击事件 + * Created by 吴天强 on 2019/3/31. + */ +public interface OnItemClickListener { + + /** + * 点击事件 + * + * @param position position + * @param val 值 + */ + void onItemClick(int position, String val); +} diff --git a/common_base/src/main/java/com/wss/common/widget/dialog/ProgressBarDialog.java b/common_base/src/main/java/com/wss/common/widget/dialog/ProgressBarDialog.java new file mode 100644 index 0000000..3126570 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/dialog/ProgressBarDialog.java @@ -0,0 +1,121 @@ +package com.wss.common.widget.dialog; + +import android.content.Context; +import android.view.View; +import android.widget.TextView; + +import com.wss.common.base.R; +import com.wss.common.utils.ValidUtils; +import com.wss.common.widget.NumberProgressBar; + +/** + * Describe:带进度条的加载框 + * Created by 吴天强 on 2021/5/10. + */ +public class ProgressBarDialog { + private AppDialog progressDialog; + private NumberProgressBar progressBar; + private Builder builder; + + private ProgressBarDialog() { + } + + private ProgressBarDialog(Builder builder) { + this.builder = builder; + } + + /** + * 设置进度 + * + * @param progress 进度值 + */ + public void setProgress(int progress) { + if (progressBar != null) { + progressBar.setProgress(progress); + } + } + + /** + * 显示加载库 + */ + public void show() { + View progressView = View.inflate(builder.context, R.layout.dialog_progressbar, null); + progressBar = progressView.findViewById(R.id.number_progress); + if (ValidUtils.isValid(builder.loadingText)) { + ((TextView) progressView.findViewById(R.id.tv_loading_text)).setText(builder.loadingText); + } + progressBar.setProgress(0); + progressDialog = new AppDialog.Builder(builder.context) + .addAllDialogView(progressView) + .setCancelable(builder.cancelable) + .create(); + progressDialog.show(); + } + + /** + * 关闭对话框 + */ + public void dismiss() { + if (progressDialog != null) { + progressDialog.dismiss(); + progressDialog = null; + } + } + + /** + * Describe:对话框建造者 + * Created by 吴天强 on 2018年9月27日 + */ + public static class Builder { + Context context; + /** + * 加载Text + */ + String loadingText; + /** + * 是否可以取消 + */ + boolean cancelable; + + /** + * 初始化 + * + * @param context 上下文 + */ + public Builder(Context context) { + this.context = context; + } + + /** + * 设置对话框内容 + * + * @param loadingText 加载文案 + * @return Builder + */ + public Builder setLoadingText(String loadingText) { + this.loadingText = loadingText; + return this; + } + + /** + * 是否可取消 + * + * @param cancelable 可取消 + * @return Builder + */ + public Builder setCancelable(boolean cancelable) { + this.cancelable = cancelable; + return this; + } + + + /** + * 创建对话框 + * + * @return AppDialog + */ + public ProgressBarDialog create() { + return new ProgressBarDialog(this); + } + } +} diff --git a/common_base/src/main/java/com/wss/common/widget/manager/ScrollSpeedLinearLayoutManger.java b/common_base/src/main/java/com/wss/common/widget/manager/ScrollSpeedLinearLayoutManger.java new file mode 100644 index 0000000..9dd5957 --- /dev/null +++ b/common_base/src/main/java/com/wss/common/widget/manager/ScrollSpeedLinearLayoutManger.java @@ -0,0 +1,79 @@ +package com.wss.common.widget.manager; + +import android.content.Context; +import android.graphics.PointF; +import android.util.DisplayMetrics; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.LinearSmoothScroller; +import androidx.recyclerview.widget.RecyclerView; + +/** + * Describe:带效果的RecyclerView LinearLayoutManager滚动指定位置 + * Created by 吴天强 on 2019/5/14. + */ +public class ScrollSpeedLinearLayoutManger extends LinearLayoutManager { + + /** + * 滚动速度因子 + */ + private float speed = 0.3f; + + public ScrollSpeedLinearLayoutManger(Context context) { + super(context, VERTICAL, false); + } + + public ScrollSpeedLinearLayoutManger(Context context, int orientation, boolean reverseLayout) { + super(context, orientation, reverseLayout); + } + + /** + * 设置滚动速度因子 越大越快 越小越慢 取值在0.1~1.0之间即可 + * + * @param speed 速度因子 + */ + public ScrollSpeedLinearLayoutManger setSpeed(float speed) { + if (speed < 0 && speed < 1) { + this.speed = speed; + } + return this; + } + + @Override + public void smoothScrollToPosition(@NonNull RecyclerView recyclerView, RecyclerView.State state, int position) { + RecyclerView.SmoothScroller smoothScroller = new CenterSmoothScroller(recyclerView.getContext()); + smoothScroller.setTargetPosition(position); + startSmoothScroll(smoothScroller); + } + + private class CenterSmoothScroller extends LinearSmoothScroller { + + CenterSmoothScroller(Context context) { + super(context); + } + + @Nullable + @Override + public PointF computeScrollVectorForPosition(int targetPosition) { + return ScrollSpeedLinearLayoutManger.this.computeScrollVectorForPosition(targetPosition); + } + + @Override + public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) { + return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2); + } + + @Override + protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) { + return speed; + } + + @Override + protected int getVerticalSnapPreference() { + return SNAP_TO_START; + } + } + +} \ No newline at end of file diff --git a/common_base/src/main/java/com/wss/common/widget/pulltorefresh/IRefreshView.java b/common_base/src/main/java/com/wss/common/widget/pulltorefresh/IRefreshView.java deleted file mode 100644 index 5a7367e..0000000 --- a/common_base/src/main/java/com/wss/common/widget/pulltorefresh/IRefreshView.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.wss.common.widget.pulltorefresh; - -import android.view.View; - -/** - * 刷新view - */ -public interface IRefreshView { - - /** - * 开始下拉 - */ - void begin(); - - /** - * 回调的精度,单位为px - * - * @param progress 当前高度 - * @param all 总高度 为默认高度的2倍 - */ - void progress(float progress, float all); - - void finishing(float progress, float all); - - /** - * 下拉完毕 - */ - void loading(); - - /** - * 看不见的状态 - */ - void normal(); - - /** - * 返回当前视图 - */ - View getView(); - - void setType(boolean refresh); - -} diff --git a/common_base/src/main/java/com/wss/common/widget/pulltorefresh/OnPullRefreshListener.java b/common_base/src/main/java/com/wss/common/widget/pulltorefresh/OnPullRefreshListener.java deleted file mode 100644 index 932f21d..0000000 --- a/common_base/src/main/java/com/wss/common/widget/pulltorefresh/OnPullRefreshListener.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.wss.common.widget.pulltorefresh; - - -/** - * 刷新 加载监听 - */ -public interface OnPullRefreshListener { - - - /** - * 刷新 - */ - void onRefresh(); - - /** - * 加载更多 - */ - void onLoadMore(); -} diff --git a/common_base/src/main/java/com/wss/common/widget/pulltorefresh/PullToRefreshLayout.java b/common_base/src/main/java/com/wss/common/widget/pulltorefresh/PullToRefreshLayout.java deleted file mode 100644 index 079311b..0000000 --- a/common_base/src/main/java/com/wss/common/widget/pulltorefresh/PullToRefreshLayout.java +++ /dev/null @@ -1,586 +0,0 @@ -package com.wss.common.widget.pulltorefresh; - -import android.animation.ValueAnimator; -import android.content.Context; -import android.content.res.TypedArray; -import android.support.v4.view.ViewCompat; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.widget.FrameLayout; - -import com.wss.common.base.R; -import com.wss.common.utils.PxUtils; - - -/** - * 下拉刷新 上拉加载更多控件 - */ -public class PullToRefreshLayout extends FrameLayout { - - private IRefreshView mHeaderView; - private IRefreshView mFooterView; - private View mChildView; - - private static final long ANIM_TIME = 300; - private static int HEAD_HEIGHT = 60; - private static int FOOT_HEIGHT = 60; - - private static int head_height; - private static int head_height_2; - private static int foot_height; - private static int foot_height_2; - - private float mTouchY; - private float mCurrentY; - - private boolean canLoadMore = true; - private boolean canRefresh = true; - private boolean isRefresh; - private boolean isLoadMore; - - //滑动的最小距离 - private int mTouchSlope; - - private OnPullRefreshListener refreshListener; - - - private View loadingView, errorView, emptyView; - private int loading = R.layout.layout_loading, empty = R.layout.layout_empty, error = R.layout.layout_error; - - public void setOnPullRefreshListener(OnPullRefreshListener refreshListener) { - this.refreshListener = refreshListener; - } - - public PullToRefreshLayout(Context context) { - this(context, null); - } - - public PullToRefreshLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public PullToRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PullToRefreshLayout, defStyleAttr, 0); - error = a.getResourceId(R.styleable.PullToRefreshLayout_view_error, error); - loading = a.getResourceId(R.styleable.PullToRefreshLayout_view_loading, loading); - empty = a.getResourceId(R.styleable.PullToRefreshLayout_view_empty, empty); - a.recycle(); - init(); - } - - private void cal() { - head_height = PxUtils.dp2px(getContext(), HEAD_HEIGHT); - foot_height = PxUtils.dp2px(getContext(), FOOT_HEIGHT); - head_height_2 = PxUtils.dp2px(getContext(), HEAD_HEIGHT * 2); - foot_height_2 = PxUtils.dp2px(getContext(), FOOT_HEIGHT * 2); - - mTouchSlope = ViewConfiguration.get(getContext()).getScaledTouchSlop(); - } - - private void init() { - cal(); - int count = getChildCount(); - if (count != 1) { - new IllegalArgumentException("child only can be one"); - } - - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mChildView = getChildAt(0); - addHeadView(); - addFooterView(); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - - } - - private void addHeadView() { - if (mHeaderView == null) { - mHeaderView = new RefreshView(getContext()); - } else { - removeView(mHeaderView.getView()); - } - LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0); - mHeaderView.getView().setLayoutParams(layoutParams); - if (mHeaderView.getView().getParent() != null) - ((ViewGroup) mHeaderView.getView().getParent()).removeAllViews(); - addView(mHeaderView.getView(), 0); - } - - private void addFooterView() { - if (mFooterView == null) { - mFooterView = new RefreshView(getContext()); - mFooterView.setType(false); - } else { - removeView(mFooterView.getView()); - } - LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0); - layoutParams.gravity = Gravity.BOTTOM; - mFooterView.getView().setLayoutParams(layoutParams); - if (mFooterView.getView().getParent() != null) - ((ViewGroup) mFooterView.getView().getParent()).removeAllViews(); - addView(mFooterView.getView()); - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - if (!canLoadMore && !canRefresh) return super.onInterceptTouchEvent(ev); - switch (ev.getAction()) { - case MotionEvent.ACTION_DOWN: - mTouchY = ev.getY(); - mCurrentY = mTouchY; - break; - case MotionEvent.ACTION_MOVE: - float currentY = ev.getY(); - float dy = currentY - mCurrentY; - if (canRefresh) { - boolean canChildScrollUp = canChildScrollUp(); - if (dy > mTouchSlope && !canChildScrollUp) { - mHeaderView.begin(); - return true; - } - } - if (canLoadMore) { - boolean canChildScrollDown = canChildScrollDown(); - if (dy < -mTouchSlope && !canChildScrollDown) { - mFooterView.begin(); - return true; - } - } - } - return super.onInterceptTouchEvent(ev); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (isRefresh || isLoadMore) return true; - switch (event.getAction()) { - case MotionEvent.ACTION_MOVE: - mCurrentY = event.getY(); - float dura = (mCurrentY - mTouchY) / 3.0f; - if (dura > 0 && canRefresh) { - dura = Math.min(head_height_2, dura); - dura = Math.max(0, dura); - mHeaderView.getView().getLayoutParams().height = (int) dura; - ViewCompat.setTranslationY(mChildView, dura); - requestLayout(); - mHeaderView.progress(dura, head_height); - } else { - if (canLoadMore) { - dura = Math.min(foot_height_2, Math.abs(dura)); - dura = Math.max(0, Math.abs(dura)); - mFooterView.getView().getLayoutParams().height = (int) dura; - ViewCompat.setTranslationY(mChildView, -dura); - requestLayout(); - mFooterView.progress(dura, foot_height); - } - } - return true; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - float currentY = event.getY(); - final int dy1 = (int) (currentY - mTouchY) / 3; - if (dy1 > 0 && canRefresh) { - if (dy1 >= head_height) { - createAnimatorTranslationY(State.REFRESH, - dy1 > head_height_2 ? head_height_2 : dy1, head_height, - new CallBack() { - @Override - public void onSuccess() { - isRefresh = true; - if (refreshListener != null) { - refreshListener.onRefresh(); - } - mHeaderView.loading(); - } - }); - } else if (dy1 > 0 && dy1 < head_height) { - setFinish(dy1, State.REFRESH); - mHeaderView.normal(); - } - } else { - if (canLoadMore) { - if (Math.abs(dy1) >= foot_height) { - createAnimatorTranslationY(State.LOAD_MORE, Math.abs(dy1) > foot_height_2 ? foot_height_2 : Math.abs(dy1), foot_height, new CallBack() { - @Override - public void onSuccess() { - isLoadMore = true; - if (refreshListener != null) { - refreshListener.onLoadMore(); - } - mFooterView.loading(); - } - }); - } else { - setFinish(Math.abs(dy1), State.LOAD_MORE); - mFooterView.normal(); - } - } - } - break; - } - return super.onTouchEvent(event); - } - - private boolean canChildScrollDown() { - return mChildView != null && ViewCompat.canScrollVertically(mChildView, 1); - } - - private boolean canChildScrollUp() { - return mChildView != null && ViewCompat.canScrollVertically(mChildView, -1); - } - - /** - * 创建动画 - */ - public void createAnimatorTranslationY(@State.REFRESH_STATE final int state, final int start, - final int purpose, final CallBack callBack) { - final ValueAnimator anim; - anim = ValueAnimator.ofInt(start, purpose); - anim.setDuration(ANIM_TIME); - anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator valueAnimator) { - int value = (int) valueAnimator.getAnimatedValue(); - if (state == State.REFRESH) { - mHeaderView.getView().getLayoutParams().height = value; - ViewCompat.setTranslationY(mChildView, value); - if (purpose == 0) { //代表结束加载 - mHeaderView.finishing(value, head_height_2); - } else { - mHeaderView.progress(value, head_height); - } - } else { - mFooterView.getView().getLayoutParams().height = value; - ViewCompat.setTranslationY(mChildView, -value); - if (purpose == 0) { //代表结束加载 - mFooterView.finishing(value, head_height_2); - } else { - mFooterView.progress(value, foot_height); - } - } - if (value == purpose) { - if (callBack != null) - callBack.onSuccess(); - } - requestLayout(); - - - } - - }); - anim.start(); - } - - /** - * 结束下拉刷新 - */ - private void setFinish(int height, @State.REFRESH_STATE final int state) { - createAnimatorTranslationY(state, height, 0, new CallBack() { - @Override - public void onSuccess() { - if (state == State.REFRESH) { - isRefresh = false; - mHeaderView.normal(); - - } else { - isLoadMore = false; - mFooterView.normal(); - } - } - }); - } - - private void setFinish(@State.REFRESH_STATE int state) { - if (state == State.REFRESH) { - if (mHeaderView != null && mHeaderView.getView().getLayoutParams().height > 0 && isRefresh) { - setFinish(head_height, state); - } - } else { - if (mFooterView != null && mFooterView.getView().getLayoutParams().height > 0 && isLoadMore) { - setFinish(foot_height, state); - } - } - } - - public interface CallBack { - void onSuccess(); - } - - private void showLoadingView() { - if (loadingView == null) { - loadingView = LayoutInflater.from(getContext()).inflate(loading, null); - LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT); - addView(loadingView, layoutParams); - } else { - loadingView.setVisibility(VISIBLE); - } - } - - private void showEmptyView() { - if (emptyView == null) { - emptyView = LayoutInflater.from(getContext()).inflate(empty, null); - LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT); - addView(emptyView, layoutParams); - } else { - emptyView.setVisibility(VISIBLE); - } - } - - private void showErrorView() { - if (errorView == null) { - errorView = LayoutInflater.from(getContext()).inflate(error, null); - LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT); - addView(errorView, layoutParams); - } else { - errorView.setVisibility(VISIBLE); - } - } - - private void hideView(View view) { - if (view != null) - view.setVisibility(GONE); - } - - private void switchView(int status) { - switch (status) { - case ViewStatus.CONTENT_STATUS: - hideView(loadingView); - hideView(emptyView); - hideView(errorView); - - mChildView.setVisibility(VISIBLE); - break; - case ViewStatus.LOADING_STATUS: - hideView(mChildView); - hideView(emptyView); - hideView(errorView); - - showLoadingView(); - break; - case ViewStatus.EMPTY_STATUS: - hideView(mChildView); - hideView(loadingView); - hideView(errorView); - - showEmptyView(); - break; - case ViewStatus.ERROR_STATUS: - hideView(mChildView); - hideView(loadingView); - hideView(emptyView); - - showErrorView(); - break; - default: - hideView(loadingView); - hideView(emptyView); - hideView(errorView); - - mChildView.setVisibility(VISIBLE); - break; - } - } - - /** - * 设置展示view (error,empty,loading) - */ - public void showView(@ViewStatus.VIEW_STATUS int status) { - switchView(status); - } - - /** - * 获取view (error,empty,loading) - */ - public View getView(@ViewStatus.VIEW_STATUS int status) { - switch (status) { - case ViewStatus.EMPTY_STATUS: - return emptyView; - case ViewStatus.LOADING_STATUS: - return loadingView; - case ViewStatus.ERROR_STATUS: - return errorView; - case ViewStatus.CONTENT_STATUS: - return mChildView; - } - return null; - } - - public void autoRefresh() { - createAnimatorTranslationY(State.REFRESH, - 0, head_height, - new CallBack() { - @Override - public void onSuccess() { - isRefresh = true; - if (refreshListener != null) { - refreshListener.onRefresh(); - } - mHeaderView.loading(); - } - }); - } - - /** - * 结束刷新 - */ - public void finishRefresh() { - setFinish(State.REFRESH); - } - - /** - * 结束加载更多 - */ - public void finishLoadMore() { - setFinish(State.LOAD_MORE); - } - - /** - * 设置是否启用加载更多 - */ - public void setCanLoadMore(boolean canLoadMore) { - this.canLoadMore = canLoadMore; - } - - /** - * 设置是否启用下拉刷新 - */ - public void setCanRefresh(boolean canRefresh) { - this.canRefresh = canRefresh; - } - - /** - * 设置是下拉刷新头部 - * - * @param mHeaderView 需实现 HeadView 接口 - */ - public void setHeaderView(IRefreshView mHeaderView) { - this.mHeaderView = mHeaderView; - addHeadView(); - } - - /** - * 设置是下拉刷新尾部 - * - * @param mFooterView 需实现 IRefreshView 接口 - */ - public void setFooterView(IRefreshView mFooterView) { - this.mFooterView = mFooterView; - addFooterView(); - } - - - /** - * 设置刷新控件的高度 - * - * @param dp 单位为dp - */ - public void setHeadHeight(int dp) { - head_height = PxUtils.dp2px(getContext(), dp); - } - - /** - * 设置加载更多控件的高度 - * - * @param dp 单位为dp - */ - public void setFootHeight(int dp) { - foot_height = PxUtils.dp2px(getContext(), dp); - } - - /** - * 同时设置加载更多控件和刷新控件的高度 - * - * @param dp 单位为dp - */ - public void setAllHeight(int dp) { - head_height = PxUtils.dp2px(getContext(), dp); - foot_height = PxUtils.dp2px(getContext(), dp); - } - - /** - * 同时设置加载更多控件和刷新控件的高度 - * - * @param refresh 刷新控件的高度 单位为dp - * @param loadMore 加载控件的高度 单位为dp - */ - public void setAllHeight(int refresh, int loadMore) { - head_height = PxUtils.dp2px(getContext(), refresh); - foot_height = PxUtils.dp2px(getContext(), loadMore); - } - - /** - * 设置刷新控件的下拉的最大高度 且必须大于本身控件的高度 最佳为2倍 - * - * @param dp 单位为dp - */ - public void setMaxHeadHeight(int dp) { - if (head_height >= PxUtils.dp2px(getContext(), dp)) { - return; - } - head_height_2 = PxUtils.dp2px(getContext(), dp); - } - - /** - * 设置加载更多控件的上拉的最大高度 且必须大于本身控件的高度 最佳为2倍 - * - * @param dp 单位为dp - */ - public void setMaxFootHeight(int dp) { - if (foot_height >= PxUtils.dp2px(getContext(), dp)) { - return; - } - foot_height_2 = PxUtils.dp2px(getContext(), dp); - } - - /** - * 同时设置加载更多控件和刷新控件的最大高度 且必须大于本身控件的高度 最佳为2倍 - * - * @param dp 单位为dp - */ - public void setAllMaxHeight(int dp) { - if (head_height >= PxUtils.dp2px(getContext(), dp)) { - return; - } - if (foot_height >= PxUtils.dp2px(getContext(), dp)) { - return; - } - head_height_2 = PxUtils.dp2px(getContext(), dp); - foot_height_2 = PxUtils.dp2px(getContext(), dp); - } - - /** - * 同时设置加载更多控件和刷新控件的最大高度 且必须大于本身控件的高度 最佳为2倍 - * - * @param refresh 刷新控件下拉的最大高度 单位为dp - * @param loadMore 加载控件上拉的最大高度 单位为dp - */ - public void setAllMaxHeight(int refresh, int loadMore) { - if (head_height >= PxUtils.dp2px(getContext(), refresh)) { - return; - } - if (foot_height >= PxUtils.dp2px(getContext(), loadMore)) { - return; - } - head_height_2 = PxUtils.dp2px(getContext(), refresh); - foot_height_2 = PxUtils.dp2px(getContext(), loadMore); - } - - -} diff --git a/common_base/src/main/java/com/wss/common/widget/pulltorefresh/RefreshView.java b/common_base/src/main/java/com/wss/common/widget/pulltorefresh/RefreshView.java deleted file mode 100644 index 2c340e3..0000000 --- a/common_base/src/main/java/com/wss/common/widget/pulltorefresh/RefreshView.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.wss.common.widget.pulltorefresh; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.TextView; - -import com.wss.common.base.R; -import com.wss.common.widget.LVCircularRing; - - -/** - * 刷新控件 - */ -public class RefreshView extends FrameLayout implements IRefreshView { - - private TextView tv; - private ImageView arrow; - // private ProgressBar progressBar; - private LVCircularRing lvCircularRing; - private boolean refresh = false;//是否是刷新 - - public RefreshView(Context context) { - this(context, null); - } - - public RefreshView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public RefreshView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(context); - } - - @Override - public void setType(boolean refresh) { - this.refresh = refresh; - } - - - private void init(Context context) { - View childView = View.inflate(context, R.layout.layout_refresh_view, null); - addView(childView); - tv = childView.findViewById(R.id.header_tv); - arrow = childView.findViewById(R.id.header_arrow); - lvCircularRing = childView.findViewById(R.id.header_progress); - } - - @Override - public void begin() { - - } - - @Override - public void progress(float progress, float all) { - float s = progress / all; - if (s >= 0.9f) { - arrow.setRotation(180); - } else { - arrow.setRotation(0); - } - if (progress >= all - 10) { - tv.setText(refresh ? R.string.pull_loose_refreshing : R.string.pull_loose_load_more); - } else { - tv.setText(refresh ? R.string.pull_down_refresh : R.string.pull_up_loading); - } - } - - @Override - public void finishing(float progress, float all) { - lvCircularRing.stopAnim(); - } - - @Override - public void loading() { - arrow.setVisibility(GONE); - lvCircularRing.setVisibility(VISIBLE); - lvCircularRing.startAnim(); - tv.setText(R.string.loading); - } - - @Override - public void normal() { - arrow.setVisibility(VISIBLE); - lvCircularRing.setVisibility(GONE); - lvCircularRing.stopAnim(); - tv.setText(refresh ? R.string.pull_down_refresh : R.string.pull_up_loading); - } - - @Override - public View getView() { - return this; - } -} diff --git a/common_base/src/main/java/com/wss/common/widget/pulltorefresh/State.java b/common_base/src/main/java/com/wss/common/widget/pulltorefresh/State.java deleted file mode 100644 index a7d0efb..0000000 --- a/common_base/src/main/java/com/wss/common/widget/pulltorefresh/State.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.wss.common.widget.pulltorefresh; - -import android.support.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - - -/** - * 刷新状态 - */ -interface State { - - - @IntDef({REFRESH, LOAD_MORE}) - @Retention(RetentionPolicy.SOURCE) - @interface REFRESH_STATE { - } - - int REFRESH = 10; - int LOAD_MORE = 11; -} diff --git a/common_base/src/main/java/com/wss/common/widget/pulltorefresh/ViewStatus.java b/common_base/src/main/java/com/wss/common/widget/pulltorefresh/ViewStatus.java deleted file mode 100644 index 8dc43e6..0000000 --- a/common_base/src/main/java/com/wss/common/widget/pulltorefresh/ViewStatus.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.wss.common.widget.pulltorefresh; - -import android.support.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - - -/** - * 加载状态 - */ -interface ViewStatus { - - @IntDef({CONTENT_STATUS, LOADING_STATUS, EMPTY_STATUS, ERROR_STATUS}) - @Retention(RetentionPolicy.SOURCE) - @interface VIEW_STATUS { - - } - - int CONTENT_STATUS = 0; - int LOADING_STATUS = 1; - int EMPTY_STATUS = 2; - int ERROR_STATUS = 3; - -} diff --git a/common_base/src/main/res/anim/anim_right_in.xml b/common_base/src/main/res/anim/anim_right_in.xml new file mode 100644 index 0000000..b0d312f --- /dev/null +++ b/common_base/src/main/res/anim/anim_right_in.xml @@ -0,0 +1,5 @@ + + diff --git a/common_base/src/main/res/anim/anim_right_out.xml b/common_base/src/main/res/anim/anim_right_out.xml new file mode 100644 index 0000000..1ee5add --- /dev/null +++ b/common_base/src/main/res/anim/anim_right_out.xml @@ -0,0 +1,5 @@ + + diff --git a/common_base/src/main/res/color/selector_text_color.xml b/common_base/src/main/res/color/selector_text_color.xml new file mode 100644 index 0000000..4f9bfb5 --- /dev/null +++ b/common_base/src/main/res/color/selector_text_color.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/common_base/src/main/res/color/tab_text_color.xml b/common_base/src/main/res/color/tab_text_color.xml new file mode 100644 index 0000000..8cc3e47 --- /dev/null +++ b/common_base/src/main/res/color/tab_text_color.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/common_base/src/main/res/drawable-xhdpi/ic_back.png b/common_base/src/main/res/drawable-xhdpi/ic_back.png new file mode 100644 index 0000000..e395a62 Binary files /dev/null and b/common_base/src/main/res/drawable-xhdpi/ic_back.png differ diff --git a/common_base/src/main/res/drawable-xhdpi/input_add_default.png b/common_base/src/main/res/drawable-xhdpi/input_add_default.png new file mode 100644 index 0000000..b2e5657 Binary files /dev/null and b/common_base/src/main/res/drawable-xhdpi/input_add_default.png differ diff --git a/common_base/src/main/res/drawable-xhdpi/input_add_disabled.png b/common_base/src/main/res/drawable-xhdpi/input_add_disabled.png new file mode 100644 index 0000000..dd67fb7 Binary files /dev/null and b/common_base/src/main/res/drawable-xhdpi/input_add_disabled.png differ diff --git a/common_base/src/main/res/drawable-xhdpi/input_minus_default.png b/common_base/src/main/res/drawable-xhdpi/input_minus_default.png new file mode 100644 index 0000000..753174d Binary files /dev/null and b/common_base/src/main/res/drawable-xhdpi/input_minus_default.png differ diff --git a/common_base/src/main/res/drawable-xhdpi/input_minus_disabled.png b/common_base/src/main/res/drawable-xhdpi/input_minus_disabled.png new file mode 100644 index 0000000..7b18444 Binary files /dev/null and b/common_base/src/main/res/drawable-xhdpi/input_minus_disabled.png differ diff --git a/common_base/src/main/res/drawable-xhdpi/popup_icon_close.png b/common_base/src/main/res/drawable-xhdpi/popup_icon_close.png new file mode 100644 index 0000000..220beb4 Binary files /dev/null and b/common_base/src/main/res/drawable-xhdpi/popup_icon_close.png differ diff --git a/common_base/src/main/res/drawable-xxhdpi/go_top.png b/common_base/src/main/res/drawable-xxhdpi/go_top.png new file mode 100644 index 0000000..4d8e885 Binary files /dev/null and b/common_base/src/main/res/drawable-xxhdpi/go_top.png differ diff --git a/common_base/src/main/res/drawable-xxhdpi/header_load_default.png b/common_base/src/main/res/drawable-xxhdpi/header_load_default.png new file mode 100644 index 0000000..26c21f0 Binary files /dev/null and b/common_base/src/main/res/drawable-xxhdpi/header_load_default.png differ diff --git a/common_base/src/main/res/drawable-xxhdpi/ic_back.png b/common_base/src/main/res/drawable-xxhdpi/ic_back.png index e395a62..048fb39 100644 Binary files a/common_base/src/main/res/drawable-xxhdpi/ic_back.png and b/common_base/src/main/res/drawable-xxhdpi/ic_back.png differ diff --git a/common_base/src/main/res/drawable-xxhdpi/ic_back_black.png b/common_base/src/main/res/drawable-xxhdpi/ic_back_black.png new file mode 100644 index 0000000..089b248 Binary files /dev/null and b/common_base/src/main/res/drawable-xxhdpi/ic_back_black.png differ diff --git a/common_base/src/main/res/drawable-xxhdpi/ic_clear_white.png b/common_base/src/main/res/drawable-xxhdpi/ic_clear_white.png new file mode 100644 index 0000000..e80681a Binary files /dev/null and b/common_base/src/main/res/drawable-xxhdpi/ic_clear_white.png differ diff --git a/common_base/src/main/res/drawable-xxhdpi/ic_delete_white.png b/common_base/src/main/res/drawable-xxhdpi/ic_delete_white.png new file mode 100644 index 0000000..e4ea52e Binary files /dev/null and b/common_base/src/main/res/drawable-xxhdpi/ic_delete_white.png differ diff --git a/common_base/src/main/res/drawable-xxhdpi/ic_file_download_white.png b/common_base/src/main/res/drawable-xxhdpi/ic_file_download_white.png new file mode 100644 index 0000000..e089466 Binary files /dev/null and b/common_base/src/main/res/drawable-xxhdpi/ic_file_download_white.png differ diff --git a/common_base/src/main/res/drawable-xxhdpi/ic_loading_image.png b/common_base/src/main/res/drawable-xxhdpi/ic_loading_image.png new file mode 100644 index 0000000..ed9e773 Binary files /dev/null and b/common_base/src/main/res/drawable-xxhdpi/ic_loading_image.png differ diff --git a/common_base/src/main/res/drawable-xxhdpi/icon_back.png b/common_base/src/main/res/drawable-xxhdpi/icon_back.png new file mode 100644 index 0000000..e395a62 Binary files /dev/null and b/common_base/src/main/res/drawable-xxhdpi/icon_back.png differ diff --git a/common_base/src/main/res/drawable-xxhdpi/icon_collection.png b/common_base/src/main/res/drawable-xxhdpi/icon_collection.png new file mode 100644 index 0000000..9f40077 Binary files /dev/null and b/common_base/src/main/res/drawable-xxhdpi/icon_collection.png differ diff --git a/common_base/src/main/res/drawable-xxhdpi/icon_collectioned.png b/common_base/src/main/res/drawable-xxhdpi/icon_collectioned.png new file mode 100644 index 0000000..987255e Binary files /dev/null and b/common_base/src/main/res/drawable-xxhdpi/icon_collectioned.png differ diff --git a/common_base/src/main/res/drawable-xxhdpi/icon_refresh.png b/common_base/src/main/res/drawable-xxhdpi/icon_refresh.png new file mode 100644 index 0000000..0db589f Binary files /dev/null and b/common_base/src/main/res/drawable-xxhdpi/icon_refresh.png differ diff --git a/common_base/src/main/res/drawable-xxhdpi/input_add_default.png b/common_base/src/main/res/drawable-xxhdpi/input_add_default.png index b2e5657..34fa146 100644 Binary files a/common_base/src/main/res/drawable-xxhdpi/input_add_default.png and b/common_base/src/main/res/drawable-xxhdpi/input_add_default.png differ diff --git a/common_base/src/main/res/drawable-xxhdpi/input_add_disabled.png b/common_base/src/main/res/drawable-xxhdpi/input_add_disabled.png index dd67fb7..cee60c9 100644 Binary files a/common_base/src/main/res/drawable-xxhdpi/input_add_disabled.png and b/common_base/src/main/res/drawable-xxhdpi/input_add_disabled.png differ diff --git a/common_base/src/main/res/drawable-xxhdpi/input_minus_default.png b/common_base/src/main/res/drawable-xxhdpi/input_minus_default.png index 753174d..1f8d100 100644 Binary files a/common_base/src/main/res/drawable-xxhdpi/input_minus_default.png and b/common_base/src/main/res/drawable-xxhdpi/input_minus_default.png differ diff --git a/common_base/src/main/res/drawable-xxhdpi/input_minus_disabled.png b/common_base/src/main/res/drawable-xxhdpi/input_minus_disabled.png index 7b18444..525e156 100644 Binary files a/common_base/src/main/res/drawable-xxhdpi/input_minus_disabled.png and b/common_base/src/main/res/drawable-xxhdpi/input_minus_disabled.png differ diff --git a/common_base/src/main/res/drawable-xxhdpi/list_checkbox_default.png b/common_base/src/main/res/drawable-xxhdpi/list_checkbox_default.png new file mode 100644 index 0000000..954d518 Binary files /dev/null and b/common_base/src/main/res/drawable-xxhdpi/list_checkbox_default.png differ diff --git a/common_base/src/main/res/drawable-xxhdpi/list_checkbox_select.png b/common_base/src/main/res/drawable-xxhdpi/list_checkbox_select.png new file mode 100644 index 0000000..4f5c8c4 Binary files /dev/null and b/common_base/src/main/res/drawable-xxhdpi/list_checkbox_select.png differ diff --git a/common_base/src/main/res/drawable-xxhdpi/popup_icon_close.png b/common_base/src/main/res/drawable-xxhdpi/popup_icon_close.png new file mode 100644 index 0000000..1dc435b Binary files /dev/null and b/common_base/src/main/res/drawable-xxhdpi/popup_icon_close.png differ diff --git a/common_base/src/main/res/drawable-xxhdpi/update_bg.png b/common_base/src/main/res/drawable-xxhdpi/update_bg.png new file mode 100644 index 0000000..15a2873 Binary files /dev/null and b/common_base/src/main/res/drawable-xxhdpi/update_bg.png differ diff --git a/common_base/src/main/res/drawable-xxxhdpi/ic_back.png b/common_base/src/main/res/drawable-xxxhdpi/ic_back.png deleted file mode 100644 index 048fb39..0000000 Binary files a/common_base/src/main/res/drawable-xxxhdpi/ic_back.png and /dev/null differ diff --git a/common_base/src/main/res/drawable-xxxhdpi/ic_launcher.png b/common_base/src/main/res/drawable-xxxhdpi/ic_launcher.png deleted file mode 100644 index 41c09d5..0000000 Binary files a/common_base/src/main/res/drawable-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/common_base/src/main/res/drawable-xxxhdpi/input_add_default.png b/common_base/src/main/res/drawable-xxxhdpi/input_add_default.png deleted file mode 100644 index 34fa146..0000000 Binary files a/common_base/src/main/res/drawable-xxxhdpi/input_add_default.png and /dev/null differ diff --git a/common_base/src/main/res/drawable-xxxhdpi/input_add_disabled.png b/common_base/src/main/res/drawable-xxxhdpi/input_add_disabled.png deleted file mode 100644 index cee60c9..0000000 Binary files a/common_base/src/main/res/drawable-xxxhdpi/input_add_disabled.png and /dev/null differ diff --git a/common_base/src/main/res/drawable-xxxhdpi/input_minus_default.png b/common_base/src/main/res/drawable-xxxhdpi/input_minus_default.png deleted file mode 100644 index 1f8d100..0000000 Binary files a/common_base/src/main/res/drawable-xxxhdpi/input_minus_default.png and /dev/null differ diff --git a/common_base/src/main/res/drawable-xxxhdpi/input_minus_disabled.png b/common_base/src/main/res/drawable-xxxhdpi/input_minus_disabled.png deleted file mode 100644 index 525e156..0000000 Binary files a/common_base/src/main/res/drawable-xxxhdpi/input_minus_disabled.png and /dev/null differ diff --git a/common_base/src/main/res/drawable/bg_of_add_count_view.xml b/common_base/src/main/res/drawable/bg_of_add_count_view.xml new file mode 100644 index 0000000..8215d70 --- /dev/null +++ b/common_base/src/main/res/drawable/bg_of_add_count_view.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/common_base/src/main/res/drawable/bg_of_btn_selector.xml b/common_base/src/main/res/drawable/bg_of_btn_selector.xml index 74f2fdd..2edd10d 100644 --- a/common_base/src/main/res/drawable/bg_of_btn_selector.xml +++ b/common_base/src/main/res/drawable/bg_of_btn_selector.xml @@ -1,5 +1,5 @@ - + diff --git a/common_base/src/main/res/drawable/bg_of_color_f5_4radius.xml b/common_base/src/main/res/drawable/bg_of_color_f5_4radius.xml new file mode 100644 index 0000000..f633d90 --- /dev/null +++ b/common_base/src/main/res/drawable/bg_of_color_f5_4radius.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/common_base/src/main/res/drawable/bg_of_green_gradient_radius.xml b/common_base/src/main/res/drawable/bg_of_green_gradient_radius.xml new file mode 100644 index 0000000..bf9e8e9 --- /dev/null +++ b/common_base/src/main/res/drawable/bg_of_green_gradient_radius.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/common_base/src/main/res/drawable/bg_of_minus_count_view.xml b/common_base/src/main/res/drawable/bg_of_minus_count_view.xml new file mode 100644 index 0000000..1b6aa1c --- /dev/null +++ b/common_base/src/main/res/drawable/bg_of_minus_count_view.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/common_base/src/main/res/drawable/bg_of_gradient.xml b/common_base/src/main/res/drawable/bg_of_orange_gradient.xml similarity index 100% rename from common_base/src/main/res/drawable/bg_of_gradient.xml rename to common_base/src/main/res/drawable/bg_of_orange_gradient.xml diff --git a/common_base/src/main/res/drawable/bg_of_orange_gradient_radius.xml b/common_base/src/main/res/drawable/bg_of_orange_gradient_radius.xml new file mode 100644 index 0000000..26a204d --- /dev/null +++ b/common_base/src/main/res/drawable/bg_of_orange_gradient_radius.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/common_base/src/main/res/drawable/bg_of_red_gradient.xml b/common_base/src/main/res/drawable/bg_of_red_gradient.xml new file mode 100644 index 0000000..43fdb81 --- /dev/null +++ b/common_base/src/main/res/drawable/bg_of_red_gradient.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/common_base/src/main/res/drawable/bg_of_select_icon_selector.xml b/common_base/src/main/res/drawable/bg_of_select_icon_selector.xml new file mode 100644 index 0000000..eb644aa --- /dev/null +++ b/common_base/src/main/res/drawable/bg_of_select_icon_selector.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/common_base/src/main/res/drawable/browser_progress_bg.xml b/common_base/src/main/res/drawable/browser_progress_bg.xml new file mode 100644 index 0000000..d887fed --- /dev/null +++ b/common_base/src/main/res/drawable/browser_progress_bg.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/common_base/src/main/res/drawable-hdpi/corner_gray_shape.xml b/common_base/src/main/res/drawable/corner_gray_shape.xml similarity index 100% rename from common_base/src/main/res/drawable-hdpi/corner_gray_shape.xml rename to common_base/src/main/res/drawable/corner_gray_shape.xml diff --git a/common_base/src/main/res/drawable/corners_black_shape.xml b/common_base/src/main/res/drawable/corners_black_shape.xml new file mode 100644 index 0000000..695d250 --- /dev/null +++ b/common_base/src/main/res/drawable/corners_black_shape.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/common_base/src/main/res/drawable/corners_blue_shape.xml b/common_base/src/main/res/drawable/corners_blue_shape.xml index dad6292..695d250 100644 --- a/common_base/src/main/res/drawable/corners_blue_shape.xml +++ b/common_base/src/main/res/drawable/corners_blue_shape.xml @@ -1,7 +1,7 @@ - + diff --git a/common_base/src/main/res/drawable/corners_left_white_gray_selecter.xml b/common_base/src/main/res/drawable/corners_left_white_gray_selecter.xml index ee1c128..cba7038 100644 --- a/common_base/src/main/res/drawable/corners_left_white_gray_selecter.xml +++ b/common_base/src/main/res/drawable/corners_left_white_gray_selecter.xml @@ -10,7 +10,7 @@ - + diff --git a/common_base/src/main/res/drawable/corners_red_shape.xml b/common_base/src/main/res/drawable/corners_red_shape.xml new file mode 100644 index 0000000..5f94c77 --- /dev/null +++ b/common_base/src/main/res/drawable/corners_red_shape.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/common_base/src/main/res/drawable/corners_right_white_gray_selecter.xml b/common_base/src/main/res/drawable/corners_right_white_gray_selecter.xml index 98de50d..148ce63 100644 --- a/common_base/src/main/res/drawable/corners_right_white_gray_selecter.xml +++ b/common_base/src/main/res/drawable/corners_right_white_gray_selecter.xml @@ -10,7 +10,7 @@ - + diff --git a/common_base/src/main/res/drawable/corners_theme_shape_45.xml b/common_base/src/main/res/drawable/corners_theme_shape_45.xml new file mode 100644 index 0000000..e85348d --- /dev/null +++ b/common_base/src/main/res/drawable/corners_theme_shape_45.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/common_base/src/main/res/drawable/corners_white_gray_selecter.xml b/common_base/src/main/res/drawable/corners_white_gray_selecter.xml index 8b708c5..9fdbf27 100644 --- a/common_base/src/main/res/drawable/corners_white_gray_selecter.xml +++ b/common_base/src/main/res/drawable/corners_white_gray_selecter.xml @@ -10,7 +10,7 @@ - + diff --git a/common_base/src/main/res/drawable/market_shape_round_red.xml b/common_base/src/main/res/drawable/market_shape_round_red.xml new file mode 100644 index 0000000..ac2db34 --- /dev/null +++ b/common_base/src/main/res/drawable/market_shape_round_red.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/common_base/src/main/res/drawable/shape_item_index_red.xml b/common_base/src/main/res/drawable/shape_item_index_red.xml new file mode 100644 index 0000000..ad35a17 --- /dev/null +++ b/common_base/src/main/res/drawable/shape_item_index_red.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/common_base/src/main/res/drawable/shape_item_index_white.xml b/common_base/src/main/res/drawable/shape_item_index_white.xml new file mode 100644 index 0000000..59091bd --- /dev/null +++ b/common_base/src/main/res/drawable/shape_item_index_white.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/common_base/src/main/res/drawable/stroke_gray_white_shape.xml b/common_base/src/main/res/drawable/stroke_gray_white_shape.xml new file mode 100644 index 0000000..8125cfd --- /dev/null +++ b/common_base/src/main/res/drawable/stroke_gray_white_shape.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/common_base/src/main/res/drawable/stroke_hollow_white_shape.xml b/common_base/src/main/res/drawable/stroke_hollow_white_shape.xml index bf768f9..8125cfd 100644 --- a/common_base/src/main/res/drawable/stroke_hollow_white_shape.xml +++ b/common_base/src/main/res/drawable/stroke_hollow_white_shape.xml @@ -5,7 +5,7 @@ + android:color="@color/light_white" /> diff --git a/common_base/src/main/res/layout/action_bar.xml b/common_base/src/main/res/layout/action_bar.xml index b96d64a..9da3300 100644 --- a/common_base/src/main/res/layout/action_bar.xml +++ b/common_base/src/main/res/layout/action_bar.xml @@ -1,35 +1,36 @@ + android:paddingStart="@dimen/title_bar_padding" + android:paddingEnd="6dp" /> + android:orientation="horizontal" /> + android:paddingStart="4dp" + android:paddingEnd="@dimen/title_bar_padding" + tools:ignore="RtlHardcoded" /> - diff --git a/common_base/src/main/res/layout/activity_base.xml b/common_base/src/main/res/layout/activity_base.xml index d37ca85..79d7212 100644 --- a/common_base/src/main/res/layout/activity_base.xml +++ b/common_base/src/main/res/layout/activity_base.xml @@ -2,15 +2,20 @@ + android:layout_height="@dimen/title_bar_height" + android:background="@color/white" + android:visibility="gone" /> - + + + diff --git a/common_base/src/main/res/layout/activity_browser.xml b/common_base/src/main/res/layout/activity_browser.xml new file mode 100644 index 0000000..ea06bb1 --- /dev/null +++ b/common_base/src/main/res/layout/activity_browser.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common_base/src/main/res/layout/activity_horizontal_tab.xml b/common_base/src/main/res/layout/activity_horizontal_tab.xml index 8c026bc..6e353e7 100644 --- a/common_base/src/main/res/layout/activity_horizontal_tab.xml +++ b/common_base/src/main/res/layout/activity_horizontal_tab.xml @@ -13,16 +13,16 @@ android:background="@color/white" app:pstsBold="true" app:pstsHasTopLine="false" - app:pstsIndicatorColor="@color/blue" + app:pstsIndicatorColor="@color/theme" app:pstsIndicatorHeight="1.5dp" app:pstsShouldExpand="true" app:pstsTextDefaultColor="@color/black" - app:pstsTextSelectedColor="@color/blue" + app:pstsTextSelectedColor="@color/theme" app:pstsTextSize="14sp" app:pstsUnderlineColor="@color/gray" app:pstsUnderlineHeight="1px" /> - diff --git a/common_base/src/main/res/layout/activity_image_gallery.xml b/common_base/src/main/res/layout/activity_image_gallery.xml new file mode 100644 index 0000000..fa58373 --- /dev/null +++ b/common_base/src/main/res/layout/activity_image_gallery.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common_base/src/main/res/layout/activity_scan.xml b/common_base/src/main/res/layout/activity_scan.xml new file mode 100644 index 0000000..c6f24c3 --- /dev/null +++ b/common_base/src/main/res/layout/activity_scan.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/common_base/src/main/res/layout/layout_loading.xml b/common_base/src/main/res/layout/activity_select_file.xml similarity index 52% rename from common_base/src/main/res/layout/layout_loading.xml rename to common_base/src/main/res/layout/activity_select_file.xml index 773706a..1936a51 100644 --- a/common_base/src/main/res/layout/layout_loading.xml +++ b/common_base/src/main/res/layout/activity_select_file.xml @@ -2,17 +2,12 @@ - - - - + android:layout_marginStart="5dp" + android:layout_marginEnd="5dp" /> \ No newline at end of file diff --git a/common_base/src/main/res/layout/activity_springboard.xml b/common_base/src/main/res/layout/activity_springboard.xml new file mode 100644 index 0000000..8e17cb3 --- /dev/null +++ b/common_base/src/main/res/layout/activity_springboard.xml @@ -0,0 +1,6 @@ + + diff --git a/common_base/src/main/res/layout/activity_webview.xml b/common_base/src/main/res/layout/activity_webview.xml deleted file mode 100644 index ef427f5..0000000 --- a/common_base/src/main/res/layout/activity_webview.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/common_base/src/main/res/layout/base_tablayout_child.xml b/common_base/src/main/res/layout/base_tablayout_child.xml new file mode 100644 index 0000000..ab0e0dd --- /dev/null +++ b/common_base/src/main/res/layout/base_tablayout_child.xml @@ -0,0 +1,13 @@ + + + \ No newline at end of file diff --git a/common_base/src/main/res/layout/dailog_date.xml b/common_base/src/main/res/layout/dailog_date.xml new file mode 100644 index 0000000..0ed6397 --- /dev/null +++ b/common_base/src/main/res/layout/dailog_date.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + diff --git a/common_base/src/main/res/layout/dialog_app.xml b/common_base/src/main/res/layout/dialog_app.xml index 3935f40..54c0709 100644 --- a/common_base/src/main/res/layout/dialog_app.xml +++ b/common_base/src/main/res/layout/dialog_app.xml @@ -7,12 +7,13 @@ + android:paddingTop="15dp" + android:visibility="gone"> - - - - - + android:layout_height="wrap_content"> - - - - - - - + android:id="@+id/ll_content_layout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center" + android:orientation="vertical" + android:paddingLeft="10dp" + android:paddingRight="10dp"> - + - - - - - - - - - - + android:visibility="gone" /> - + + android:layout_marginTop="15dp"> - - - - - @@ -16,10 +17,12 @@ \ No newline at end of file diff --git a/common_base/src/main/res/layout/dialog_progressbar.xml b/common_base/src/main/res/layout/dialog_progressbar.xml new file mode 100644 index 0000000..fe18e6a --- /dev/null +++ b/common_base/src/main/res/layout/dialog_progressbar.xml @@ -0,0 +1,25 @@ + + + + + + + \ No newline at end of file diff --git a/common_base/src/main/res/layout/empty_layout.xml b/common_base/src/main/res/layout/empty_layout.xml deleted file mode 100644 index cec4857..0000000 --- a/common_base/src/main/res/layout/empty_layout.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - diff --git a/common_base/src/main/res/layout/item_of_load_all_data.xml b/common_base/src/main/res/layout/item_of_load_all_data.xml new file mode 100644 index 0000000..e72aab4 --- /dev/null +++ b/common_base/src/main/res/layout/item_of_load_all_data.xml @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/common_base/src/main/res/layout/item_of_select_file.xml b/common_base/src/main/res/layout/item_of_select_file.xml new file mode 100644 index 0000000..6986840 --- /dev/null +++ b/common_base/src/main/res/layout/item_of_select_file.xml @@ -0,0 +1,27 @@ + + + + + + + + \ No newline at end of file diff --git a/common_base/src/main/res/layout/item_of_select_image.xml b/common_base/src/main/res/layout/item_of_select_image.xml new file mode 100644 index 0000000..ac480ff --- /dev/null +++ b/common_base/src/main/res/layout/item_of_select_image.xml @@ -0,0 +1,26 @@ + + + + + + + + \ No newline at end of file diff --git a/common_base/src/main/res/layout/item_of_selector_pop.xml b/common_base/src/main/res/layout/item_of_selector_pop.xml new file mode 100644 index 0000000..79adb8e --- /dev/null +++ b/common_base/src/main/res/layout/item_of_selector_pop.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/common_base/src/main/res/layout/layout_banner_img.xml b/common_base/src/main/res/layout/layout_banner_img.xml new file mode 100644 index 0000000..f9f2372 --- /dev/null +++ b/common_base/src/main/res/layout/layout_banner_img.xml @@ -0,0 +1,15 @@ + + + + + + + diff --git a/common_base/src/main/res/layout/fragment_base.xml b/common_base/src/main/res/layout/layout_base.xml similarity index 86% rename from common_base/src/main/res/layout/fragment_base.xml rename to common_base/src/main/res/layout/layout_base.xml index 15cc49e..9919d9a 100644 --- a/common_base/src/main/res/layout/fragment_base.xml +++ b/common_base/src/main/res/layout/layout_base.xml @@ -2,13 +2,14 @@ + android:layout="@layout/layout_empty" /> + android:layout_height="12dp" + android:background="@drawable/bg_of_minus_count_view" /> + android:layout_height="12dp" + android:background="@drawable/bg_of_add_count_view" /> \ No newline at end of file diff --git a/common_base/src/main/res/layout/layout_empty.xml b/common_base/src/main/res/layout/layout_empty.xml index b7568b5..05e4928 100644 --- a/common_base/src/main/res/layout/layout_empty.xml +++ b/common_base/src/main/res/layout/layout_empty.xml @@ -1,21 +1,39 @@ + android:id="@+id/iv_empty" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + android:layout_marginTop="19dp" + android:textColor="@color/color_999999" + android:textSize="14sp" /> + + + - \ No newline at end of file + diff --git a/common_base/src/main/res/layout/layout_error.xml b/common_base/src/main/res/layout/layout_error.xml deleted file mode 100644 index 8f580bc..0000000 --- a/common_base/src/main/res/layout/layout_error.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/common_base/src/main/res/layout/layout_item_of_dialog_bottom_in.xml b/common_base/src/main/res/layout/layout_item_of_dialog_bottom_in.xml index ed06b47..a7c33fd 100644 --- a/common_base/src/main/res/layout/layout_item_of_dialog_bottom_in.xml +++ b/common_base/src/main/res/layout/layout_item_of_dialog_bottom_in.xml @@ -8,10 +8,10 @@ android:id="@+id/tv_text" style="@style/TextStyle" android:layout_width="match_parent" - android:layout_height="50dp" - android:gravity="center" /> + android:layout_height="wrap_content" + android:gravity="center" + android:minHeight="45dp" /> - diff --git a/common_base/src/main/res/layout/layout_multiple_item.xml b/common_base/src/main/res/layout/layout_multiple_item.xml index 2df4adb..6bc1aa9 100644 --- a/common_base/src/main/res/layout/layout_multiple_item.xml +++ b/common_base/src/main/res/layout/layout_multiple_item.xml @@ -1,6 +1,7 @@ diff --git a/common_base/src/main/res/layout/layout_nothing.xml b/common_base/src/main/res/layout/layout_nothing.xml new file mode 100644 index 0000000..4ce1eae --- /dev/null +++ b/common_base/src/main/res/layout/layout_nothing.xml @@ -0,0 +1,5 @@ + + + diff --git a/common_base/src/main/res/layout/layout_refresh.xml b/common_base/src/main/res/layout/layout_refresh.xml index c29def5..e0845be 100644 --- a/common_base/src/main/res/layout/layout_refresh.xml +++ b/common_base/src/main/res/layout/layout_refresh.xml @@ -1,13 +1,15 @@ - + android:layout_height="match_parent" + android:background="@color/bg" + android:fillViewport="true"> - + android:layout_height="match_parent" + android:background="@color/bg" /> - - + diff --git a/common_base/src/main/res/layout/layout_refresh_view.xml b/common_base/src/main/res/layout/layout_refresh_view.xml deleted file mode 100644 index b1a4130..0000000 --- a/common_base/src/main/res/layout/layout_refresh_view.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/common_base/src/main/res/layout/layout_search.xml b/common_base/src/main/res/layout/layout_search.xml new file mode 100644 index 0000000..4eeffa7 --- /dev/null +++ b/common_base/src/main/res/layout/layout_search.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/common_base/src/main/res/layout/laytou_update_progress.xml b/common_base/src/main/res/layout/laytou_update_progress.xml deleted file mode 100644 index b3be34d..0000000 --- a/common_base/src/main/res/layout/laytou_update_progress.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/common_base/src/main/res/layout/pop_date_range.xml b/common_base/src/main/res/layout/pop_date_range.xml new file mode 100644 index 0000000..a58e5ce --- /dev/null +++ b/common_base/src/main/res/layout/pop_date_range.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/common_base/src/main/res/layout/pop_selector.xml b/common_base/src/main/res/layout/pop_selector.xml new file mode 100644 index 0000000..d1a280c --- /dev/null +++ b/common_base/src/main/res/layout/pop_selector.xml @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/common_base/src/main/res/layout/toast.xml b/common_base/src/main/res/layout/toast.xml deleted file mode 100644 index be9a8d1..0000000 --- a/common_base/src/main/res/layout/toast.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - diff --git a/common_base/src/main/res/values/attrs.xml b/common_base/src/main/res/values/attrs.xml index bd3caf8..2441cb3 100644 --- a/common_base/src/main/res/values/attrs.xml +++ b/common_base/src/main/res/values/attrs.xml @@ -1,7 +1,7 @@ - + @@ -19,33 +19,139 @@ + + + + + - + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -61,16 +167,24 @@ - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common_base/src/main/res/values/colors.xml b/common_base/src/main/res/values/colors.xml index 8db3f9f..46caca2 100644 --- a/common_base/src/main/res/values/colors.xml +++ b/common_base/src/main/res/values/colors.xml @@ -5,11 +5,19 @@ #00000000 #FFFFFF #BBBBBB - #DC143C + #FF0000 #0099FF #000000 #FFA82E - #eeeeee - #ebebeb + #eeeff3 + #ff8000 + #eeeeee + #E1FFFF + #F0E68C + #7FFF00 + #333333 + #666666 + #999999 + #F5F5F5 diff --git a/common_base/src/main/res/values/dimens.xml b/common_base/src/main/res/values/dimens.xml index 29627de..9451c33 100644 --- a/common_base/src/main/res/values/dimens.xml +++ b/common_base/src/main/res/values/dimens.xml @@ -8,6 +8,12 @@ 150dp 115dp 100dp + + 20dp + + 50dp + + 14dp \ No newline at end of file diff --git a/common_base/src/main/res/values/strings.xml b/common_base/src/main/res/values/strings.xml index 93f1ce9..5ff7237 100644 --- a/common_base/src/main/res/values/strings.xml +++ b/common_base/src/main/res/values/strings.xml @@ -1,8 +1,122 @@ AMD + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 暂无数据 - 网络请求失败 + 网络连接失败, 请检查网络 + 服务器异常 + 请求超时 上拉加载 下拉刷新 松开刷新 @@ -11,5 +125,45 @@ 取消 确定 提示 + 您必须授权必要权限才可使用该功能 + 返回 + 关闭 + 删除成功 + 您最多可选%d张图片 + 您最多可选%d个文件 + 选择图片 + 选择文件 + 您还未选择呢 + 请先设置图片 + 选择图片失败 + 扫一扫 + 相机 + 相册 + ~没有更多拉~ + 正在处理中,请稍等… + + + + 下拉可以刷新 + 正在刷新… + 正在加载… + 释放立即刷新 + 刷新完成 + 刷新失败 + 上次更新 MM-dd HH:mm + 释放进入二楼 + 上拉加载更多 + 释放立即加载 + 正在加载… + 正在刷新… + 加载完成 + 加载失败 + ~没有更多啦~ + 刷新重试 + 开始时间 + 结束时间 + + 重置 + diff --git a/common_base/src/main/res/values/styles.xml b/common_base/src/main/res/values/styles.xml index f04a5f1..464bda8 100644 --- a/common_base/src/main/res/values/styles.xml +++ b/common_base/src/main/res/values/styles.xml @@ -4,6 +4,11 @@ + + + diff --git a/config.gradle b/config.gradle index c71bcd9..9c3790d 100644 --- a/config.gradle +++ b/config.gradle @@ -1,6 +1,4 @@ -/** - * 全局统一配置文件 - */ +//Gradle配置文件 ext { //true 每个业务Module可以单独开发 //false 每个业务Module以lib的方式运行 @@ -8,130 +6,128 @@ ext { isModule = false //版本号 versions = [ - applicationId : "com.wss.amd", //应用ID - versionCode : 1, //版本号 - versionName : "1.0.0", //版本名称 - - compileSdkVersion : 27, - buildToolsVersion : "27.0.3", - minSdkVersion : 17, - targetSdkVersion : 23, - - androidSupportSdkVersion: "27.1.1", - constraintLayoutVersion : "1.1.1", - runnerVersion : "1.0.1", - espressoVersion : "3.0.1", - junitVersion : "4.12", - annotationsVersion : "24.0.0", - - multidexVersion : "1.0.2", - butterknifeVersion : "8.4.0", - arouterApiVersion : "1.4.0", - arouterCompilerVersion : "1.2.1", - arouterannotationVersion: "1.0.4", - eventbusVersion : "3.0.0", - novateVersion : "1.5.5", - loggerVersion : "2.2.0", - fastjsonVersion : "1.1.54", - barlibraryVersion : "2.3.0", - picassoVersion : "2.71828", - bannerVersion : "1.4.10", - javaxVersion : "1.2", - lombokVersion : "1.16.6", - greendaoVersion : "3.2.2", - pickerViewVersion : "4.1.6", - superAdapterVersion : "3.6.8", + applicationIdDEV : "com.wss.amddev", //应用ID + applicationId : "com.wss.amd", //应用ID + versionNameDEV : "2.0.0", //版本名称 + versionName : "2.0.0", //版本名称 + + javaSDKVersion : 1.8, + + compileSdkVersion : 29, + buildToolsVersion : "29.0.2", + minSdkVersion : 21, + targetSdkVersion : 26, + + appcompatVersion : "1.1.0", + recyclerviewVersion : "1.1.0", + cardviewVersion : "1.0.0", + annotationVersion : "1.1.0", + materialVersion : "1.2.0-alpha05", + multidexVersion : "2.0.1", + junitVersion : "4.12", + swiperefreshVersion : "1.0.0", + localbroadcastVersion : "1.0.0", + + javaxAnnotationVersion: "1.2", + lombokVersion : "1.16.6", + arouterVersion : "1.5.0", + arouterCompilerVersion: "1.2.2", + butterknifeVersion : "10.2.1", + eventBusVersion : "3.0.0", + loggerVersion : "2.2.0", + immersionbarVersion : "3.0.0", + bannerVersion : "2.1.5", + glideVersion : "4.8.0", + pickerViewVersion : "4.1.9", + QRCodeViewVersion : "1.3", + xxpermissionsVersion : "6.2", + rxHttpVersion : "2.1.1", + rxAndroidVersion : "2.1.1", + rxLifeVersion : "2.0.0", + superAdapterVersion : "3.6.8", + mpAndroidChartVersion : "v3.1.0", + scaleImageViewVersion : "3.10.0", + zxingViewVersion : "0.9.6", + zxingcoreVersion : "3.3.3", + refreshLayoutVersion : "1.1.0-alpha-14", + greendaoVersion : "3.2.2", ] - dependencies = ["appcompat_v7" : "com.android.support:appcompat-v7:${versions["androidSupportSdkVersion"]}", - "constraint_layout" : "com.android.support.constraint:constraint-layout:${versions["constraintLayoutVersion"]}", - "runner" : "com.android.support.test:runner:${versions["runnerVersion"]}", - "espresso_core" : "com.android.support.test.espresso:espresso-core:${versions["espressoVersion"]}", - "junit" : "junit:junit:${versions["junitVersion"]}", - "support_annotations" : "com.android.support:support-annotations:${versions["annotationsVersion"]}", - "design" : "com.android.support:design:${versions["androidSupportSdkVersion"]}", - "support-v4" : "com.android.support:support-v4:${versions["androidSupportSdkVersion"]}", - "cardview-v7" : "com.android.support:cardview-v7:${versions["androidSupportSdkVersion"]}", - "recyclerview-v7" : "com.android.support:recyclerview-v7:${versions["androidSupportSdkVersion"]}", - - //方法数超过65535解决方法64K MultiDex分包方法 - "multidex" : "com.android.support:multidex:${versions["multidexVersion"]}", - - //路由 - "arouter_api" : "com.alibaba:arouter-api:${versions["arouterApiVersion"]}", - "arouter_compiler" : "com.alibaba:arouter-compiler:${versions["arouterCompilerVersion"]}", - "arouter_annotation" : "com.alibaba:arouter-annotation:${versions["arouterannotationVersion"]}", - - //黄油刀 - "butterknife_compiler": "com.jakewharton:butterknife-compiler:${versions["butterknifeVersion"]}", - "butterknife" : "com.jakewharton:butterknife:${versions["butterknifeVersion"]}", - - //事件订阅 - "eventbus" : "org.greenrobot:eventbus:${versions["eventbusVersion"]}", - - //网络 - "novate" : "com.tamic.novate:novate:${versions["novateVersion"]}", - - //日志 - "logger" : "com.orhanobut:logger:${versions["loggerVersion"]}", - - //fastJson - "fastjson" : "com.alibaba:fastjson:${versions["fastjsonVersion"]}.android", - - //沉浸式状态栏 - "barlibrary" : "com.gyf.barlibrary:barlibrary:${versions["barlibraryVersion"]}", - - //banner - "banner" : "com.youth.banner:banner:${versions["bannerVersion"]}", - - //图片加载 - "picasso" : "com.squareup.picasso:picasso:${versions["picassoVersion"]}", - - //lombok - "lombokJavax" : "javax.annotation:javax.annotation-api:${versions["javaxVersion"]}", - "lombok" : "org.projectlombok:lombok:${versions["lombokVersion"]}", - - //数据库 - "greenDao" : "org.greenrobot:greendao:${versions["greendaoVersion"]}", - - //时间,地址,条件选择器 - "pickerView" : "com.contrarywind:Android-PickerView:${versions["pickerViewVersion"]}", - - //万能Adapter - "superAdapter" : "org.byteam.superadapter:superadapter:${versions["superAdapterVersion"]}", - -// //图片缩放 -// "photoview" : "com.github.chrisbanes.photoview:library:1.2.4", -// //SmartRefreshLayout -// "smartRefreshLayout" : "com.scwang.smartrefresh:SmartRefreshLayout:1.0.5.1", -// "SmartRefreshHeader" : "com.scwang.smartrefresh:SmartRefreshHeader:1.0.5.1", - -// //baseRecyclerViewAdapterHelper -// "baseRecyclerViewAdapterHelper" : "com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.22", -// //Bugly集成 -// "bugly" : "com.tencent.bugly:crashreport_upgrade:latest.release", -// //仿ios进度条 已抽取到lib中 -// "kprogresshud" : "com.kaopiz:kprogresshud:1.1.0", - // //greendao -// "greendao3" : "org.greenrobot:greendao:3.2.2", -// //Android屏幕适配 -// "autolayout" : "com.zhy:autolayout:1.4.5", -// //安卓调试神器-Stetho -// "stetho" : "com.facebook.stetho:stetho:1.5.0", -// "stetho-okhttp3" : "com.facebook.stetho:stetho-okhttp3:1.5.0", -// // 仿ios弹出对话窗体 已抽取到lib中 -// "alertview" : "com.bigkoo:alertview:1.0.3", -// //bugly sdk -// "tencent-bugly" : "com.tencent.bugly:crashreport_upgrade:latest.release", -// //垂直的tabLayout -// "VerticalTabLayout" : "q.rorbin:VerticalTabLayout:1.2.5", -// //水平tablayout -// "FlycoTabLayout_Lib" : "com.flyco.tablayout:FlycoTabLayout_Lib:2.1.2@aar", -// //leakcanary内存泄露 -// "leakcanary-android" : "com.squareup.leakcanary:leakcanary-android:1.5.1", -// "leakcanary-android-no-op" : "com.squareup.leakcanary:leakcanary-android-no-op:1.5.1", + //依赖库 + dependencies = [ + //android系统依赖 + appcompat : "androidx.appcompat:appcompat:${versions["appcompatVersion"]}", + recyclerview : "androidx.recyclerview:recyclerview:${versions["recyclerviewVersion"]}", + cardview : "androidx.cardview:cardview:${versions["cardviewVersion"]}", + annotation : "androidx.annotation:annotation:${versions["annotationVersion"]}", + material : "com.google.android.material:material:${versions["materialVersion"]}", + multidex : "androidx.multidex:multidex:${versions["multidexVersion"]}", + junit : "junit:junit:${versions["junitVersion"]}", + //google官方下拉刷新插件,也就im中使用了 + swiperefreshlayout : "androidx.swiperefreshlayout:swiperefreshlayout:${versions["swiperefreshVersion"]}", + localbroadcastmanager: "androidx.localbroadcastmanager:localbroadcastmanager:${versions["localbroadcastVersion"]}", + //Lombok + lombok : "org.projectlombok:lombok:${versions["lombokVersion"]}", + javaxAnnotation : "javax.annotation:javax.annotation-api:${versions["javaxAnnotationVersion"]}", + + //阿里ARouter + arouter : "com.alibaba:arouter-api:${versions["arouterVersion"]}", + arouterCompiler : "com.alibaba:arouter-compiler:${versions["arouterCompilerVersion"]}", + + //Butterknife + butterknife : "com.jakewharton:butterknife:${versions["butterknifeVersion"]}", + butterknifeCompiler : "com.jakewharton:butterknife-compiler:${versions["butterknifeVersion"]}", + + //EventBus + eventBus : "org.greenrobot:eventbus:${versions["eventBusVersion"]}", + + //日志 + logger : "com.orhanobut:logger:${versions["loggerVersion"]}", + + //沉浸栏 + immersionbar : "com.gyf.immersionbar:immersionbar:${versions["immersionbarVersion"]}", + + //Banner轮播控件 + banner : "com.bigkoo:convenientbanner:${versions["bannerVersion"]}", + + //Gllide + glide : "com.github.bumptech.glide:glide:${versions["glideVersion"]}", + + //弹窗选择器 + pickerView : "com.contrarywind:Android-PickerView:${versions["pickerViewVersion"]}", + + //危险权限 + xxpermissions : "com.hjq:xxpermissions:${versions["xxpermissionsVersion"]}", + + //网络 + rxHttp : "com.rxjava.rxhttp:rxhttp:${versions["rxHttpVersion"]}", + rxLife : "com.rxjava.rxlife:rxlife-x:${versions["rxLifeVersion"]}", + rxCompiler : "com.rxjava.rxhttp:rxhttp-compiler:${versions["rxHttpVersion"]}", + rxAndroid : "io.reactivex.rxjava2:rxandroid:${versions["rxAndroidVersion"]}", + + //万能Adapter + superAdapter : "org.byteam.superadapter:superadapter:${versions["superAdapterVersion"]}", + + //图表库 + mpAndroidChart : "com.github.PhilJay:MPAndroidChart:${versions["mpAndroidChartVersion"]}", + + //展示大图+手势滑动 + scaleImageView : "com.davemorrissey.labs:subsampling-scale-image-view:${versions["scaleImageViewVersion"]}", + + //二维码扫描 + zxing : "me.devilsen:CZXing:${versions["zxingViewVersion"]}", + + //刷新库 + refreshLayout : "com.scwang.smartrefresh:SmartRefreshLayout:${versions["refreshLayoutVersion"]}", + + //数据库 + "greenDao" : "org.greenrobot:greendao:${versions["greendaoVersion"]}", ] + //第三方AppId、AppKey等配置 + appkeys = [ + + ] } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 69b4e93..d0bdce0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,5 +14,8 @@ org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true -android.enableAapt2=false +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true +#VIVO INSTALL +android.injected.testOnly=false diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fe35556..2a9a320 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/gradlew b/gradlew index 9d82f78..7870379 100644 --- a/gradlew +++ b/gradlew @@ -119,7 +119,7 @@ if $cygwin ; then SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments + # Add a people-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi diff --git a/key/amd.jsk b/key/amd.jsk new file mode 100644 index 0000000..462c63a Binary files /dev/null and b/key/amd.jsk differ diff --git a/module_main/README.txt b/module_main/README.txt new file mode 100644 index 0000000..36632d5 --- /dev/null +++ b/module_main/README.txt @@ -0,0 +1,11 @@ +项目主模块 + +为防止同名资源文件被覆盖,则采用如下方式来规避 +1.该module的资源命名必须以[main_]打头 + 如:main_icon_close,main_img_bg,main_shape_round_4 + +2.该module的布局文件命名必须以[main_]打头 + 如:main_activity_main,main_fragment_main,main_item_of_user_list + +3.涉及到跨module跳转的Activity、Fragment需要在common_base模块的ARouterConfig中配置跳转的Path + diff --git a/module_main/build.gradle b/module_main/build.gradle index 7faf644..d2d33ef 100644 --- a/module_main/build.gradle +++ b/module_main/build.gradle @@ -4,6 +4,7 @@ if (Boolean.valueOf(rootProject.ext.isModule)) { apply plugin: 'com.android.library' } apply plugin: 'com.jakewharton.butterknife' +apply plugin: 'org.greenrobot.greendao' android { compileSdkVersion rootProject.ext.versions.compileSdkVersion @@ -49,16 +50,19 @@ android { } } } + compileOptions { + targetCompatibility rootProject.ext.versions.javaSDKVersion + sourceCompatibility rootProject.ext.versions.javaSDKVersion + } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) //公用依赖包 implementation project(':common_base') - //Arouter路由 - annotationProcessor rootProject.ext.dependencies["arouter_compiler"] + annotationProcessor rootProject.ext.dependencies["arouterCompiler"] //黄油刀 - annotationProcessor rootProject.ext.dependencies["butterknife_compiler"] + annotationProcessor rootProject.ext.dependencies["butterknifeCompiler"] } diff --git a/module_main/doc.txt b/module_main/doc.txt deleted file mode 100644 index eac0b80..0000000 --- a/module_main/doc.txt +++ /dev/null @@ -1,3 +0,0 @@ - -说明:该module为Main_module 即 包含启动页、tab等入口页面的module - 为防止资源文件命名冲突,该模块下的所有资源文件命名都以main_开头 \ No newline at end of file diff --git a/module_main/src/main/AndroidManifest.xml b/module_main/src/main/AndroidManifest.xml index 7d27273..538c336 100644 --- a/module_main/src/main/AndroidManifest.xml +++ b/module_main/src/main/AndroidManifest.xml @@ -1,27 +1,28 @@ - + + + - - - - - - + android:name=".ui.main.MainActivity" + android:launchMode="singleTask" + android:screenOrientation="portrait" + android:theme="@style/ActivityTheme" + android:windowSoftInputMode="adjustUnspecified|stateHidden" /> + + + + + + + + + + + - - - - - - - - - diff --git a/module_main/src/main/java/com/wss/module/main/bean/Article.java b/module_main/src/main/java/com/wss/module/main/bean/Article.java deleted file mode 100644 index bd65d3f..0000000 --- a/module_main/src/main/java/com/wss/module/main/bean/Article.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.wss.module.main.bean; - -import com.wss.common.base.bean.BaseBean; - -import java.util.List; - -import lombok.Getter; -import lombok.Setter; - -/** - * Describe:文章 - * Created by 吴天强 on 2018/10/18. - */ - -@Getter -@Setter -public class Article extends BaseBean { - - /** - * apkLink : - * author : 请叫我大苏 - * chapterId : 228 - * chapterName : 辅助 or 工具类 - * collect : false - * courseId : 13 - * desc : - * envelopePic : - * fresh : false - * id : 5852 - * link : https://www.jianshu.com/p/6064a14d86a3 - * niceDate : 2018-10-14 - * origin : - * projectLink : - * publishTime : 1539513452000 - * superChapterId : 135 - * superChapterName : 项目必备 - * title : 封装个 Android 的高斯模糊组件 - * type : 0 - * userId : -1 - * visible : 1 - * zan : 0 - */ - private String apkLink; - private String author; - private int chapterId; - private String chapterName; - private boolean collect; - private int courseId; - private String desc; - private String envelopePic; - private boolean fresh; - private int id; - private String link; - private String niceDate; - private String origin; - private String projectLink; - private long publishTime; - private int superChapterId; - private String superChapterName; - private String title; - private int type; - private int userId; - private int visible; - private int zan; - private List tags; - - @Getter - @Setter - public class ArticleTag extends BaseBean { - - private String name; - private String url; - } -} diff --git a/module_main/src/main/java/com/wss/module/main/bean/BannerInfo.java b/module_main/src/main/java/com/wss/module/main/bean/BannerInfo.java deleted file mode 100644 index f958fbb..0000000 --- a/module_main/src/main/java/com/wss/module/main/bean/BannerInfo.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.wss.module.main.bean; - -import com.wss.common.base.bean.BaseBean; - -/** - * Describe:Banner实体类 - * Created by 吴天强 on 2018/10/17. - */ - -public class BannerInfo extends BaseBean { - - - /** - * desc : - * id : 18 - * imagePath : http://www.wanandroid.com/blogimgs/00f83f1d-3c50-439f-b705-54a49fc3d90d.jpg - * isVisible : 1 - * order : 0 - * title : 公众号文章列表强势上线 - * type : 0 - * url : http://www.wanandroid.com/wxarticle/list/409/1 - */ - - private int id; - private String title; - private String desc; - private String imagePath; - private int isVisible; - private int order; - private int type; - private String url; - - public void setDesc(String desc) { - this.desc = desc; - } - - public void setId(int id) { - this.id = id; - } - - public void setImagePath(String imagePath) { - this.imagePath = imagePath; - } - - public void setIsVisible(int isVisible) { - this.isVisible = isVisible; - } - - public void setOrder(int order) { - this.order = order; - } - - public void setTitle(String title) { - this.title = title; - } - - public void setType(int type) { - this.type = type; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getDesc() { - return desc; - } - - public int getId() { - return id; - } - - public String getImagePath() { - return imagePath; - } - - public int getIsVisible() { - return isVisible; - } - - public int getOrder() { - return order; - } - - public String getTitle() { - return title; - } - - public int getType() { - return type; - } - - public String getUrl() { - return url; - } -} diff --git a/module_main/src/main/java/com/wss/module/main/bean/IMMessage.java b/module_main/src/main/java/com/wss/module/main/bean/IMMessage.java new file mode 100644 index 0000000..7919f67 --- /dev/null +++ b/module_main/src/main/java/com/wss/module/main/bean/IMMessage.java @@ -0,0 +1,31 @@ +package com.wss.module.main.bean; + +import com.wss.common.base.bean.BaseBean; + +import lombok.Getter; +import lombok.Setter; + +/** + * Describe:聊天信息 + * Created by 吴天强 on 2018/10/30. + */ +@Getter +@Setter +public class IMMessage extends BaseBean { + + public static final int TYPE_SEND = 0; + public static final int TYPE_FROM = 1; + + private String name;//姓名 + private int icon;//头像 + private int type;//消息类型 0 发送 1接受 + private String msg;//消息内容 + private String date;//消息时间 + + public IMMessage(String name, int icon, int type, String msg) { + this.name = name; + this.icon = icon; + this.type = type; + this.msg = msg; + } +} diff --git a/module_main/src/main/java/com/wss/module/main/bean/MainBlock.java b/module_main/src/main/java/com/wss/module/main/bean/MainBlock.java deleted file mode 100644 index 9517915..0000000 --- a/module_main/src/main/java/com/wss/module/main/bean/MainBlock.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.wss.module.main.bean; - -import com.wss.common.base.BaseActivity; -import com.wss.common.base.bean.BaseBean; - -import lombok.Getter; -import lombok.Setter; - -/** - * Describe:首页模块 - * Created by 吴天强 on 2018/10/18. - */ -@Getter -@Setter -public class MainBlock extends BaseBean { - - private String title; - private int res; - private String url; - private String describe; - private Class clazz; - - public MainBlock(String title, int res) { - this(title, "", res); - } - - public MainBlock(String title, String url, int res) { - this(title, res, url, null, ""); - } - - public MainBlock(String title, int res, Class clazz, String describe) { - this(title, res, "", clazz, describe); - } - - public MainBlock(String title, int res, String url, Class clazz, String describe) { - this.title = title; - this.res = res; - this.url = url; - this.clazz = clazz; - this.describe = describe; - } - - @Override - public String toString() { - return "MainBlock{" + - "title='" + title + '\'' + - ", res=" + res + - ", url='" + url + '\'' + - '}'; - } -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/count/CountViewActivity.java b/module_main/src/main/java/com/wss/module/main/ui/count/CountViewActivity.java deleted file mode 100644 index 4f97691..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/count/CountViewActivity.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.wss.module.main.ui.count; - -import com.wss.common.base.ActionBarActivity; -import com.wss.common.base.mvp.BasePresenter; -import com.wss.common.widget.CountClickView; -import com.wss.module.main.R; -import com.wss.module.main.R2; - -import butterknife.BindView; - -/** - * Describe:数量加减控件 - * Created by 吴天强 on 2018/10/24. - */ - -public class CountViewActivity extends ActionBarActivity { - - @BindView(R2.id.ccv_dialog) - CountClickView ccvDialog; - - @BindView(R2.id.ccv_cust) - CountClickView ccvCust; - - @Override - protected BasePresenter createPresenter() { - return null; - } - - @Override - protected int getLayoutId() { - return R.layout.main_activity_count_view; - } - - @Override - protected void initView() { - setTitleText("数量加减控件"); - - //显示输入框 - ccvDialog.setInput(true); - - //自定义背景图 - ccvCust.setButtonRes(R.drawable.main_stepper_reduce, R.drawable.main_stepper_reduce, - R.drawable.main_stepper_add, R.drawable.main_stepper_add_disable); - ccvCust.setBtnParentBg(R.color.white); - ccvCust.setBtnSize(20, 20); - ccvCust.setCountViewAttr(R.color.transparent, 0, 4, 4); - } -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/dialog/DialogActivity.java b/module_main/src/main/java/com/wss/module/main/ui/dialog/DialogActivity.java deleted file mode 100644 index a80df6e..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/dialog/DialogActivity.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.wss.module.main.ui.dialog; - -import android.view.View; - -import com.wss.common.base.ActionBarActivity; -import com.wss.common.base.mvp.BasePresenter; -import com.wss.common.utils.CacheUtils; -import com.wss.common.utils.ToastUtils; -import com.wss.common.widget.dialog.AppDialog; -import com.wss.common.widget.dialog.DialogType; -import com.wss.module.main.R; -import com.wss.module.main.R2; - -import java.util.ArrayList; -import java.util.List; - -import butterknife.OnClick; - -/** - * Describe:对话框 - * Created by 吴天强 on 2018/10/26. - */ - -public class DialogActivity extends ActionBarActivity { - @Override - protected BasePresenter createPresenter() { - return null; - } - - @Override - protected int getLayoutId() { - return R.layout.main_activity_dialog; - } - - @Override - protected void initView() { - setTitleText("对话框"); - } - - /** - * 注意:lib中使用 ButterKnife 绑定事件用R2 判断 用if else 判断里面用R - */ - @OnClick({R2.id.btn_01, R2.id.btn_02, R2.id.btn_03, R2.id.btn_04, R2.id.btn_05, R2.id.btn_06}) - public void onBtnClick(View view) { - if (view.getId() == R.id.btn_01) { - new AppDialog(mContext) - .setContent("我的自定义默认对话框") - .show(); - } else if (view.getId() == R.id.btn_02) { - new AppDialog(mContext, DialogType.INPUT) - .setTitle("来一段文字") - .setLeftButton("输好了", new AppDialog.OnButtonClickListener() { - @Override - public void onClick(String val) { - ToastUtils.showToast(mContext, val); - } - }) - .setRightButton("不输了") - .show(); - } else if (view.getId() == R.id.btn_03) { - new AppDialog(mContext, DialogType.COUNT) - .setTitle("修改数量") - .setNumber(1, 10, 2) - .setLeftButton("OK", new AppDialog.OnButtonClickListener() { - @Override - public void onClick(String val) { - ToastUtils.showToast(mContext, val); - } - }) - .setLeftButtonTextColor(R.color.red) - .setRightButtonTextColor(R.color.gray) - .setRightButton("CANCEL") - .show(); - } else if (view.getId() == R.id.btn_04) { - new AppDialog(mContext) - .setTitle("单个按钮") - .setContent("这是一段没有意义的文字") - .setSingleButton() - .show(); - } else if (view.getId() == R.id.btn_05) { - new AppDialog(mContext, DialogType.NO_TITLE) - .setContent("我没有title,点我也没啥用") - .show(); - - } else if (view.getId() == R.id.btn_06) { - final List list = new ArrayList<>(); - list.add("相机"); - list.add("相册"); - - new AppDialog(mContext, DialogType.BOTTOM_IN) - .setTitle("多条目") - .setBottomItems(list, new AppDialog.OnItemClickListener() { - @Override - public void onItemClick(int position) { - ToastUtils.showToast(mContext, list.get(position)); - } - }) - .setBottomCancelText("再见") - .show(); - - } - CacheUtils.get(mContext).put("asdas", "asd"); - } - -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/flow/FlowLayoutActivity.java b/module_main/src/main/java/com/wss/module/main/ui/flow/FlowLayoutActivity.java deleted file mode 100644 index c69e25b..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/flow/FlowLayoutActivity.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.wss.module.main.ui.flow; - -import com.wss.common.base.ActionBarActivity; -import com.wss.common.base.mvp.BasePresenter; -import com.wss.module.main.R; - -/** - * Describe:流式布局 - * Created by 吴天强 on 2018/10/29. - */ - -public class FlowLayoutActivity extends ActionBarActivity { - @Override - protected BasePresenter createPresenter() { - return null; - } - - @Override - protected int getLayoutId() { - return R.layout.main_activity_flow_layout; - } - - @Override - protected void initView() { - setTitleText("流式布局"); - } -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/home/MainActivity.java b/module_main/src/main/java/com/wss/module/main/ui/home/MainActivity.java deleted file mode 100644 index c520f4f..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/home/MainActivity.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.wss.module.main.ui.home; - -import android.support.annotation.IdRes; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentTransaction; -import android.text.TextUtils; -import android.view.KeyEvent; -import android.widget.RadioGroup; -import android.widget.TextView; - -import com.wss.common.base.BaseActivity; -import com.wss.common.base.BaseApplication; -import com.wss.common.bean.Event; -import com.wss.common.constants.EventConstant; -import com.wss.common.utils.ToastUtils; -import com.wss.module.main.R; -import com.wss.module.main.R2; -import com.wss.module.main.ui.home.fragment.CenterFragment; -import com.wss.module.main.ui.home.fragment.HomeFragment; -import com.wss.module.main.ui.home.fragment.UserFragment; - -import java.util.ArrayList; -import java.util.List; - -import butterknife.BindView; - -/** - * Describe:程序入口 - * Created by 吴天强 on 2018/10/15. - */ - -public class MainActivity extends BaseActivity { - - - @BindView(R2.id.rg_main) - RadioGroup mainTab; - - @BindView(R2.id.tv_title) - TextView tvTitle; - - - private long mExitTime; - /** - * 存放切换Fragment - */ - private List mFragmentList = new ArrayList<>(); - - @Override - protected int getLayoutId() { - return R.layout.main_activity_main; - } - - @Override - protected void initView() { - changeFragment(HomeFragment.class.getSimpleName()); - mainTab.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) { - if (checkedId == R.id.rb_main) { - changeFragment(HomeFragment.class.getName()); - tvTitle.setText(R.string.main_tab_home); - } else if (checkedId == R.id.rb_center) { - changeFragment(CenterFragment.class.getName()); - tvTitle.setText(R.string.main_tab_center); - } else if (checkedId == R.id.rb_user) { - changeFragment(UserFragment.class.getName()); - tvTitle.setText(R.string.main_tab_user); - } else { - changeFragment(HomeFragment.class.getName()); - tvTitle.setText(R.string.main_tab_home); - } - - } - }); - } - - - /** - * Fragment 发生改变 - * - * @param tag Fragment 类名 - */ - public void changeFragment(String tag) { - hideFragment(); - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag); - if (fragment != null) { - transaction.show(fragment); - } else { - - if (TextUtils.equals(tag, HomeFragment.class.getName())) { - fragment = new HomeFragment(); - } else if (TextUtils.equals(tag, CenterFragment.class.getName())) { - fragment = new CenterFragment(); - } else if (TextUtils.equals(tag, UserFragment.class.getName())) { - fragment = new UserFragment(); - } else { - fragment = new HomeFragment(); - } - mFragmentList.add(fragment); - transaction.add(R.id.fl_context, fragment, fragment.getClass().getName()); - } - transaction.commitAllowingStateLoss(); - } - - - /** - * 隐藏所有Fragment - */ - private void hideFragment() { - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - for (Fragment f : mFragmentList) { - transaction.hide(f); - } - transaction.commit(); - - } - - - @Override - protected boolean regEvent() { - return true; - } - - @Override - public void onEventBus(Event event) { - if (TextUtils.equals(event.getAction(), EventConstant.EVENT_MARKET_CLICK)) { - ToastUtils.showToast(mContext,"main 模块收到"+ event.getData().toString()); - } - } - - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK) { - if ((System.currentTimeMillis() - mExitTime) > 2000) { - ToastUtils.showToast(mContext, getString(R.string.main_exit_app)); - mExitTime = System.currentTimeMillis(); - } else { - BaseApplication.getApplication().exitApp(); - } - return true; - } - return super.onKeyDown(keyCode, event); - } - -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/home/adapter/CenterRcyAdapter.java b/module_main/src/main/java/com/wss/module/main/ui/home/adapter/CenterRcyAdapter.java deleted file mode 100644 index 163202b..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/home/adapter/CenterRcyAdapter.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.wss.module.main.ui.home.adapter; - -import android.content.Context; -import android.view.View; - -import com.wss.common.base.adapter.BaseListAdapter; -import com.wss.common.listener.OnListItemClickListener; -import com.wss.module.main.R; -import com.wss.module.main.bean.MainBlock; - -import org.byteam.superadapter.SuperViewHolder; - -import java.util.List; - -/** - * Describe:中间适配器 - * Created by 吴天强 on 2018/10/18. - */ - -public class CenterRcyAdapter extends BaseListAdapter { - - - public CenterRcyAdapter(Context context, List items, int layoutResId, OnListItemClickListener listener) { - super(context, items, layoutResId, listener); - } - - @Override - public void onBind(SuperViewHolder holder, int viewType, final int layoutPosition, MainBlock item) { - holder.setText(R.id.tv_title, item.getTitle()); - holder.setText(R.id.tv_describe, item.getDescribe()); - holder.setBackgroundResource(R.id.iv_icon, item.getRes()); - holder.itemView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (listener != null) { - listener.onItemClick(layoutPosition); - } - } - }); - } -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/home/adapter/HomeRcyAdapter.java b/module_main/src/main/java/com/wss/module/main/ui/home/adapter/HomeRcyAdapter.java deleted file mode 100644 index 4b96d8b..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/home/adapter/HomeRcyAdapter.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.wss.module.main.ui.home.adapter; - -import android.content.Context; -import android.view.View; - -import com.wss.common.base.adapter.BaseListAdapter; -import com.wss.common.listener.OnListItemClickListener; -import com.wss.module.main.R; -import com.wss.module.main.bean.MainBlock; - -import org.byteam.superadapter.SuperViewHolder; - -import java.util.List; - -/** - * Describe:首页适配器 - * Created by 吴天强 on 2018/10/18. - */ - -public class HomeRcyAdapter extends BaseListAdapter { - - - public HomeRcyAdapter(Context context, List items, int layoutResId, OnListItemClickListener listener) { - super(context, items, layoutResId, listener); - } - - @Override - public void onBind(SuperViewHolder holder, int viewType, final int layoutPosition, MainBlock item) { - holder.setText(R.id.tv_text, item.getTitle()); - holder.setBackgroundResource(R.id.iv_icon, item.getRes()); - holder.itemView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (listener != null) { - listener.onItemClick(layoutPosition); - } - } - }); - } -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/home/fragment/CenterFragment.java b/module_main/src/main/java/com/wss/module/main/ui/home/fragment/CenterFragment.java deleted file mode 100644 index 7e0caa4..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/home/fragment/CenterFragment.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.wss.module.main.ui.home.fragment; - -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; - -import com.wss.common.base.BaseMvpFragment; -import com.wss.common.listener.OnListItemClickListener; -import com.wss.common.utils.ActivityToActivity; -import com.wss.module.main.R; -import com.wss.module.main.R2; -import com.wss.module.main.bean.MainBlock; -import com.wss.module.main.ui.home.adapter.CenterRcyAdapter; -import com.wss.module.main.ui.home.mvp.CenterPresenter; -import com.wss.module.main.ui.home.mvp.ICenterView; - -import java.util.ArrayList; -import java.util.List; - -import butterknife.BindView; - -/** - * Describe:中间 - * Created by 吴天强 on 2018/10/17. - */ -public class CenterFragment extends BaseMvpFragment implements ICenterView, OnListItemClickListener { - - @BindView(R2.id.recycle_view) - RecyclerView recyclerView; - - - private List data = new ArrayList<>(); - - private CenterRcyAdapter adapter; - - @Override - protected void initView() { - - adapter = new CenterRcyAdapter(mContext, data, R.layout.main_item_of_center_list, this); - recyclerView.setLayoutManager(new LinearLayoutManager(mContext)); - recyclerView.setAdapter(adapter); - presenter.getTabList(); - } - - @Override - protected int getLayoutId() { - return R.layout.main_fragment_center; - } - - @Override - protected CenterPresenter createPresenter() { - return new CenterPresenter(); - } - - @Override - public void tabList(List blockList) { - this.data.addAll(blockList); - adapter.notifyDataSetChanged(); - } - - - @SuppressWarnings("unchecked") - @Override - public void onItemClick(int position) { - MainBlock mainBlock = data.get(position); - if (mainBlock.getClazz() != null) { - ActivityToActivity.toActivity(mContext, mainBlock.getClazz()); - } - } - - -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/home/fragment/HomeFragment.java b/module_main/src/main/java/com/wss/module/main/ui/home/fragment/HomeFragment.java deleted file mode 100644 index f51e5d5..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/home/fragment/HomeFragment.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.wss.module.main.ui.home.fragment; - -import android.support.v7.widget.GridLayoutManager; -import android.support.v7.widget.RecyclerView; - -import com.alibaba.android.arouter.launcher.ARouter; -import com.wss.common.activity.WebViewActivity; -import com.wss.common.base.BaseActivity; -import com.wss.common.base.BaseMvpFragment; -import com.wss.common.listener.OnListItemClickListener; -import com.wss.common.utils.ImageUtils; -import com.wss.common.utils.ToastUtils; -import com.wss.module.main.R; -import com.wss.module.main.R2; -import com.wss.module.main.bean.BannerInfo; -import com.wss.module.main.bean.MainBlock; -import com.wss.module.main.ui.home.adapter.HomeRcyAdapter; -import com.wss.module.main.ui.home.mvp.HomePresenter; -import com.wss.module.main.ui.home.mvp.IHomeView; -import com.youth.banner.Banner; -import com.youth.banner.listener.OnBannerListener; - -import java.util.ArrayList; -import java.util.List; - -import butterknife.BindView; - -/** - * Describe:首页 - * Created by 吴天强 on 2018/10/17. - */ - -public class HomeFragment extends BaseMvpFragment implements IHomeView, OnListItemClickListener { - - @BindView(R2.id.banner) - Banner banner; - - @BindView(R2.id.recycle_view) - RecyclerView recyclerView; - - - private HomeRcyAdapter adapter; - private List data = new ArrayList<>(); - private List bannerList = new ArrayList<>(); - - @Override - protected int getLayoutId() { - return R.layout.main_fragment_main; - } - - @Override - protected void initView() { - banner.setOnBannerListener(new OnBannerListener() { - @Override - public void OnBannerClick(int position) { - //Banner点击 - WebViewActivity.actionStart(mContext, bannerList.get(position).getUrl()); - } - }); - - adapter = new HomeRcyAdapter(mContext, data, R.layout.main_item_of_block_list, this); - recyclerView.setLayoutManager(new GridLayoutManager(mContext, 4)); - recyclerView.setAdapter(adapter); - - presenter.getBanner(); - presenter.getBlock(); - } - - - @Override - protected HomePresenter createPresenter() { - return new HomePresenter(); - } - - @Override - public void bannerList(List banners) { - this.bannerList.addAll(banners); - List strings = new ArrayList<>(); - for (BannerInfo bannerInfo : banners) { - strings.add(bannerInfo.getImagePath()); - } - ImageUtils.loadBanner(banner, strings); - } - - @Override - public void blockList(List blockList) { - this.data.addAll(blockList); - //presenter中子线程模拟获取数据 这里更新需要切换到UI线程 - ((BaseActivity) mContext).runOnUiThread(new Runnable() { - @Override - public void run() { - adapter.notifyDataSetChanged(); - } - }); - } - - @Override - public void onItemClick(int position) { - MainBlock mainBlock = data.get(position); - switch (position) { - case 0: - ToastUtils.showToast(mContext, "跳转wan_android模块"); - //跳转 玩安卓 - ARouter.getInstance() - .build(mainBlock.getUrl()) - .navigation(); - break; - case 1: - ToastUtils.showToast(mContext, "跳转market模块"); - //跳转 商城 - ARouter.getInstance() - .build(mainBlock.getUrl()) - .navigation(); - break; - default: - WebViewActivity.actionStart(mContext, mainBlock.getUrl()); - break; - } - } -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/home/fragment/UserFragment.java b/module_main/src/main/java/com/wss/module/main/ui/home/fragment/UserFragment.java deleted file mode 100644 index 79b228f..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/home/fragment/UserFragment.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.wss.module.main.ui.home.fragment; - -import com.wss.module.main.R; -import com.wss.common.base.BaseFragment; - -/** - * Describe:个人中心 - * Created by 吴天强 on 2018/10/17. - */ - -public class UserFragment extends BaseFragment { - - - @Override - protected void initView() { - - } - - @Override - protected int getLayoutId() { - return R.layout.main_fragment_user; - } -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/home/mvp/CenterModule.java b/module_main/src/main/java/com/wss/module/main/ui/home/mvp/CenterModule.java deleted file mode 100644 index 36e2876..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/home/mvp/CenterModule.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.wss.module.main.ui.home.mvp; - -import com.wss.common.base.mvp.IBaseModule; - -/** - * Describe:中间Module - * Created by 吴天强 on 2018/10/22. - */ - -public class CenterModule implements IBaseModule { -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/home/mvp/CenterPresenter.java b/module_main/src/main/java/com/wss/module/main/ui/home/mvp/CenterPresenter.java deleted file mode 100644 index a63e2ca..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/home/mvp/CenterPresenter.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.wss.module.main.ui.home.mvp; - -import com.wss.common.base.mvp.BasePresenter; -import com.wss.module.main.R; -import com.wss.module.main.bean.MainBlock; -import com.wss.module.main.ui.count.CountViewActivity; -import com.wss.module.main.ui.dialog.DialogActivity; -import com.wss.module.main.ui.flow.FlowLayoutActivity; -import com.wss.module.main.ui.hortab.OrderListActivity; -import com.wss.module.main.ui.item.MultipleItemActivity; -import com.wss.module.main.ui.observer.ObserverButtonActivity; -import com.wss.module.main.ui.refresh.ArticleListActivity; -import com.wss.module.main.ui.selector.SelectorActivity; - -import java.util.ArrayList; -import java.util.List; - -/** - * Describe:中间Presenter - * Created by 吴天强 on 2018/10/22. - */ - -public class CenterPresenter extends BasePresenter { - - - public void getTabList() { - if (isViewAttached()) { - - List list = new ArrayList<>(); - list.add(new MainBlock("水平Tab", R.drawable.main_icon_8, OrderListActivity.class, "水平滑动选项卡")); - list.add(new MainBlock("下拉刷新", R.drawable.main_icon_9, ArticleListActivity.class, "下拉刷新,上拉加载更多,可自定义加载View")); - list.add(new MainBlock("多功能选择器", R.drawable.main_icon_10, SelectorActivity.class, "时间、日期、地址、自定义数据滑动选择器")); - list.add(new MainBlock("数量加减控件", R.drawable.main_icon_11, CountViewActivity.class, "数量加减、支持手动输入")); - list.add(new MainBlock("多功能横向Item", R.drawable.main_icon_12, MultipleItemActivity.class, "左右文字+Icon的Item 支持右输入框显示")); - list.add(new MainBlock("观察者按钮", R.drawable.main_icon_13, ObserverButtonActivity.class, "利用按钮对多个输入框监听,如:注册、修改信息等")); - list.add(new MainBlock("自定义多功能对话框", R.drawable.main_icon_14, DialogActivity.class, "开发中可能会遇到的对话框封装,支持输入 单按钮等")); - list.add(new MainBlock("流式布局", R.drawable.main_icon_15, FlowLayoutActivity.class, "流式布局,从左上角位置开始,自动换行")); - getView().tabList(list); - } - } - - - @Override - protected CenterModule createModule() { - return new CenterModule(); - } -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/home/mvp/HomeModule.java b/module_main/src/main/java/com/wss/module/main/ui/home/mvp/HomeModule.java deleted file mode 100644 index 8074e09..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/home/mvp/HomeModule.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.wss.module.main.ui.home.mvp; - -import android.content.Context; - -import com.tamic.novate.callback.ResponseCallback; -import com.wss.common.base.mvp.IBaseModule; -import com.wss.common.net.Api; -import com.wss.common.net.HttpUtils; - -/** - * Describe:首页Module - * Created by 吴天强 on 2018/10/17. - */ - -public class HomeModule implements IBaseModule { - - void getBanner(Context context, ResponseCallback callback) { - HttpUtils.getInstance(context) - .getRequest(Api.GET_BANNER_LIST, callback); - } - -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/home/mvp/HomePresenter.java b/module_main/src/main/java/com/wss/module/main/ui/home/mvp/HomePresenter.java deleted file mode 100644 index bf3cfa2..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/home/mvp/HomePresenter.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.wss.module.main.ui.home.mvp; - -import com.wss.common.base.mvp.BasePresenter; -import com.wss.common.constants.ARouterConfig; -import com.wss.common.constants.Constant; -import com.wss.common.net.callback.OnResultListCallBack; -import com.wss.module.main.R; -import com.wss.module.main.bean.BannerInfo; -import com.wss.module.main.bean.MainBlock; - -import java.util.ArrayList; -import java.util.List; - -/** - * Describe:首页Presenter - * Created by 吴天强 on 2018/10/17. - */ - -public class HomePresenter extends BasePresenter { - - - public void getBanner() { - if (isViewAttached()) { - getView().showLoading(); - getModule().getBanner(getView().getContext(), - new OnResultListCallBack>() { - - @Override - public void onSuccess(boolean success, int code, String msg, Object tag, List response) { - if (code == 0) { - if (response != null && response.size() > 0) { - getView().bannerList(response); - } else { - getView().onEmpty(tag); - } - } else { - getView().onError(tag, msg); - } - } - - @Override - public void onFailure(Object tag, Exception e) { - getView().onError(tag, Constant.ERROR_MESSAGE); - } - - @Override - public void onCompleted() { - getView().dismissLoading(); - } - }); - } - } - - public void getBlock() { - if (isViewAttached()) { - getView().showLoading(); - //模拟请求数据 - new Thread() { - @Override - public void run() { - super.run(); - try { - Thread.sleep(10); - List list = new ArrayList<>(); - list.add(new MainBlock("玩安卓", ARouterConfig.WAN_MAIN_ACTIVITY, R.drawable.main_icon_1)); - list.add(new MainBlock("商城", ARouterConfig.MARKET_MAIN_ACTIVITY, R.drawable.main_icon_4)); - list.add(new MainBlock("花花", "https://www.jianshu.com/p/8703343be072", R.drawable.main_icon_3)); - list.add(new MainBlock("树木", "https://www.jianshu.com/p/fadfd7965865", R.drawable.main_icon_2)); - list.add(new MainBlock("草草", "https://www.jianshu.com/p/3977a1c2d07b", R.drawable.main_icon_6)); - list.add(new MainBlock("猫猫", "https://www.jianshu.com/p/bce49dccc962", R.drawable.main_icon_5)); - getView().blockList(list); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }.start(); - } - } - - @Override - protected HomeModule createModule() { - return new HomeModule(); - } -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/home/mvp/ICenterView.java b/module_main/src/main/java/com/wss/module/main/ui/home/mvp/ICenterView.java deleted file mode 100644 index 67cc482..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/home/mvp/ICenterView.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.wss.module.main.ui.home.mvp; - -import com.wss.common.base.mvp.IBaseView; -import com.wss.module.main.bean.MainBlock; - -import java.util.List; - -/** - * Describe:中间View - * Created by 吴天强 on 2018/10/22. - */ - -public interface ICenterView extends IBaseView { - - void tabList(List blockList); -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/home/mvp/IHomeView.java b/module_main/src/main/java/com/wss/module/main/ui/home/mvp/IHomeView.java deleted file mode 100644 index 644865f..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/home/mvp/IHomeView.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.wss.module.main.ui.home.mvp; - -import com.wss.module.main.bean.BannerInfo; -import com.wss.module.main.bean.MainBlock; -import com.wss.common.base.mvp.IBaseView; - -import java.util.List; - -/** - * Describe:首页View - * Created by 吴天强 on 2018/10/17. - */ - -public interface IHomeView extends IBaseView { - - - /** - * banner列表 - */ - void bannerList(List banners); - - /** - * 模块列表 - */ - void blockList(List blockList); -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/hortab/OrderListActivity.java b/module_main/src/main/java/com/wss/module/main/ui/hortab/OrderListActivity.java deleted file mode 100644 index 63894ed..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/hortab/OrderListActivity.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.wss.module.main.ui.hortab; - -import com.wss.common.base.BaseFragment; -import com.wss.common.base.HorizontalTabActivity; -import com.wss.common.base.mvp.BasePresenter; -import com.wss.common.bean.HorizontalTabTitle; -import com.wss.module.main.ui.hortab.fragment.OrderFragment; - -import java.util.ArrayList; -import java.util.List; - -/** - * Describe:水平滑动Tab - * Created by 吴天强 on 2018/10/22. - */ - -public class OrderListActivity extends HorizontalTabActivity { - - - @Override - protected BasePresenter createPresenter() { - return null; - } - - @Override - protected void initView() { - super.initView(); - setTitleText("我的订单"); - } - - @Override - protected List getTabTitles() { - List titles = new ArrayList<>(); - titles.add(new HorizontalTabTitle<>("全部", 0)); - titles.add(new HorizontalTabTitle<>("待支付", 1)); - titles.add(new HorizontalTabTitle<>("待发货", 2)); - titles.add(new HorizontalTabTitle<>("待确认", 3)); - titles.add(new HorizontalTabTitle<>("已失效", -1)); - return titles; - } - - @Override - protected BaseFragment getTabFragment() { - return new OrderFragment(); - } - - -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/hortab/adapter/OrderListAdapter.java b/module_main/src/main/java/com/wss/module/main/ui/hortab/adapter/OrderListAdapter.java deleted file mode 100644 index d05c082..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/hortab/adapter/OrderListAdapter.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.wss.module.main.ui.hortab.adapter; - -import android.content.Context; -import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import com.wss.common.base.adapter.BaseListAdapter; -import com.wss.common.listener.OnListItemClickListener; -import com.wss.common.utils.ImageUtils; -import com.wss.module.main.R; -import com.wss.module.main.R2; -import com.wss.module.main.bean.Goods; -import com.wss.module.main.bean.Order; - -import org.byteam.superadapter.SuperViewHolder; - -import java.util.List; - -import butterknife.BindView; -import butterknife.ButterKnife; - -/** - * Describe:订单列表适配器 - * Created by 吴天强 on 2018/10/23. - */ - -public class OrderListAdapter extends BaseListAdapter { - - - public OrderListAdapter(Context context, List items, int layoutResId, OnListItemClickListener listener) { - super(context, items, layoutResId, listener); - } - - - @Override - public void onBind(SuperViewHolder holder, int viewType, final int layoutPosition, Order item) { - holder.setText(R.id.tv_date, item.getOrderDate()); - holder.setText(R.id.tv_state, getState(item.getOrderState())); - LinearLayout layout = holder.findViewById(R.id.layout_goods); - layout.removeAllViews(); - int sum = 0; - for (Goods goods : item.getGoodsList()) { - View childView = View.inflate(getContext(), R.layout.main_item_of_order_goods_list, null); - new GoodsVH(childView).bindingData(goods); - layout.addView(childView); - sum += goods.getGoodsNum(); - } - holder.setText(R.id.tv_total, String.format("共%s种商品,合计:¥%s", sum, item.getOrderTotal())); - holder.itemView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (listener != null) { - listener.onItemClick(layoutPosition); - } - } - }); - } - - public class GoodsVH { - - @BindView(R2.id.tv_goods) - ImageView ivGoods; - - @BindView(R2.id.tv_name) - TextView tvName; - - @BindView(R2.id.tv_price) - TextView tvPrice; - - @BindView(R2.id.tv_num) - TextView tvNum; - - GoodsVH(View item) { - ButterKnife.bind(this, item); - } - - void bindingData(Goods goods) { - ImageUtils.loadImage(getContext(), goods.getGoodsImg(), ivGoods); - tvName.setText(goods.getGoodsName()); - tvPrice.setText(String.format("¥%s", goods.getGoodsPrice())); - tvNum.setText(String.format("x%s", goods.getGoodsNum())); - } - } - - - private String getState(int code) { - switch (code) { - case 1: - return "待支付"; - case 2: - return "待发货"; - case 3: - return "待确认"; - case 4: - return "已失效"; - default: - return "已完成"; - } - } -} \ No newline at end of file diff --git a/module_main/src/main/java/com/wss/module/main/ui/hortab/fragment/OrderFragment.java b/module_main/src/main/java/com/wss/module/main/ui/hortab/fragment/OrderFragment.java deleted file mode 100644 index 9a22f42..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/hortab/fragment/OrderFragment.java +++ /dev/null @@ -1,124 +0,0 @@ -package com.wss.module.main.ui.hortab.fragment; - -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; - -import com.wss.common.base.RefreshListFragment; -import com.wss.common.bean.HorizontalTabTitle; -import com.wss.common.utils.ToastUtils; -import com.wss.module.main.R; -import com.wss.module.main.bean.Order; -import com.wss.module.main.ui.hortab.adapter.OrderListAdapter; -import com.wss.module.main.ui.hortab.mvp.IOrderView; -import com.wss.module.main.ui.hortab.mvp.OrderPresenter; - -import org.byteam.superadapter.SuperViewHolder; - -import java.util.ArrayList; -import java.util.List; - -/** - * Describe:订单列表 - * Created by 吴天强 on 2018/10/22. - */ - -public class OrderFragment extends RefreshListFragment implements IOrderView { - - - private int page = 0; - private HorizontalTabTitle title; - private List orderList = new ArrayList<>(); - - @Override - public void setFragmentData(Object data) { - super.setFragmentData(data); - this.title = (HorizontalTabTitle) data; - } - - @Override - protected void initView() { - super.initView(); - initData(); - } - - private void initData() { - page = 0; - orderList.clear(); - presenter.getOrderList(); - } - - @Override - public void onRefresh() { - initData(); - } - - @Override - public void onLoadMore() { - presenter.getOrderList(); - } - - @Override - public void onItemClick(int position) { - - } - - @Override - public void onError(Object tag, String errorMsg) { - super.onError(tag, errorMsg); - if (page == 0) { - showErrorView(); - } else { - ToastUtils.showToast(mContext, errorMsg); - } - } - - @Override - public void onEmpty(Object tag) { - super.onEmpty(tag); - if (page == 0) { - showEmptyView(); - } else { - ToastUtils.showToast(mContext, "暂无更多数据"); - } - } - - @Override - protected void onEmptyViewClick() { - super.onEmptyViewClick(); - initData(); - } - - @Override - protected RecyclerView.LayoutManager getLayoutManager() { - return new LinearLayoutManager(mContext); - } - - @Override - protected RecyclerView.Adapter createAdapter() { - return new OrderListAdapter(mContext, orderList, R.layout.main_item_of_order_list, this); - } - - @Override - protected OrderPresenter createPresenter() { - return new OrderPresenter(); - } - - @Override - public void orderList(List orders) { - this.orderList.addAll(orders); - adapter.notifyDataSetChanged(); - } - - @Override - public int getPage() { - return page; - } - - @Override - public int getType() { - if (title != null) { - return (int) title.getData(); - } - return 0; - } -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/hortab/mvp/IOrderView.java b/module_main/src/main/java/com/wss/module/main/ui/hortab/mvp/IOrderView.java deleted file mode 100644 index 252a3cf..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/hortab/mvp/IOrderView.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.wss.module.main.ui.hortab.mvp; - -import com.wss.common.base.mvp.IBaseView; -import com.wss.module.main.bean.Order; - -import java.util.List; - -/** - * Describe:订单View - * Created by 吴天强 on 2018/10/23. - */ - -public interface IOrderView extends IBaseView { - - void orderList(List orders); - - int getPage(); - - int getType(); -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/hortab/mvp/OrderModule.java b/module_main/src/main/java/com/wss/module/main/ui/hortab/mvp/OrderModule.java deleted file mode 100644 index a3b9500..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/hortab/mvp/OrderModule.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.wss.module.main.ui.hortab.mvp; - -import com.wss.common.base.mvp.IBaseModule; - -/** - * Describe:订单Module - * Created by 吴天强 on 2018/10/23. - */ - -public class OrderModule implements IBaseModule { -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/hortab/mvp/OrderPresenter.java b/module_main/src/main/java/com/wss/module/main/ui/hortab/mvp/OrderPresenter.java deleted file mode 100644 index 890d0b6..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/hortab/mvp/OrderPresenter.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.wss.module.main.ui.hortab.mvp; - -import com.wss.common.base.mvp.BasePresenter; -import com.wss.module.main.bean.Goods; -import com.wss.module.main.bean.Order; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -/** - * Describe:订单Presenter - * Created by 吴天强 on 2018/10/23. - */ - -public class OrderPresenter extends BasePresenter { - - - public void getOrderList() { - if (isViewAttached()) { - - if (getView().getType() == 1) { - //Error - getView().onError("", ""); - } else if (getView().getType() == 2) { - getView().onEmpty(""); - } else { - if (getView().getPage() == 3) { - //到第三页告诉他没有数据了 - getView().onEmpty(""); - } else { - getView().orderList(getData()); - getView().dismissLoading(); - } - } - - } - - } - - private List getData() { - List list = new ArrayList<>(); - for (int i = 0; i < 10; i++) { - Order order = new Order(); - order.setOrderDate("2018年10月23日18:39:41"); - order.setOrderTotal(String.valueOf(6.28 * (i + 1))); - order.setGoodsList(getGoods()); - order.setOrderState(getView().getType() != 0 ? getView().getType() : new Random().nextInt(4) + 1); - list.add(order); - } - return list; - } - - private List getGoods() { - List list = new ArrayList<>(); - int max = new Random().nextInt(5) + 1; - for (int i = 1; i <= max; i++) { - Goods goods = new Goods(); - goods.setGoodsName(String.format("这是第%s页的第%s个商品", getView().getPage() + 1, i)); - goods.setGoodsNum(i * max + 1); - goods.setGoodsPrice(String.valueOf(3 * max + 0.28)); - goods.setGoodsImg("https://img14.360buyimg.com/n7/jfs/t20509/311/508682869/290069/4d2c41e/5b0fcab1N217bcf3d.jpg"); - list.add(goods); - } - - return list; - } - - - @Override - protected OrderModule createModule() { - return new OrderModule(); - } -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/loading/LoadingActivity.java b/module_main/src/main/java/com/wss/module/main/ui/loading/LoadingActivity.java deleted file mode 100644 index daa406b..0000000 --- a/module_main/src/main/java/com/wss/module/main/ui/loading/LoadingActivity.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.wss.module.main.ui.loading; - -import android.annotation.SuppressLint; -import android.content.Intent; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.support.annotation.Nullable; -import android.view.WindowManager; - -import com.wss.common.base.BaseActivity; -import com.wss.module.main.R; -import com.wss.module.main.ui.home.MainActivity; - -/** - * Describe:应用启动页 - * Created by 吴天强 on 2018/10/16. - */ - -public class LoadingActivity extends BaseActivity { - - private long loadingTime = 1500; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); -// if (!UserUtils.getInstance().isFirstBoot()) { -// GuideActivity.actionStart(mContext); -// finish(); -// } else { -// handler.sendEmptyMessageDelayed(0, loadingTime); -// } -// CountryDaoUtils.initialization(); - - handler.sendEmptyMessageDelayed(0, loadingTime); - } - - - @SuppressLint("HandlerLeak") - private Handler handler = new Handler() { - @Override - public void handleMessage(Message msg) { - super.handleMessage(msg); - startActivity(new Intent(mContext, MainActivity.class)); - finish(); - } - }; - - @Override - protected void initView() { - - } - - @Override - protected int getLayoutId() { - return R.layout.main_activity_loading; - } -} diff --git a/module_main/src/main/java/com/wss/module/main/ui/main/MainActivity.java b/module_main/src/main/java/com/wss/module/main/ui/main/MainActivity.java new file mode 100644 index 0000000..484cbb6 --- /dev/null +++ b/module_main/src/main/java/com/wss/module/main/ui/main/MainActivity.java @@ -0,0 +1,141 @@ +package com.wss.module.main.ui.main; + +import android.text.TextUtils; +import android.view.KeyEvent; +import android.widget.RadioGroup; + +import com.wss.common.base.BaseActionBarActivity; +import com.wss.common.base.BaseApplication; +import com.wss.common.base.BaseFragment; +import com.wss.common.base.mvp.BasePresenter; +import com.wss.common.constants.ARouterConfig; +import com.wss.common.utils.ARouterUtils; +import com.wss.common.utils.ToastUtils; +import com.wss.module.main.R; +import com.wss.module.main.R2; +import com.wss.module.main.ui.main.fragment.CaseFragment; + +import java.util.ArrayList; +import java.util.List; + +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentTransaction; +import butterknife.BindView; + +/** + * Describe:首页 + * Created by 吴天强 on 2018/10/15. + */ +public class MainActivity extends BaseActionBarActivity { + + + @BindView(R2.id.rg_main) + RadioGroup mainTab; + + private long mExitTime; + /** + * 存放切换Fragment + */ + private List mFragmentList = new ArrayList<>(); + + /** + * 玩android模块Fragment + */ + private BaseFragment wanFragment = ARouterUtils.getFragment(ARouterConfig.WAN_MAIN_FRAGMENT); + + /** + * 我的模块Fragment + */ + private BaseFragment userFragment = ARouterUtils.getFragment(ARouterConfig.USER_MAIN_FRAGMENT); + + @Override + protected int getLayoutId() { + return R.layout.main_activity_main; + } + + @Override + protected void initView() { + changeFragment(CaseFragment.class.getName()); + getTitleBar().showBackImg(false); + setCenterText(R.string.main_tab_center); + mainTab.setOnCheckedChangeListener((group, checkedId) -> { + if (checkedId == R.id.rb_main) { + changeFragment(ARouterConfig.WAN_MAIN_FRAGMENT); + setCenterText(R.string.main_tab_home); + } else if (checkedId == R.id.rb_center) { + changeFragment(CaseFragment.class.getName()); + setCenterText(R.string.main_tab_center); + } else if (checkedId == R.id.rb_user) { + changeFragment(ARouterConfig.USER_MAIN_FRAGMENT); + setCenterText(R.string.main_tab_user); + } else { + changeFragment(ARouterConfig.WAN_MAIN_FRAGMENT); + setCenterText(R.string.main_tab_home); + } + + }); + } + + + /** + * Fragment 发生改变 + * + * @param tag Fragment 类名 + */ + public void changeFragment(String tag) { + hideFragment(); + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag); + if (fragment != null) { + transaction.show(fragment); + } else { + if (TextUtils.equals(tag, ARouterConfig.WAN_MAIN_FRAGMENT)) { + fragment = wanFragment; + } else if (TextUtils.equals(tag, CaseFragment.class.getName())) { + fragment = new CaseFragment(); + } else if (TextUtils.equals(tag, ARouterConfig.USER_MAIN_FRAGMENT)) { + fragment = userFragment; + } else { + fragment = new CaseFragment(); + } + if (fragment == null) { + return; + } + mFragmentList.add(fragment); + transaction.add(R.id.fl_context, fragment, tag); + } + transaction.commitAllowingStateLoss(); + } + + + /** + * 隐藏所有Fragment + */ + private void hideFragment() { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + for (Fragment f : mFragmentList) { + transaction.hide(f); + } + transaction.commit(); + + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + if ((System.currentTimeMillis() - mExitTime) > 2000) { + ToastUtils.show(context, getString(R.string.main_exit_app)); + mExitTime = System.currentTimeMillis(); + } else { + BaseApplication.i().exitApp(); + } + return true; + } + return super.onKeyDown(keyCode, event); + } + + @Override + protected BasePresenter createPresenter() { + return null; + } +} diff --git a/module_main/src/main/java/com/wss/module/main/ui/main/adapter/CaseAdapter.java b/module_main/src/main/java/com/wss/module/main/ui/main/adapter/CaseAdapter.java new file mode 100644 index 0000000..623a3f3 --- /dev/null +++ b/module_main/src/main/java/com/wss/module/main/ui/main/adapter/CaseAdapter.java @@ -0,0 +1,29 @@ +package com.wss.module.main.ui.main.adapter; + +import android.content.Context; + +import com.wss.common.base.adapter.BaseListAdapter; +import com.wss.common.base.adapter.listener.OnListItemClickListener; +import com.wss.common.bean.Template; +import com.wss.module.main.R; + +import org.byteam.superadapter.SuperViewHolder; + +import java.util.List; + +/** + * Describe:案例适配器 + * Created by 吴天强 on 2018/10/18. + */ +public class CaseAdapter extends BaseListAdapter