├── .gitignore ├── .idea ├── gradle.xml ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── senon │ │ └── modularapp │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── senon │ │ │ └── modularapp │ │ │ ├── App.java │ │ │ ├── MainActivity.java │ │ │ └── TestActivity.java │ └── res │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_test.xml │ │ ├── pop_item.xml │ │ └── pop_test.xml │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── senon │ └── modularapp │ └── ExampleUnitTest.java ├── build.gradle ├── dependencies.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── lib_common ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── senon │ │ └── lib_common │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── senon │ │ │ └── lib_common │ │ │ ├── AppConfig.java │ │ │ ├── ConstantArouter.java │ │ │ ├── ConstantLoginArouter.java │ │ │ ├── adapter │ │ │ ├── CommonAdapter.java │ │ │ ├── CommonHolder.java │ │ │ ├── RecycleHolder.java │ │ │ └── RecyclerAdapter.java │ │ │ ├── api │ │ │ └── BaseApi.java │ │ │ ├── base │ │ │ ├── BaseActivity.java │ │ │ ├── BaseAppDeletage.java │ │ │ ├── BaseApplication.java │ │ │ ├── BaseEvent.java │ │ │ ├── BaseFragment.java │ │ │ ├── BaseLazyFragment.java │ │ │ ├── BaseModelCallBack.java │ │ │ ├── BaseNestingLazyFragment.java │ │ │ ├── BasePresenter.java │ │ │ ├── BaseResponse.java │ │ │ └── BaseViewImp.java │ │ │ ├── bean │ │ │ ├── Chapter.java │ │ │ └── Login.java │ │ │ ├── common │ │ │ ├── contract │ │ │ │ ├── LoginContract.java │ │ │ │ └── ModelRequestContract.java │ │ │ ├── model │ │ │ │ └── ModelRequestMod.java │ │ │ ├── presenter │ │ │ │ ├── LoginPresenter.java │ │ │ │ └── ModelRequestPresenter.java │ │ │ └── ui │ │ │ │ ├── LoginActivity.java │ │ │ │ ├── ModelRequestActivity.java │ │ │ │ └── RegisterActivity.java │ │ │ ├── net │ │ │ ├── RequestInterceptor.java │ │ │ ├── ServerUtils.java │ │ │ ├── callback │ │ │ │ ├── ErrorListener.java │ │ │ │ ├── RequestCallback.java │ │ │ │ └── RxErrorHandler.java │ │ │ └── progress │ │ │ │ ├── ProgressCancelListener.java │ │ │ │ └── ProgressDialogHandler.java │ │ │ └── utils │ │ │ ├── ActivityCollector.java │ │ │ ├── ComUtil.java │ │ │ ├── ConstantUtils.java │ │ │ ├── JsonServiceImpl.java │ │ │ ├── LogUtils.java │ │ │ ├── MD5Utils.java │ │ │ ├── PreferenceTool.java │ │ │ ├── RetryWithDelay.java │ │ │ ├── RxUtils.java │ │ │ ├── StatusBarUtils.java │ │ │ └── ToastUtil.java │ └── res │ │ ├── drawable │ │ ├── common_ic_launcher_background.xml │ │ └── common_toast_shape.xml │ │ ├── layout │ │ ├── common_activity_login.xml │ │ ├── common_activity_modelrequest.xml │ │ ├── common_activity_register.xml │ │ └── common_toast_layout.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── senon │ └── lib_common │ └── ExampleUnitTest.java ├── lib_opensource ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── senon │ │ └── lib_opensource │ │ └── ExampleInstrumentedTest.java │ ├── main │ └── AndroidManifest.xml │ └── test │ └── java │ └── com │ └── senon │ └── lib_opensource │ └── ExampleUnitTest.java ├── module_one ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── senon │ │ └── module_one │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── debug │ │ └── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── senon │ │ │ └── module_one │ │ │ ├── App_One.java │ │ │ ├── MainActivity.java │ │ │ └── TestActivity.java │ ├── release │ │ └── AndroidManifest.xml │ └── res │ │ ├── drawable │ │ └── one_ic_launcher_background.xml │ │ ├── layout │ │ ├── one_activity_main.xml │ │ └── one_activity_test.xml │ │ ├── mipmap-xhdpi │ │ ├── one_ic_launcher.png │ │ └── one_ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── one_ic_launcher.png │ │ └── one_ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── senon │ └── module_one │ └── ExampleUnitTest.java ├── module_two ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── senon │ │ └── module_two │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── debug │ │ └── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── senon │ │ │ └── module_two │ │ │ ├── App_Two.java │ │ │ ├── MainActivity.java │ │ │ └── TestActivity.java │ ├── release │ │ └── AndroidManifest.xml │ └── res │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── two_activity_main.xml │ │ └── two_activity_test.xml │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── senon │ └── module_two │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 34 | 35 | 36 | 37 | 1.8 38 | 39 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.android["compileSdkVersion"] 5 | 6 | defaultConfig { 7 | applicationId "com.senon.modularapp" 8 | minSdkVersion rootProject.ext.android["minSdkVersion"] 9 | targetSdkVersion rootProject.ext.android["targetSdkVersion"] 10 | versionCode rootProject.ext.android["versionCode"] 11 | versionName rootProject.ext.android["versionName"] 12 | 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | javaCompileOptions { 15 | annotationProcessorOptions { 16 | arguments = [AROUTER_MODULE_NAME: project.getName()] 17 | } 18 | } 19 | } 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | } 27 | 28 | dependencies { 29 | if (isBuildModule.toBoolean()) { 30 | implementation project(':lib_common') 31 | } else { 32 | implementation project(':module_one') 33 | implementation project(':module_two') 34 | } 35 | 36 | annotationProcessor rootProject.ext.dependencies["butterknife-compiler"] 37 | annotationProcessor rootProject.ext.dependencies["router-compiler"] 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/senon/modularapp/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.senon.modularapp; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.senon.modularapp", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/senon/modularapp/App.java: -------------------------------------------------------------------------------- 1 | package com.senon.modularapp; 2 | 3 | import com.alibaba.android.arouter.launcher.ARouter; 4 | import com.senon.lib_common.base.BaseApplication; 5 | import com.senon.lib_common.utils.ConstantUtils; 6 | 7 | /** 8 | * 工程Application 9 | */ 10 | public class App extends BaseApplication { 11 | @Override 12 | public void onCreate() { 13 | super.onCreate(); 14 | 15 | initARouter(); 16 | } 17 | 18 | private void initARouter() { 19 | if (ConstantUtils.isAppDebug()) { 20 | //开启InstantRun之后,一定要在ARouter.init之前调用openDebug 21 | ARouter.openDebug(); 22 | ARouter.openLog(); 23 | } 24 | ARouter.init(this); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/senon/modularapp/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.senon.modularapp; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.widget.TextView; 6 | import com.alibaba.android.arouter.facade.annotation.Autowired; 7 | import com.alibaba.android.arouter.facade.annotation.Route; 8 | import com.alibaba.android.arouter.launcher.ARouter; 9 | import com.senon.lib_common.ConstantLoginArouter; 10 | import com.senon.lib_common.bean.Login; 11 | import com.senon.lib_common.utils.ComUtil; 12 | import com.senon.lib_common.utils.StatusBarUtils; 13 | 14 | /** 15 | * app 模块主页面 16 | */ 17 | @Route(path = ConstantLoginArouter.PATH_APP_MAINACTIVITY) 18 | public class MainActivity extends AppCompatActivity { 19 | 20 | @Autowired 21 | Login data; 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | StatusBarUtils.with(this).init();//沉浸式 27 | setContentView(R.layout.activity_main); 28 | ComUtil.changeStatusBarTextColor(this,true); 29 | 30 | ARouter.getInstance().inject(this); 31 | 32 | if(data == null){ 33 | ((TextView)findViewById(R.id.main_tv)).setText("这是app模块 主页面MainActivity"+"\n没有携带参数: "); 34 | }else{ 35 | ((TextView)findViewById(R.id.main_tv)).setText("这是app模块 主页面MainActivity"+"\n携带参数: "+data.toString()); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/senon/modularapp/TestActivity.java: -------------------------------------------------------------------------------- 1 | package com.senon.modularapp; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.WindowManager; 8 | import android.widget.ListView; 9 | import android.widget.PopupWindow; 10 | import com.alibaba.android.arouter.facade.annotation.Route; 11 | import com.alibaba.android.arouter.launcher.ARouter; 12 | import com.senon.lib_common.ConstantArouter; 13 | import com.senon.lib_common.ConstantLoginArouter; 14 | import com.senon.lib_common.adapter.CommonAdapter; 15 | import com.senon.lib_common.adapter.CommonHolder; 16 | import com.senon.lib_common.utils.ComUtil; 17 | import com.senon.lib_common.utils.StatusBarUtils; 18 | import java.util.ArrayList; 19 | import java.util.Map; 20 | 21 | /** 22 | * app test页面 23 | */ 24 | @Route(path = ConstantArouter.PATH_APP_TESTACTIVITY) 25 | public class TestActivity extends AppCompatActivity { 26 | 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | StatusBarUtils.with(this).init(); 31 | setContentView(R.layout.activity_test); 32 | ComUtil.changeStatusBarTextColor(this,true); 33 | 34 | } 35 | 36 | public void toA(View view){ 37 | // 应用内简单的跳转 38 | // String curUrl = ConstantLoginArouter.getCurRouter(this.getClass().getSimpleName()); 39 | // String curUrl = ConstantLoginArouter.getCurRouter(ConstantLoginArouter.PATH_HOME_MAINACTIVITY); 40 | // ARouter.getInstance().build(ConstantLoginArouter.PATH_COMMON_LOGINACTIVITY)//指定跳到那个页面 41 | // .withString("targetUrl", ConstantLoginArouter.PATH_APP_MAINACTIVITY)//传入目标页面路由地址 可以在指定页面跳入到目标页面 42 | // .navigation(); 43 | 44 | showPopwindow(view,ConstantLoginArouter.PATH_COMMON_LOGINACTIVITY); 45 | 46 | // 进阶用法 47 | // Uri testUriMix = Uri.parse("router://com.senon.firstmoduel/firstmoduel/firstmainactivity"); 48 | // ARouter.getInstance().build(testUriMix) 49 | // .withString("key1", "value1") 50 | // .navigation(); 51 | } 52 | 53 | public void toB(View view){ 54 | showPopwindow(view,null); 55 | } 56 | 57 | private void showPopwindow(View v,final String gotoUrl){ 58 | View view = LayoutInflater.from(this).inflate(R.layout.pop_test,null); 59 | final PopupWindow pop = new PopupWindow(view,WindowManager.LayoutParams.WRAP_CONTENT, 60 | WindowManager.LayoutParams.WRAP_CONTENT); 61 | ListView lv = view.findViewById(R.id.pop_lv); 62 | lv.setAdapter(new CommonAdapter(this,getData(),R.layout.pop_item) { 63 | @Override 64 | public void convert(CommonHolder helper, final String item, final int position) { 65 | helper.setText(R.id.item_tv,position==0?item:item.substring(item.lastIndexOf("/") + 1)); 66 | helper.setOnClickListener(R.id.item_tv,new View.OnClickListener() { 67 | @Override 68 | public void onClick(View v) { 69 | if(position == 0){ 70 | return; 71 | } 72 | if(gotoUrl == null){ 73 | ARouter.getInstance().build(item) 74 | .navigation(); 75 | }else{ 76 | ARouter.getInstance().build(gotoUrl)//指定跳到那个页面 77 | .withString("targetUrl",item)//传入目标页面路由地址 可以在指定页面跳入到目标页面 78 | .navigation(); 79 | } 80 | pop.dismiss(); 81 | } 82 | }); 83 | } 84 | }); 85 | //(注意一下!!)如果不设置popupWindow的背景,无论是点击外部区域还是Back键都无法 86 | pop.setBackgroundDrawable(null); 87 | //点击外部收起 88 | pop.setOutsideTouchable(true); 89 | 90 | pop.showAsDropDown(v); 91 | 92 | } 93 | 94 | private ArrayList getData(){ 95 | ArrayList list = new ArrayList<>(); 96 | for (Map.Entry entry : ConstantLoginArouter.activityRouterMap.entrySet()) { 97 | if(entry.getValue().contains(ConstantLoginArouter.PATH_COMMON_LOGINACTIVITY)){ 98 | continue; 99 | } 100 | list.add(entry.getValue()); 101 | } 102 | list.add(0,"选择目标页面"); 103 | return list; 104 | } 105 | 106 | public void modelRequest(View view) { 107 | ARouter.getInstance().build(ConstantArouter.PATH_MODELREQUESTACTIVITY)//指定跳到那个页面 108 | .navigation(); 109 | } 110 | 111 | } 112 | 113 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_test.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 20 | 31 | 32 | 43 | 44 | -------------------------------------------------------------------------------- /app/src/main/res/layout/pop_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/pop_test.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senonwx/ModularApp/2e028761a531d0c3ed76dc4dc21c1bdc177c8e0d/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senonwx/ModularApp/2e028761a531d0c3ed76dc4dc21c1bdc177c8e0d/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senonwx/ModularApp/2e028761a531d0c3ed76dc4dc21c1bdc177c8e0d/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senonwx/ModularApp/2e028761a531d0c3ed76dc4dc21c1bdc177c8e0d/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ModularApp 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/senon/modularapp/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.senon.modularapp; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | apply from: "dependencies.gradle" 3 | 4 | buildscript { 5 | 6 | repositories { 7 | google() 8 | jcenter() 9 | } 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:3.2.1' 12 | 13 | 14 | // NOTE: Do not place your application dependencies here; they belong 15 | // in the individual module build.gradle files 16 | } 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | google() 22 | jcenter() 23 | } 24 | } 25 | 26 | task clean(type: Delete) { 27 | delete rootProject.buildDir 28 | } 29 | -------------------------------------------------------------------------------- /dependencies.gradle: -------------------------------------------------------------------------------- 1 | def supportVersion = "27.1.1" 2 | def rxBindingVersion = "3.0.0-alpha1" 3 | def retrofitVersion = "2.4.0" 4 | def okHttpVersion = "3.11.0" 5 | def butterKnifeVersion = "8.5.1" 6 | def daggerVersion = "2.8" 7 | 8 | project.ext { 9 | android = [ 10 | compileSdkVersion: 27, 11 | minSdkVersion : 19, 12 | targetSdkVersion : 27, 13 | versionCode : 1, 14 | versionName : "1.0" 15 | ] 16 | 17 | dependencies = [ 18 | //android-support 19 | "support-v4" : "com.android.support:support-v4:${supportVersion}", 20 | "appcompat-v7" : "com.android.support:appcompat-v7:${supportVersion}", 21 | "design" : "com.android.support:design:${supportVersion}", 22 | "recyclerview-v7" : "com.android.support:recyclerview-v7:${supportVersion}", 23 | "cardview-v7" : "com.android.support:cardview-v7:${supportVersion}", 24 | "constraint-layout" : "com.android.support.constraint:constraint-layout:1.1.3", 25 | 26 | //java8-support 27 | "stream" : "com.annimon:stream:1.0.8", 28 | 29 | //rx 30 | "rxjava" : "io.reactivex.rxjava2:rxjava:2.2.3", 31 | "rxandroid" : "io.reactivex.rxjava2:rxandroid:2.1.0", 32 | "rxlifecycle" : "com.trello.rxlifecycle2:rxlifecycle:2.2.1", 33 | "rxlifecycle-components" : "com.trello.rxlifecycle2:rxlifecycle-components:2.2.1", 34 | "rxbinding" : "com.jakewharton.rxbinding3:rxbinding-core:${rxBindingVersion}", 35 | "rxbinding-appcompat-v7" : "com.jakewharton.rxbinding3:rxbinding-appcompat:${rxBindingVersion}", 36 | "rxbinding-recyclerview-v7" : "com.jakewharton.rxbinding3:rxbinding-recyclerview:${rxBindingVersion}", 37 | 38 | //retrofit 39 | "retrofit" : "com.squareup.retrofit2:retrofit:${retrofitVersion}", 40 | "adapter-rxjava" : "com.squareup.retrofit2:adapter-rxjava2:${retrofitVersion}", 41 | "retrofit-converter" : "com.squareup.retrofit2:converter-scalars:${retrofitVersion}", 42 | "retrofit-converter-gson" : "com.squareup.retrofit2:converter-gson:${retrofitVersion}", 43 | "gson" : "com.google.code.gson:gson:2.8.5", 44 | 45 | //dagger 46 | "dagger" : "com.google.dagger:dagger:${daggerVersion}", 47 | "dagger-compiler" : "com.google.dagger:dagger-compiler:${daggerVersion}", 48 | 49 | //router 50 | // 替换成最新版本, 需要注意的是api 51 | // 要与compiler匹配使用,均使用最新版可以保证兼容 52 | "router" : "com.alibaba:arouter-api:1.4.1", 53 | "router-compiler" : "com.alibaba:arouter-compiler:1.2.2", 54 | 55 | //butterKnife 子模块的libary与application转化 R 与R2文件转化(所以最好不用) 56 | //https://www.jianshu.com/p/1fa69ad55b0e 57 | "butterknife" : "com.jakewharton:butterknife:${butterKnifeVersion}", 58 | "butterknife-compiler" : "com.jakewharton:butterknife-compiler:${butterKnifeVersion}", 59 | 60 | //okHttp3 61 | "okhttp3" : "com.squareup.okhttp3:okhttp:${okHttpVersion}", 62 | "okhttp3-logging-interceptor" : "com.squareup.okhttp3:logging-interceptor:${okHttpVersion}", 63 | 64 | //test 65 | "junit" : "junit:junit:4.12", 66 | 67 | //sweet alert dialog 68 | "sweetalert" : "com.github.f0ris.sweetalert:library:1.5.1", 69 | 70 | //autosize 71 | "autosize" : "me.jessyan:autosize:1.0.6", 72 | "glide" : "com.github.bumptech.glide:glide:3.7.0", 73 | "eventbus" : "org.greenrobot:eventbus:3.1.1", 74 | 75 | 76 | ] 77 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senonwx/ModularApp/2e028761a531d0c3ed76dc4dc21c1bdc177c8e0d/gradle.properties -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senonwx/ModularApp/2e028761a531d0c3ed76dc4dc21c1bdc177c8e0d/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Jan 03 09:40:54 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /lib_common/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /lib_common/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.android["compileSdkVersion"] 5 | 6 | defaultConfig { 7 | minSdkVersion rootProject.ext.android["minSdkVersion"] 8 | targetSdkVersion rootProject.ext.android["targetSdkVersion"] 9 | versionCode rootProject.ext.android["versionCode"] 10 | versionName rootProject.ext.android["versionName"] 11 | 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | 14 | javaCompileOptions { 15 | annotationProcessorOptions { 16 | arguments = [AROUTER_MODULE_NAME: project.getName()] 17 | } 18 | } 19 | } 20 | 21 | buildTypes { 22 | release { 23 | minifyEnabled false 24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 25 | } 26 | } 27 | 28 | } 29 | 30 | dependencies { 31 | implementation fileTree(dir: 'libs', include: ['*.jar']) 32 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 33 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 34 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 35 | testImplementation rootProject.ext.dependencies["junit"] 36 | 37 | 38 | api project(':lib_opensource') 39 | annotationProcessor rootProject.ext.dependencies["router-compiler"] 40 | 41 | } 42 | -------------------------------------------------------------------------------- /lib_common/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /lib_common/src/androidTest/java/com/senon/lib_common/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.senon.lib_common", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib_common/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 10 | 11 | 12 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/AppConfig.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common; 2 | 3 | 4 | /** 5 | * APP配置参数 6 | */ 7 | public class AppConfig { 8 | 9 | 10 | public static final String BASE_URL = "https://www.wanandroid.com/"; 11 | 12 | 13 | } 14 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/ConstantArouter.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common; 2 | 3 | /** 4 | * 所有模块均依赖commonmoduel 所以路由跳转均写入本Constant方便调用 5 | * 常量类 6 | * 其中: 路由跳转命名统一用:path+模块名+Activity名 7 | */ 8 | public class ConstantArouter { 9 | 10 | /** 11 | * App 模块 12 | */ 13 | public static final String PATH_APP_TESTACTIVITY = "/app/TestActivity"; 14 | 15 | 16 | /** 17 | * home 主页 18 | */ 19 | public static final String PATH_HOME_MAINACTIVITY = "/home/MainActivity"; 20 | 21 | 22 | /** 23 | * life 知识体系 24 | */ 25 | public static final String PATH_LIFE_KNOWLEDGESYSTEMACTIVITY = "/life/KnowledgeSystemActivity"; 26 | 27 | /** 28 | * common 29 | */ 30 | public static final String PATH_COMMON_REGISTERACTIVITY = "/lib_common/CommonRegisterActivity"; 31 | public static final String PATH_COMMON_WEBVIEWCTIVITY = "/lib_common/CommonWebviewActivity"; 32 | public static final String PATH_MODELREQUESTACTIVITY = "/lib_common/ModelRequestActivity"; 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/ConstantLoginArouter.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * 存储各个主模块首页的路由地址:判断是哪个模块登录的 8 | * 当然也可以不用单独从ConstantArouter分离出这些主页地址,看个人喜好 9 | */ 10 | public class ConstantLoginArouter { 11 | 12 | public static Map activityRouterMap = new HashMap<>(); 13 | 14 | //app主页 15 | public static final String PATH_APP_MAINACTIVITY = "/app/AppMainActivity"; 16 | 17 | //one主页 18 | public static final String PATH_ONE_MAINACTIVITY = "/one/OneMainActivity"; 19 | //two首页 20 | public static final String PATH_TWO_MAINACTIVITY = "/two/TwoMainActivity"; 21 | 22 | //登录 注册 23 | public static final String PATH_COMMON_LOGINACTIVITY = "/lib_common/LoginActivity"; 24 | 25 | static { 26 | activityRouterMap.put(getActivityName(PATH_COMMON_LOGINACTIVITY), PATH_COMMON_LOGINACTIVITY); 27 | 28 | activityRouterMap.put(getActivityName(PATH_APP_MAINACTIVITY), PATH_APP_MAINACTIVITY); 29 | activityRouterMap.put(getActivityName(PATH_ONE_MAINACTIVITY), PATH_ONE_MAINACTIVITY); 30 | activityRouterMap.put(getActivityName(PATH_TWO_MAINACTIVITY), PATH_TWO_MAINACTIVITY); 31 | } 32 | 33 | private static String getActivityName(String routerUrl) { 34 | int pos = routerUrl.lastIndexOf("/"); 35 | return routerUrl.substring(pos + 1); 36 | } 37 | 38 | public static String getCurRouter(String activityName) { 39 | return activityRouterMap.get(activityName); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/adapter/CommonAdapter.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.adapter; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.BaseAdapter; 8 | import java.util.List; 9 | 10 | /** 11 | * 做为listview 万能适配器 12 | * @param 13 | */ 14 | public abstract class CommonAdapter extends BaseAdapter { 15 | protected LayoutInflater mInflater; 16 | protected Context mContext; 17 | protected List mDatas; 18 | protected final int mItemLayoutId; 19 | 20 | public CommonAdapter(Context context, List mDatas, int itemLayoutId) { 21 | mInflater = LayoutInflater.from(context); 22 | this.mContext = context; 23 | this.mDatas = mDatas; 24 | this.mItemLayoutId = itemLayoutId; 25 | } 26 | 27 | @Override 28 | public int getCount() { 29 | return mDatas.size(); 30 | } 31 | 32 | @Override 33 | public T getItem(int position) { 34 | return mDatas.get(position); 35 | } 36 | 37 | 38 | @Override 39 | public long getItemId(int position) { 40 | return position; 41 | } 42 | 43 | @Override 44 | public View getView(int position, View convertView, ViewGroup parent) { 45 | final CommonHolder viewHolder = getViewHolder(position, convertView, parent); 46 | convert(viewHolder, getItem(position),position); 47 | return viewHolder.getConvertView(); 48 | 49 | } 50 | 51 | public abstract void convert(CommonHolder helper, T item, int position); 52 | 53 | private CommonHolder getViewHolder(int position, View convertView, ViewGroup parent) { 54 | return CommonHolder.get(mContext, convertView, parent, mItemLayoutId, position); 55 | } 56 | } -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/adapter/RecycleHolder.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.adapter; 2 | 3 | import android.app.Activity; 4 | import android.graphics.Bitmap; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.text.Spannable; 7 | import android.text.SpannableString; 8 | import android.text.SpannableStringBuilder; 9 | import android.text.style.AbsoluteSizeSpan; 10 | import android.text.style.ForegroundColorSpan; 11 | import android.util.SparseArray; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.widget.Checkable; 15 | import android.widget.ImageView; 16 | import android.widget.ProgressBar; 17 | import android.widget.TextView; 18 | 19 | import com.bumptech.glide.Glide; 20 | 21 | /** 22 | * recyclerview 万能适配器的 ViewHolder 23 | */ 24 | public class RecycleHolder extends RecyclerView.ViewHolder { 25 | 26 | /** 27 | * 用于存储当前item当中的View 28 | */ 29 | private SparseArray mViews; 30 | 31 | public RecycleHolder(View itemView) { 32 | super(itemView); 33 | mViews = new SparseArray(); 34 | } 35 | 36 | public T findView(int ViewId) { 37 | View view = mViews.get(ViewId); 38 | //集合中没有,则从item当中获取,并存入集合当中 39 | if (view == null) { 40 | view = itemView.findViewById(ViewId); 41 | mViews.put(ViewId, view); 42 | } 43 | return (T) view; 44 | } 45 | 46 | public RecycleHolder setOnClickListener(int viewId, View.OnClickListener listener) { 47 | View view = findView(viewId); 48 | view.setOnClickListener(listener); 49 | return this; 50 | } 51 | 52 | public RecycleHolder setOnClickListener(int viewId, int viewId2 , View.OnClickListener listener) { 53 | View view = findView(viewId); 54 | view.setOnClickListener(listener); 55 | 56 | View view2 = findView(viewId2); 57 | view2.setOnClickListener(listener); 58 | return this; 59 | } 60 | 61 | public RecycleHolder setOnLongClickListener(int viewId, View.OnLongClickListener listener) { 62 | View view = findView(viewId); 63 | view.setOnLongClickListener(listener); 64 | return this; 65 | } 66 | 67 | public RecycleHolder setOnLongClickListener(int viewId, int viewId2, View.OnLongClickListener listener) { 68 | View view = findView(viewId); 69 | view.setOnLongClickListener(listener); 70 | 71 | View view2 = findView(viewId2); 72 | view2.setOnLongClickListener(listener); 73 | return this; 74 | } 75 | 76 | public RecycleHolder setText(int viewId, String text) { 77 | TextView tv = findView(viewId); 78 | tv.setText(text); 79 | return this; 80 | } 81 | 82 | public RecycleHolder setText(int viewId, SpannableStringBuilder text) { 83 | TextView tv = findView(viewId); 84 | tv.setText(text); 85 | return this; 86 | } 87 | 88 | public RecycleHolder setMaxLine(int viewId, int lineCount) { 89 | TextView tv = findView(viewId); 90 | tv.setMaxLines(lineCount); 91 | return this; 92 | } 93 | 94 | public RecycleHolder setPadding(int viewId, int left, int top, int right, int bottom) { 95 | View tv = findView(viewId); 96 | tv.setPadding(left, top, right, bottom); 97 | return this; 98 | } 99 | 100 | public RecycleHolder setAppendTextColor(int viewId, CharSequence str, int resColorId, int resDimen) { 101 | TextView tv = findView(viewId); 102 | SpannableString spannableString = new SpannableString(str); 103 | spannableString.setSpan(new ForegroundColorSpan(tv.getContext().getResources().getColor(resColorId)), 0, str.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 104 | spannableString.setSpan(new AbsoluteSizeSpan((int) tv.getContext().getResources().getDimension(resDimen)), 0, str.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 105 | tv.append(spannableString); 106 | return this; 107 | } 108 | 109 | public RecycleHolder setSelected(int viewId, boolean selected) { 110 | View tv = findView(viewId); 111 | tv.setSelected(selected); 112 | return this; 113 | } 114 | 115 | public RecycleHolder setEnabled(int viewId, boolean enabled) { 116 | View tv = findView(viewId); 117 | tv.setEnabled(enabled); 118 | return this; 119 | } 120 | public RecycleHolder setEnabled_imgView(int viewId, boolean enabled) { 121 | ImageView tv = findView(viewId); 122 | tv.setEnabled(enabled); 123 | return this; 124 | } 125 | public RecycleHolder setText(int viewId, int text) { 126 | TextView tv = findView(viewId); 127 | tv.setText(text); 128 | return this; 129 | } 130 | 131 | public RecycleHolder setChecked(int viewId, boolean checked) { 132 | Checkable view = findView(viewId); 133 | view.setChecked(checked); 134 | return this; 135 | } 136 | 137 | public RecycleHolder setTextColor(int viewId, int colorResId) { 138 | TextView tv = findView(viewId); 139 | tv.setTextColor(tv.getContext().getResources().getColor(colorResId)); 140 | return this; 141 | } 142 | 143 | public RecycleHolder setImageResource(int viewId, int ImageId) { 144 | ImageView image = findView(viewId); 145 | image.setImageResource(ImageId); 146 | return this; 147 | } 148 | 149 | public RecycleHolder setLayoutParams(int viewId, ViewGroup.LayoutParams para) { 150 | View view = findView(viewId); 151 | view.setLayoutParams(para); 152 | return this; 153 | } 154 | 155 | 156 | public RecycleHolder setImageBitmap(int viewId, Bitmap bitmap) { 157 | ImageView image = findView(viewId); 158 | image.setImageBitmap(bitmap); 159 | return this; 160 | } 161 | 162 | public RecycleHolder setGlideImage(int viewId, String url, int placehodlerImg, Activity activity){ 163 | ImageView image = findView(viewId); 164 | Glide.with(activity) 165 | .load(url) 166 | .error(placehodlerImg) 167 | .placeholder(placehodlerImg) 168 | .into(image); 169 | return this; 170 | } 171 | 172 | public RecycleHolder setImageNet(int viewId, String url) { 173 | ImageView image = findView(viewId); 174 | //使用你所用的网络框架等 175 | return this; 176 | } 177 | 178 | public RecycleHolder setBackgroundColor(int viewId, int color) { 179 | View view = findView(viewId); 180 | view.setBackgroundColor(color); 181 | return this; 182 | } 183 | 184 | public RecycleHolder setBackgroundRes(int viewId, int backgroundRes) { 185 | View view = findView(viewId); 186 | view.setBackgroundResource(backgroundRes); 187 | return this; 188 | } 189 | 190 | public RecycleHolder setVisible(int viewId, boolean visible) { 191 | View image = findView(viewId); 192 | image.setVisibility(visible ? View.VISIBLE : View.GONE); 193 | //使用你所用的网络框架等 194 | return this; 195 | } 196 | 197 | public RecycleHolder setVisible_invisible(int viewId, boolean visible) { 198 | View image = findView(viewId); 199 | image.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); 200 | //使用你所用的网络框架等 201 | return this; 202 | } 203 | 204 | public RecycleHolder setProgesss(int viewId, int percent) { 205 | ProgressBar progressBar = findView(viewId); 206 | progressBar.setProgress(percent); 207 | return this; 208 | } 209 | 210 | public boolean getVisible(int viewId) { 211 | View view = findView(viewId); 212 | return view.getVisibility() == View.VISIBLE; 213 | } 214 | 215 | 216 | } 217 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/adapter/RecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.adapter; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * 做为recyclerview 万能适配器 13 | * @param 14 | */ 15 | public abstract class RecyclerAdapter extends RecyclerView.Adapter { 16 | 17 | private Context mContext; 18 | private List mDatas; 19 | private int mLayoutId; 20 | private LayoutInflater mInflater; 21 | 22 | private OnItemClickListener onItemClickListener; 23 | 24 | public RecyclerAdapter(Context mContext, List mDatas, int mLayoutId) { 25 | this.mContext = mContext; 26 | this.mDatas = mDatas; 27 | this.mLayoutId = mLayoutId; 28 | mInflater = LayoutInflater.from(mContext); 29 | } 30 | 31 | @Override 32 | public RecycleHolder onCreateViewHolder(ViewGroup parent, int viewType) { 33 | return new RecycleHolder(mInflater.inflate(mLayoutId, parent, false)); 34 | } 35 | 36 | @Override 37 | public void onBindViewHolder(final RecycleHolder holder, int position) { 38 | convert(holder, mDatas.get(position), position); 39 | if (onItemClickListener != null) { 40 | //设置背景 41 | holder.itemView.setOnClickListener(new View.OnClickListener() { 42 | @Override 43 | public void onClick(View v) { 44 | //注意,这里的position不要用上面参数中的position,会出现位置错乱\ 45 | onItemClickListener.OnItemClickListener(holder.itemView, holder.getLayoutPosition()); 46 | } 47 | }); 48 | } 49 | 50 | } 51 | 52 | public abstract void convert(RecycleHolder holder, T data, int position); 53 | 54 | @Override 55 | public int getItemCount() { 56 | return mDatas.size(); 57 | } 58 | 59 | public void setOnItemClickListener(OnItemClickListener onItemClickListener) { 60 | this.onItemClickListener = onItemClickListener; 61 | } 62 | 63 | public interface OnItemClickListener { 64 | void OnItemClickListener(View view, int position); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/api/BaseApi.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.api; 2 | 3 | import com.senon.lib_common.base.BaseResponse; 4 | import com.senon.lib_common.bean.Chapter; 5 | import com.senon.lib_common.bean.Login; 6 | 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | import io.reactivex.Observable; 11 | import retrofit2.http.FieldMap; 12 | import retrofit2.http.FormUrlEncoded; 13 | import retrofit2.http.GET; 14 | import retrofit2.http.POST; 15 | 16 | /** 17 | * 网络请求接口 18 | */ 19 | public interface BaseApi { 20 | 21 | 22 | //登录 23 | @POST("user/login") 24 | @FormUrlEncoded 25 | Observable> login(@FieldMap Map map); 26 | //登出 27 | @GET("user/logout/json") 28 | Observable logout(); 29 | //注册 30 | @POST("user/register") 31 | @FormUrlEncoded 32 | Observable> register(@FieldMap Map map); 33 | 34 | //获取公众号文章 35 | @GET("wxarticle/chapters/json ") 36 | Observable>> getChapters(); 37 | } 38 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/base/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.base; 2 | 3 | import android.content.pm.ActivityInfo; 4 | import android.os.Bundle; 5 | import android.view.Window; 6 | 7 | import com.alibaba.android.arouter.launcher.ARouter; 8 | import com.trello.rxlifecycle2.components.support.RxAppCompatActivity; 9 | 10 | /** 11 | * 父类->基类->动态指定类型->泛型设计(通过泛型指定动态类型->由子类指定,父类只需要规定范围即可) 12 | */ 13 | public abstract class BaseActivity> extends RxAppCompatActivity { 14 | 15 | //引用V层和P层 16 | private P presenter; 17 | private V view; 18 | 19 | public P getPresenter(){ 20 | return presenter; 21 | } 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | requestWindowFeature(Window.FEATURE_NO_TITLE); 27 | setContentView(getLayoutId()); 28 | setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); // 禁止所有的activity横屏 29 | ARouter.getInstance().inject(this); 30 | if(presenter == null){ 31 | presenter = createPresenter(); 32 | } 33 | if(view == null){ 34 | view = createView(); 35 | } 36 | if(presenter != null && view != null){ 37 | presenter.attachView(view); 38 | } 39 | init(); 40 | } 41 | 42 | //由子类指定具体类型 43 | public abstract int getLayoutId(); 44 | public abstract P createPresenter(); 45 | public abstract V createView(); 46 | public abstract void init(); 47 | 48 | @Override 49 | protected void onDestroy() { 50 | super.onDestroy(); 51 | if(presenter != null){ 52 | presenter.detachView(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/base/BaseAppDeletage.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.base; 2 | 3 | import android.app.Application; 4 | import com.senon.lib_common.utils.ConstantUtils; 5 | import com.senon.lib_common.utils.LogUtils; 6 | import com.senon.lib_common.utils.PreferenceTool; 7 | import com.senon.lib_common.utils.ToastUtil; 8 | import me.jessyan.autosize.AutoSizeConfig; 9 | import me.jessyan.autosize.unit.Subunits; 10 | 11 | /** 12 | * 13 | */ 14 | public class BaseAppDeletage { 15 | 16 | private Application mApplication; 17 | 18 | public BaseAppDeletage(Application application) { 19 | mApplication = application; 20 | } 21 | 22 | public void onCreate() { 23 | ConstantUtils.init(mApplication); //全局Utils 24 | initLog(); //Log日志 25 | PreferenceTool.init(mApplication); //Preference参数 26 | ToastUtil.init(mApplication); //吐司初始化 27 | initAutoSizeUnits(); //配置全局 布局适配单位mm 28 | } 29 | 30 | private void initLog() { 31 | LogUtils.setLogEnable(ConstantUtils.isAppDebug()); 32 | } 33 | 34 | private void initAutoSizeUnits() { 35 | AutoSizeConfig.getInstance().getUnitsManager() 36 | //支持dp适配 默认true 37 | .setSupportDP(true) 38 | //支持sp适配 默认true 39 | .setSupportSP(true) 40 | // .setSupportSubunits(Subunits.MM) 41 | ; 42 | AutoSizeConfig.getInstance() 43 | //按照宽度适配 默认true 44 | .setBaseOnWidth(true) 45 | //是否让框架支持自定义 Fragment 的适配参数, 由于这个需求是比较少见的, 所以须要使用者手动开启 46 | //如果没有这个需求建议不开启 47 | .setCustomFragment(true); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/base/BaseApplication.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.base; 2 | 3 | import android.app.Application; 4 | 5 | /** 6 | * 要想使用BaseApplication,必须在组件中实现自己的Application,并且继承BaseApplication; 7 | * 组件模块中实现的Application必须在debug包中的AndroidManifest.xml中注册,否则无法使用; 8 | * 组件模块的Application需置于java/debug文件夹中,不得放于主代码; 9 | * 组件模块中获取Context的方法必须为:Util.getAPPContext(),不允许其他写法; 10 | */ 11 | public class BaseApplication extends Application { 12 | 13 | @Override 14 | public void onCreate() { 15 | super.onCreate(); 16 | BaseAppDeletage baseAppDeletage = new BaseAppDeletage(this); 17 | baseAppDeletage.onCreate(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/base/BaseEvent.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.base; 2 | 3 | /** 4 | * 自定义eventbus发送实体 5 | */ 6 | public class BaseEvent { 7 | 8 | /** 9 | * -1:退出登录时通知Mainactivity finish 10 | * 0:登录成功 刷新所有主界面数据列表 11 | * 1:退出成功 。。。 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | */ 19 | private int code; 20 | private String msg; 21 | private T data; 22 | private boolean isCollect; 23 | private int id; 24 | private boolean ingored; 25 | 26 | 27 | public int getCode() { 28 | return code; 29 | } 30 | 31 | public void setCode(int code) { 32 | this.code = code; 33 | } 34 | 35 | public String getMsg() { 36 | return msg; 37 | } 38 | 39 | public void setMsg(String msg) { 40 | this.msg = msg; 41 | } 42 | 43 | public T getData() { 44 | return data; 45 | } 46 | 47 | public void setData(T data) { 48 | this.data = data; 49 | } 50 | 51 | public boolean isCollect() { 52 | return isCollect; 53 | } 54 | 55 | public void setCollect(boolean collect) { 56 | isCollect = collect; 57 | } 58 | 59 | public int getId() { 60 | return id; 61 | } 62 | 63 | public void setId(int id) { 64 | this.id = id; 65 | } 66 | 67 | public boolean isIngored() { 68 | return ingored; 69 | } 70 | 71 | public void setIngored(boolean ingored) { 72 | this.ingored = ingored; 73 | } 74 | 75 | 76 | } 77 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/base/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.base; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | import com.trello.rxlifecycle2.components.support.RxFragment; 10 | 11 | /** 12 | * 父类->基类->动态指定类型->泛型设计(通过泛型指定动态类型->由子类指定,父类只需要规定范围即可) 13 | */ 14 | public abstract class BaseFragment> extends RxFragment { 15 | 16 | //引用V层和P层 17 | private P presenter; 18 | private V view; 19 | public Context mContext; 20 | 21 | public P getPresenter() { 22 | return presenter; 23 | } 24 | 25 | @Override 26 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 27 | View view = inflater.inflate(getLayoutId(), container, false); 28 | mContext = getActivity(); 29 | if (presenter == null) { 30 | presenter = createPresenter(); 31 | } 32 | if (this.view == null) { 33 | this.view = createView(); 34 | } 35 | if (presenter != null && view != null) { 36 | presenter.attachView(this.view); 37 | } 38 | init(); 39 | return view; 40 | } 41 | 42 | //由子类指定具体类型 43 | public abstract int getLayoutId(); 44 | public abstract P createPresenter(); 45 | public abstract V createView(); 46 | public abstract void init(); 47 | 48 | @Override 49 | public void onDestroyView() { 50 | super.onDestroyView(); 51 | if (presenter != null) { 52 | presenter.detachView(); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/base/BaseLazyFragment.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.base; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | 10 | import com.senon.lib_common.utils.LogUtils; 11 | import com.trello.rxlifecycle2.components.support.RxFragment; 12 | 13 | /** 14 | * https://juejin.im/post/5adcb0e36fb9a07aa7673fbc 15 | * BaseLazyFragment 单fragment懒加载 16 | * 17 | * * 生命周期执行的方法 如下: 18 | * 第一次生成页面-->可见 19 | * setUserVisibleHint: ----->false 20 | * setUserVisibleHint: ----->true 21 | * onCreateView: -----> onCreateView 22 | * onStart: -----> onStart 23 | * onFragmentFirst: 首次可见 24 | * onFragmentFirst: -----> 子fragment进行初始化操作 25 | * onResume: -----> onResume 26 | * 27 | * 可见-->第一次隐藏: 28 | * onPause: -----> onPause 29 | * onFragmentInVisible: 不可见 30 | * 31 | * 未销毁且不可见-->重新可见: 32 | * onStart: -----> onStart 33 | * onFragmentVisble: 可见 34 | * onFragmentVisble: -----> 子fragment每次可见时的操作 35 | * onResume: -----> onResume 36 | * 37 | * 可见-->销毁: 38 | * onPause: -----> onPause 39 | * onFragmentInVisible: 不可见 40 | * onDestroyView: -----> onDestroyView 41 | * 42 | * 我们可以更具以上生命周期来操作不同的业务逻辑, 43 | * 请务必运行此module demo,观看打印日志来自定义逻辑。 44 | */ 45 | public abstract class BaseLazyFragment> extends RxFragment { 46 | //引用V层和P层 47 | private P presenter; 48 | private V view; 49 | public Context mContext; 50 | 51 | private View rootView; 52 | private boolean mIsFirstVisible = true;/*当前Fragment是否首次可见,默认是首次可见**/ 53 | private boolean isViewCreated = false;/*当前Fragment的View是否已经创建**/ 54 | private boolean currentVisibleState = false;/*当前Fragment的可见状态,一种当前可见,一种当前不可见**/ 55 | 56 | public P getPresenter() { 57 | return presenter; 58 | } 59 | 60 | @Nullable 61 | @Override 62 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 63 | LogUtils.e("-----> onCreateView"); 64 | if(rootView == null){ 65 | rootView = inflater.inflate(getLayoutId(), container, false); 66 | mContext = getActivity(); 67 | if (presenter == null) { 68 | presenter = createPresenter(); 69 | } 70 | if (this.view == null) { 71 | this.view = createView(); 72 | } 73 | if (presenter != null && view != null) { 74 | presenter.attachView(this.view); 75 | } 76 | init(rootView); 77 | } 78 | isViewCreated=true;//在onCreateView执行完毕,将isViewCreated改为true; 79 | return rootView; 80 | } 81 | 82 | //由子类指定具体类型 83 | public abstract int getLayoutId(); 84 | public abstract P createPresenter(); 85 | public abstract V createView(); 86 | public abstract void init(View rootView); 87 | 88 | @Override 89 | public void setUserVisibleHint(boolean isVisibleToUser) { 90 | super.setUserVisibleHint(isVisibleToUser); 91 | LogUtils.e("----->"+isVisibleToUser); 92 | if (isViewCreated) { 93 | //Fragment可见且状态不是可见(从一个Fragment切换到另外一个Fragment,后一个设置状态为可见) 94 | if (isVisibleToUser && !currentVisibleState) { 95 | disPatchFragment(true); 96 | } else if (!isVisibleToUser && currentVisibleState) { 97 | //Fragment不可见且状态是可见(从一个Fragment切换到另外一个Fragment,前一个更改状态为不可见) 98 | disPatchFragment(false); 99 | } 100 | } 101 | } 102 | 103 | @Override 104 | public void onStart() { 105 | super.onStart(); 106 | LogUtils.e("-----> onStart"); 107 | //isHidden()是Fragment是否处于隐藏状态和isVisible()有区别 108 | //getUserVisibleHint(),Fragement是否可见 109 | if(!isHidden()&& getUserVisibleHint()){//如果Fragment没有隐藏且可见 110 | //执行分发的方法,三种结果对应自Fragment的三个回调,对应的操作,Fragment首次加载,可见,不可见 111 | disPatchFragment(true); 112 | } 113 | } 114 | 115 | @Override 116 | public void onResume() { 117 | super.onResume(); 118 | LogUtils.e("-----> onResume"); 119 | if(!mIsFirstVisible){ 120 | //表示点击home键又返回操作,设置可见状态为ture 121 | if(!isHidden()&& !getUserVisibleHint() && currentVisibleState){ 122 | disPatchFragment(true); 123 | } 124 | } 125 | } 126 | 127 | @Override 128 | public void onPause() { 129 | super.onPause(); 130 | LogUtils.e("-----> onPause"); 131 | //表示点击home键,原来可见的Fragment要走该方法,更改Fragment的状态为不可见 132 | if(!isHidden()&& getUserVisibleHint()){ 133 | disPatchFragment(false); 134 | } 135 | } 136 | 137 | @Override 138 | public void onDestroyView() { 139 | super.onDestroyView(); 140 | LogUtils.e("-----> onDestroyView"); 141 | //当 View 被销毁的时候我们需要重新设置 isViewCreated mIsFirstVisible 的状态 142 | isViewCreated = false; 143 | mIsFirstVisible = true; 144 | 145 | if (presenter != null) { 146 | presenter.detachView(); 147 | } 148 | } 149 | 150 | 151 | /** 152 | * @param visible Fragment当前是否可见,然后调用相关方法 153 | */ 154 | public void disPatchFragment(boolean visible){ 155 | currentVisibleState=visible; 156 | if(visible){//Fragment可见 157 | if(mIsFirstVisible){//可见又是第一次 158 | mIsFirstVisible=false;//改变首次可见的状态 159 | onFragmentFirst(); 160 | }else{//可见但不是第一次 161 | onFragmentVisble(); 162 | } 163 | }else {//不可见 164 | onFragmentInVisible(); 165 | } 166 | } 167 | 168 | //Fragemnet首次可见的方法 169 | public void onFragmentFirst(){ 170 | LogUtils.e("首次可见"); 171 | } 172 | //Fragemnet可见的方法 173 | public void onFragmentVisble(){//子Fragment调用次方法,执行可见操作 174 | LogUtils.e("可见"); 175 | } 176 | //Fragemnet不可见的方法 177 | public void onFragmentInVisible(){ 178 | LogUtils.e("不可见"); 179 | } 180 | 181 | } 182 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/base/BaseModelCallBack.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.base; 2 | 3 | 4 | public interface BaseModelCallBack { 5 | 6 | void onNext(T t); 7 | 8 | void onError(Throwable e); 9 | 10 | //数据库读取进度 11 | // void onProgress(int total,int current,float progress); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/base/BaseNestingLazyFragment.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.base; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.support.annotation.Nullable; 7 | import android.support.v4.app.Fragment; 8 | import android.support.v4.app.FragmentManager; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | 13 | import com.senon.lib_common.utils.LogUtils; 14 | import com.trello.rxlifecycle2.components.support.RxFragment; 15 | 16 | import java.util.List; 17 | 18 | /** 19 | * https://juejin.im/post/5adcb0e36fb9a07aa7673fbc 20 | * 21 | * BaseNestingLazyFragment fragment嵌套fragment的懒加载父类 22 | */ 23 | public abstract class BaseNestingLazyFragment> extends RxFragment { 24 | //引用V层和P层 25 | private P presenter; 26 | private V view; 27 | public Context mContext; 28 | 29 | private View rootView; 30 | private boolean mIsFirstVisible = true;/*当前Fragment是否首次可见,默认是首次可见**/ 31 | private boolean isViewCreated = false;/*当前Fragment的View是否已经创建**/ 32 | private boolean currentVisibleState = false;/*当前Fragment的可见状态,一种当前可见,一种当前不可见**/ 33 | 34 | public P getPresenter() { 35 | return presenter; 36 | } 37 | 38 | @Nullable 39 | @Override 40 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 41 | LogUtils.e("-----> onCreateView"); 42 | if (rootView == null) { 43 | rootView = inflater.inflate(getLayoutId(), container, false); 44 | mContext = getActivity(); 45 | if (presenter == null) { 46 | presenter = createPresenter(); 47 | } 48 | if (this.view == null) { 49 | this.view = createView(); 50 | } 51 | if (presenter != null && view != null) { 52 | presenter.attachView(this.view); 53 | } 54 | init(rootView); 55 | } 56 | isViewCreated = true;//在onCreateView执行完毕,将isViewCreated改为true; 57 | return rootView; 58 | } 59 | 60 | //由子类指定具体类型 61 | public abstract int getLayoutId(); 62 | 63 | public abstract P createPresenter(); 64 | 65 | public abstract V createView(); 66 | 67 | public abstract void init(View rootView); 68 | 69 | @Override 70 | public void setUserVisibleHint(boolean isVisibleToUser) { 71 | super.setUserVisibleHint(isVisibleToUser); 72 | LogUtils.e("----->" + isVisibleToUser); 73 | if (isViewCreated) { 74 | //Fragment可见且状态不是可见(从一个Fragment切换到另外一个Fragment,后一个设置状态为可见) 75 | if (isVisibleToUser && !currentVisibleState) { 76 | disPatchFragment(true); 77 | } else if (!isVisibleToUser && currentVisibleState) { 78 | //Fragment不可见且状态是可见(从一个Fragment切换到另外一个Fragment,前一个更改状态为不可见) 79 | disPatchFragment(false); 80 | } 81 | } 82 | } 83 | 84 | @Override 85 | public void onStart() { 86 | super.onStart(); 87 | LogUtils.e("-----> onStart"); 88 | //isHidden()是Fragment是否处于隐藏状态和isVisible()有区别 89 | //getUserVisibleHint(),Fragement是否可见 90 | if (!isHidden() && getUserVisibleHint()) {//如果Fragment没有隐藏且可见 91 | //执行分发的方法,三种结果对应自Fragment的三个回调,对应的操作,Fragment首次加载,可见,不可见 92 | disPatchFragment(true); 93 | } 94 | 95 | } 96 | 97 | @Override 98 | public void onResume() { 99 | super.onResume(); 100 | LogUtils.e("-----> onResume"); 101 | if (!mIsFirstVisible) { 102 | //表示点击home键又返回操作,设置可见状态为ture 103 | if (!isHidden() && !getUserVisibleHint() && currentVisibleState) { 104 | disPatchFragment(true); 105 | } 106 | } 107 | } 108 | 109 | @Override 110 | public void onPause() { 111 | super.onPause(); 112 | LogUtils.e("-----> onPause"); 113 | //表示点击home键,原来可见的Fragment要走该方法,更改Fragment的状态为不可见 114 | if (!isHidden() && getUserVisibleHint()) { 115 | disPatchFragment(false); 116 | } 117 | } 118 | 119 | @Override 120 | public void onDestroyView() { 121 | super.onDestroyView(); 122 | LogUtils.e("-----> onDestroyView"); 123 | //当 View 被销毁的时候我们需要重新设置 isViewCreated mIsFirstVisible 的状态 124 | isViewCreated = false; 125 | mIsFirstVisible = true; 126 | 127 | if (presenter != null) { 128 | presenter.detachView(); 129 | } 130 | 131 | } 132 | 133 | 134 | /** 135 | * @param visible Fragment当前是否可见,然后调用相关方法 136 | */ 137 | public void disPatchFragment(boolean visible) { 138 | String aa = getClass().getSimpleName(); 139 | //如果父Fragment不可见,则不向下分发给子Fragment 140 | if (visible && isParentFragmentVsible()) return; 141 | 142 | // 如果当前的 Fragment 要分发的状态与 currentVisibleState 相同(都为false)我们就没有必要去做分发了。 143 | if (currentVisibleState == visible) return; 144 | 145 | currentVisibleState = visible; 146 | if (visible) {//Fragment可见 147 | if (mIsFirstVisible) {//可见又是第一次 148 | mIsFirstVisible = false;//改变首次可见的状态 149 | onFragmentFirst(); 150 | }//可见但不是第一次 151 | onFragmentVisble(); 152 | //可见状态的时候内层 fragment 生命周期晚于外层 所以在 onFragmentResume 后分发 153 | dispatchChildFragmentVisibleState(true); 154 | } else {//不可见 155 | onFragmentInVisible(); 156 | dispatchChildFragmentVisibleState(false); 157 | } 158 | } 159 | 160 | 161 | /** 162 | * 重新分发给子Fragment 163 | * 164 | * @param visible 165 | */ 166 | private void dispatchChildFragmentVisibleState(boolean visible) { 167 | FragmentManager childFragmentManager = getChildFragmentManager(); 168 | @SuppressLint("RestrictedApi") List fragments = childFragmentManager.getFragments(); 169 | if (fragments != null) { 170 | if (!fragments.isEmpty()) { 171 | for (Fragment child : fragments) { 172 | if (child instanceof BaseNestingLazyFragment && !child.isHidden() && child.getUserVisibleHint()) { 173 | ((BaseNestingLazyFragment) child).disPatchFragment(visible); 174 | } 175 | } 176 | } 177 | } 178 | 179 | } 180 | 181 | //Fragemnet首次可见的方法 182 | public void onFragmentFirst() { 183 | LogUtils.e("首次可见"); 184 | } 185 | //Fragemnet可见的方法 186 | public void onFragmentVisble() {//子Fragment调用次方法,执行可见操作 187 | LogUtils.e("可见"); 188 | } 189 | //Fragemnet不可见的方法 190 | public void onFragmentInVisible() { 191 | LogUtils.e("不可见"); 192 | } 193 | 194 | /** 195 | * 判断多层嵌套的父Fragment是否显示 196 | */ 197 | private boolean isParentFragmentVsible() { 198 | BaseNestingLazyFragment fragment = (BaseNestingLazyFragment) getParentFragment(); 199 | return fragment != null && !fragment.getCurrentVisibleState(); 200 | } 201 | 202 | private boolean getCurrentVisibleState() { 203 | return currentVisibleState; 204 | } 205 | 206 | } 207 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/base/BasePresenter.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.base; 2 | 3 | 4 | /** 5 | * BasePresenter 6 | */ 7 | public abstract class BasePresenter{ 8 | 9 | private V mView; 10 | 11 | public V getView(){ 12 | return mView; 13 | } 14 | 15 | public void attachView(V v){ 16 | mView = v; 17 | } 18 | 19 | public void detachView(){ 20 | mView = null; 21 | } 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/base/BaseResponse.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.base; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | /** 6 | * BaseResponse 7 | */ 8 | public class BaseResponse { 9 | 10 | @SerializedName("errorMsg") 11 | private String msg; 12 | @SerializedName("errorCode") 13 | private int code; 14 | private T data; 15 | 16 | 17 | 18 | public String getMsg() { 19 | return msg; 20 | } 21 | 22 | public int getCode() { 23 | return code; 24 | } 25 | 26 | public T getData() { 27 | return data; 28 | } 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/base/BaseViewImp.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.base; 2 | 3 | /** 4 | * View 父类接口 5 | */ 6 | public interface BaseViewImp { 7 | } 8 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/bean/Chapter.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.bean; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * 公众号实体 7 | */ 8 | public class Chapter { 9 | 10 | /** 11 | * children : [] 12 | * courseId : 13 13 | * id : 434 14 | * name : Android达摩院 15 | * order : 190013 16 | * parentChapterId : 407 17 | * userControlSetTop : false 18 | * visible : 1 19 | */ 20 | 21 | private int courseId; 22 | private int id; 23 | private String name; 24 | private int order; 25 | private int parentChapterId; 26 | private boolean userControlSetTop; 27 | private int visible; 28 | private List children; 29 | 30 | public int getCourseId() { 31 | return courseId; 32 | } 33 | 34 | public void setCourseId(int courseId) { 35 | this.courseId = courseId; 36 | } 37 | 38 | public int getId() { 39 | return id; 40 | } 41 | 42 | public void setId(int id) { 43 | this.id = id; 44 | } 45 | 46 | public String getName() { 47 | return name; 48 | } 49 | 50 | public void setName(String name) { 51 | this.name = name; 52 | } 53 | 54 | public int getOrder() { 55 | return order; 56 | } 57 | 58 | public void setOrder(int order) { 59 | this.order = order; 60 | } 61 | 62 | public int getParentChapterId() { 63 | return parentChapterId; 64 | } 65 | 66 | public void setParentChapterId(int parentChapterId) { 67 | this.parentChapterId = parentChapterId; 68 | } 69 | 70 | public boolean isUserControlSetTop() { 71 | return userControlSetTop; 72 | } 73 | 74 | public void setUserControlSetTop(boolean userControlSetTop) { 75 | this.userControlSetTop = userControlSetTop; 76 | } 77 | 78 | public int getVisible() { 79 | return visible; 80 | } 81 | 82 | public void setVisible(int visible) { 83 | this.visible = visible; 84 | } 85 | 86 | public List getChildren() { 87 | return children; 88 | } 89 | 90 | public void setChildren(List children) { 91 | this.children = children; 92 | } 93 | 94 | @Override 95 | public String toString() { 96 | return "Chapter{" + 97 | "courseId=" + courseId + 98 | ", id=" + id + 99 | ", name='" + name + '\'' + 100 | ", order=" + order + 101 | ", parentChapterId=" + parentChapterId + 102 | ", userControlSetTop=" + userControlSetTop + 103 | ", visible=" + visible + 104 | ", children=" + children + 105 | '}'; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/bean/Login.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.bean; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * 登录--实体 7 | */ 8 | public class Login { 9 | 10 | /** 11 | * chapterTops : [] 12 | * collectIds : [2683,2880,2875,2868,2831,2829,2807] 13 | * email : 14 | * icon : 15 | * id : 1864 16 | * password : 17 | * token : 18 | * type : 0 19 | * username : senonwx 20 | */ 21 | 22 | public String email; 23 | public String icon; 24 | public int id; 25 | public String password; 26 | public String token; 27 | public int type; 28 | public String username; 29 | public List chapterTops; 30 | public List collectIds; 31 | 32 | public Login() { 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "Login{" + 38 | "email='" + email + '\'' + 39 | ", icon='" + icon + '\'' + 40 | ", id=" + id + 41 | ", password='" + password + '\'' + 42 | ", token='" + token + '\'' + 43 | ", type=" + type + 44 | ", username='" + username + '\'' + 45 | ", chapterTops=" + chapterTops + 46 | ", collectIds=" + collectIds + 47 | '}'; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/common/contract/LoginContract.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.common.contract; 2 | 3 | 4 | import com.senon.lib_common.base.BasePresenter; 5 | import com.senon.lib_common.base.BaseResponse; 6 | import com.senon.lib_common.base.BaseViewImp; 7 | import com.senon.lib_common.bean.Login; 8 | 9 | import java.util.HashMap; 10 | 11 | /** 12 | * LoginContract 13 | */ 14 | public interface LoginContract { 15 | 16 | //方法命名以 请求方法+Result 命名 17 | interface View extends BaseViewImp { 18 | 19 | void getLoginResult(BaseResponse data); 20 | 21 | void getRegisterResult(BaseResponse data); 22 | 23 | } 24 | 25 | //方法命名以 get+方法 命名 26 | abstract class Presenter extends BasePresenter { 27 | 28 | public abstract void getLogin(HashMap map, boolean isDialog, boolean cancelable); 29 | 30 | public abstract void getRegister(HashMap map, boolean isDialog, boolean cancelable); 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/common/contract/ModelRequestContract.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.common.contract; 2 | 3 | import android.content.Context; 4 | import com.senon.lib_common.base.BasePresenter; 5 | import com.senon.lib_common.base.BaseResponse; 6 | import com.senon.lib_common.base.BaseViewImp; 7 | import com.senon.lib_common.bean.Chapter; 8 | import com.senon.lib_common.bean.Login; 9 | import com.senon.lib_common.base.BaseModelCallBack; 10 | import com.trello.rxlifecycle2.LifecycleTransformer; 11 | 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | /** 16 | * 统一管理 v p m接口 17 | */ 18 | public interface ModelRequestContract { 19 | 20 | /** 21 | * View 22 | */ 23 | interface View extends BaseViewImp { 24 | 25 | void getDataResult(BaseResponse> data); 26 | 27 | } 28 | 29 | /** 30 | * Presenter 31 | */ 32 | abstract class Presenter extends BasePresenter { 33 | 34 | public abstract void getData(boolean isDialog, boolean cancelable); 35 | 36 | } 37 | 38 | /** 39 | * Model 40 | */ 41 | interface Model{ 42 | void getData(LifecycleTransformer>> transformer, Context context, 43 | boolean isDialog, boolean cancelable, BaseModelCallBack callBack); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/common/model/ModelRequestMod.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.common.model; 2 | 3 | import android.content.Context; 4 | import com.senon.lib_common.base.BaseResponse; 5 | import com.senon.lib_common.bean.Chapter; 6 | import com.senon.lib_common.bean.Login; 7 | import com.senon.lib_common.common.contract.ModelRequestContract; 8 | import com.senon.lib_common.base.BaseModelCallBack; 9 | import com.senon.lib_common.net.ServerUtils; 10 | import com.senon.lib_common.net.callback.RequestCallback; 11 | import com.senon.lib_common.net.callback.RxErrorHandler; 12 | import com.senon.lib_common.utils.RetryWithDelay; 13 | import com.senon.lib_common.utils.RxUtils; 14 | import com.trello.rxlifecycle2.LifecycleTransformer; 15 | 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | /** 20 | * model层 21 | */ 22 | public class ModelRequestMod implements ModelRequestContract.Model { 23 | 24 | @Override 25 | public void getData(LifecycleTransformer>> transformer, Context context, 26 | boolean isDialog, boolean cancelable, final BaseModelCallBack callBack){ 27 | ServerUtils.getCommonApi().getChapters() 28 | .compose(transformer) 29 | .compose(RxUtils.>>getSchedulerTransformer()) 30 | .subscribe(new RequestCallback>>(context, RxErrorHandler.getInstance(),isDialog,cancelable) { 31 | @Override 32 | public void onNext(BaseResponse> response) { 33 | super.onNext(response); 34 | callBack.onNext(response); 35 | } 36 | @Override 37 | public void onError(Throwable e) { 38 | super.onError(e); 39 | callBack.onError(e); 40 | } 41 | }); 42 | } 43 | 44 | 45 | //数据库读取等操作 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/common/presenter/LoginPresenter.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.common.presenter; 2 | 3 | import android.content.Context; 4 | 5 | import com.senon.lib_common.base.BaseResponse; 6 | import com.senon.lib_common.bean.Login; 7 | import com.senon.lib_common.common.contract.LoginContract; 8 | import com.senon.lib_common.net.ServerUtils; 9 | import com.senon.lib_common.net.callback.RequestCallback; 10 | import com.senon.lib_common.net.callback.RxErrorHandler; 11 | import com.senon.lib_common.utils.RetryWithDelay; 12 | import com.senon.lib_common.utils.RxUtils; 13 | import com.senon.lib_common.utils.ToastUtil; 14 | 15 | import java.util.HashMap; 16 | 17 | /** 18 | * 登录 P 19 | */ 20 | public class LoginPresenter extends LoginContract.Presenter { 21 | 22 | private Context context; 23 | 24 | public LoginPresenter(Context context) { 25 | this.context = context; 26 | } 27 | 28 | @Override 29 | public void getLogin(HashMap map, boolean isDialog, boolean cancelable) { 30 | ServerUtils.getCommonApi().login(map) 31 | .retryWhen(new RetryWithDelay(3,2)) 32 | .compose(RxUtils.>bindToLifecycle(getView())) 33 | .compose(RxUtils.>getSchedulerTransformer()) 34 | .subscribe(new RequestCallback>(context, RxErrorHandler.getInstance(),isDialog,cancelable) { 35 | @Override 36 | public void onNext(BaseResponse baseResponse) { 37 | super.onNext(baseResponse); 38 | if(baseResponse.getCode() == 0){ 39 | getView().getLoginResult(baseResponse); 40 | }else{ 41 | ToastUtil.initToast(baseResponse.getMsg()); 42 | } 43 | } 44 | @Override 45 | public void onError(Throwable e) { 46 | super.onError(e); 47 | } 48 | }); 49 | } 50 | 51 | @Override 52 | public void getRegister(HashMap map, boolean isDialog, boolean cancelable) { 53 | ServerUtils.getCommonApi().register(map) 54 | .retryWhen(new RetryWithDelay(3,2)) 55 | .compose(RxUtils.>bindToLifecycle(getView())) 56 | .compose(RxUtils.>getSchedulerTransformer()) 57 | .subscribe(new RequestCallback>(context, RxErrorHandler.getInstance(),isDialog,cancelable) { 58 | @Override 59 | public void onNext(BaseResponse baseResponse) { 60 | super.onNext(baseResponse); 61 | if(baseResponse.getCode() == 0){ 62 | getView().getRegisterResult(baseResponse); 63 | }else{ 64 | ToastUtil.initToast(baseResponse.getMsg()); 65 | } 66 | } 67 | @Override 68 | public void onError(Throwable e) { 69 | super.onError(e); 70 | } 71 | }); 72 | } 73 | 74 | 75 | } 76 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/common/presenter/ModelRequestPresenter.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.common.presenter; 2 | 3 | import android.content.Context; 4 | 5 | import com.senon.lib_common.base.BaseResponse; 6 | import com.senon.lib_common.bean.Chapter; 7 | import com.senon.lib_common.bean.Login; 8 | import com.senon.lib_common.common.contract.ModelRequestContract; 9 | import com.senon.lib_common.common.model.ModelRequestMod; 10 | import com.senon.lib_common.base.BaseModelCallBack; 11 | import com.senon.lib_common.utils.RxUtils; 12 | import com.senon.lib_common.utils.ToastUtil; 13 | 14 | import java.util.HashMap; 15 | import java.util.List; 16 | 17 | /** 18 | * 结合model使用的 p层 19 | */ 20 | public class ModelRequestPresenter extends ModelRequestContract.Presenter { 21 | 22 | private Context context; 23 | private ModelRequestMod model; 24 | 25 | public ModelRequestPresenter(Context context) { 26 | this.context = context; 27 | this.model = new ModelRequestMod(); 28 | } 29 | 30 | 31 | @Override 32 | public void getData(boolean isDialog, boolean cancelable) { 33 | /** 34 | * 为什么rx绑定生命周期不放在model层, 35 | * 因为我们不希望m层拥有直接操作v层的权利 隔离v与m层 36 | * 只希望m层与p层交互 37 | */ 38 | model.getData(RxUtils.>>bindToLifecycle(getView()), context, isDialog, cancelable, 39 | new BaseModelCallBack() { 40 | @Override 41 | public void onNext(Object o) { 42 | getView().getDataResult((BaseResponse>) o); 43 | } 44 | 45 | @Override 46 | public void onError(Throwable e) { 47 | ToastUtil.initToast(e.getMessage()); 48 | } 49 | }); 50 | } 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/common/ui/LoginActivity.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.common.ui; 2 | 3 | import android.graphics.Paint; 4 | import android.view.View; 5 | import android.widget.EditText; 6 | import android.widget.TextView; 7 | import com.alibaba.android.arouter.facade.annotation.Autowired; 8 | import com.alibaba.android.arouter.facade.annotation.Route; 9 | import com.alibaba.android.arouter.launcher.ARouter; 10 | import com.senon.lib_common.utils.ComUtil; 11 | import com.senon.lib_common.ConstantArouter; 12 | import com.senon.lib_common.ConstantLoginArouter; 13 | import com.senon.lib_common.R; 14 | import com.senon.lib_common.base.BaseActivity; 15 | import com.senon.lib_common.base.BaseResponse; 16 | import com.senon.lib_common.bean.Login; 17 | import com.senon.lib_common.common.contract.LoginContract; 18 | import com.senon.lib_common.common.presenter.LoginPresenter; 19 | import com.senon.lib_common.utils.StatusBarUtils; 20 | import com.senon.lib_common.utils.ToastUtil; 21 | 22 | /** 23 | * 所有模块统一登录页面 24 | */ 25 | @Route(path = ConstantLoginArouter.PATH_COMMON_LOGINACTIVITY) 26 | public class LoginActivity extends BaseActivity implements 27 | LoginContract.View { 28 | 29 | @Autowired 30 | String targetUrl; 31 | private EditText account_edt,password_edt; 32 | private TextView register_tv; 33 | //这是branch1中修改的 34 | 35 | 36 | @Override 37 | public int getLayoutId() { 38 | StatusBarUtils.with(this).init(); 39 | return R.layout.common_activity_login; 40 | } 41 | 42 | @Override 43 | public LoginContract.Presenter createPresenter() { 44 | return new LoginPresenter(this); 45 | } 46 | 47 | @Override 48 | public LoginContract.View createView() { 49 | return this; 50 | } 51 | 52 | @Override 53 | public void init() { 54 | ComUtil.changeStatusBarTextColor(this,true); 55 | 56 | account_edt = findViewById(R.id.account_edt); 57 | password_edt = findViewById(R.id.password_edt); 58 | register_tv = findViewById(R.id.register_tv); 59 | 60 | account_edt.setSelection(account_edt.getText().toString().length()); 61 | register_tv.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG); 62 | 63 | } 64 | 65 | @Override 66 | public void getLoginResult(BaseResponse data) { 67 | //登录成功时 68 | //保存参数... 69 | 70 | if(targetUrl != null){ 71 | //跳转到目标页 72 | ARouter.getInstance() 73 | .build(targetUrl) 74 | .withObject("data",data.getData()) 75 | .navigation(); 76 | } 77 | 78 | finish(); 79 | } 80 | 81 | @Override 82 | public void getRegisterResult(BaseResponse data) { 83 | } 84 | 85 | public void onClick(View view) { 86 | int id = view.getId(); 87 | if(id == R.id.login_btn){ 88 | String account = account_edt.getText().toString(); 89 | String password = password_edt.getText().toString(); 90 | if (account.isEmpty()) { 91 | ToastUtil.initToast("请输入账号"); 92 | return; 93 | } 94 | if (password.isEmpty()) { 95 | ToastUtil.initToast("请输入密码"); 96 | return; 97 | } 98 | getPresenter().getLogin(ComUtil.getMd5Str( 99 | new String[]{"username", "password"}, 100 | new String[]{account.trim(), password.trim()}) 101 | , true, true); 102 | }else if(id == R.id.register_tv){ 103 | //跳转到注册页 统一用arouter跳转,便于以后移动模块的改动 104 | ARouter.getInstance().build(ConstantArouter.PATH_COMMON_REGISTERACTIVITY) 105 | .withString("targetUrl",targetUrl) 106 | .navigation(); 107 | } 108 | } 109 | 110 | 111 | } 112 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/common/ui/ModelRequestActivity.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.common.ui; 2 | 3 | import android.view.View; 4 | import android.widget.TextView; 5 | import com.alibaba.android.arouter.facade.annotation.Route; 6 | import com.senon.lib_common.ConstantArouter; 7 | import com.senon.lib_common.R; 8 | import com.senon.lib_common.base.BaseActivity; 9 | import com.senon.lib_common.base.BaseResponse; 10 | import com.senon.lib_common.bean.Chapter; 11 | import com.senon.lib_common.common.contract.ModelRequestContract; 12 | import com.senon.lib_common.common.presenter.ModelRequestPresenter; 13 | import com.senon.lib_common.utils.ComUtil; 14 | import com.senon.lib_common.utils.StatusBarUtils; 15 | 16 | import java.util.List; 17 | 18 | /** 19 | * 结合model层请求数据 20 | */ 21 | @Route(path = ConstantArouter.PATH_MODELREQUESTACTIVITY) 22 | public class ModelRequestActivity extends BaseActivity implements 23 | ModelRequestContract.View { 24 | 25 | private TextView tv; 26 | 27 | @Override 28 | public int getLayoutId() { 29 | StatusBarUtils.with(this).init(); 30 | return R.layout.common_activity_modelrequest; 31 | } 32 | 33 | @Override 34 | public ModelRequestContract.Presenter createPresenter() { 35 | return new ModelRequestPresenter(this); 36 | } 37 | 38 | @Override 39 | public ModelRequestContract.View createView() { 40 | return this; 41 | } 42 | 43 | @Override 44 | public void init() { 45 | ComUtil.changeStatusBarTextColor(this,true); 46 | 47 | tv = findViewById(R.id.tv); 48 | 49 | } 50 | 51 | public void onClick(View view){ 52 | getPresenter().getData(true,true); 53 | } 54 | 55 | @Override 56 | public void getDataResult(BaseResponse> data) { 57 | tv.setText(data.getData().toString()); 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/common/ui/RegisterActivity.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.common.ui; 2 | 3 | import android.graphics.Paint; 4 | import android.view.View; 5 | import android.widget.EditText; 6 | import android.widget.TextView; 7 | import com.alibaba.android.arouter.facade.annotation.Autowired; 8 | import com.alibaba.android.arouter.facade.annotation.Route; 9 | import com.alibaba.android.arouter.launcher.ARouter; 10 | import com.senon.lib_common.utils.ComUtil; 11 | import com.senon.lib_common.ConstantArouter; 12 | import com.senon.lib_common.ConstantLoginArouter; 13 | import com.senon.lib_common.R; 14 | import com.senon.lib_common.base.BaseActivity; 15 | import com.senon.lib_common.base.BaseResponse; 16 | import com.senon.lib_common.bean.Login; 17 | import com.senon.lib_common.common.contract.LoginContract; 18 | import com.senon.lib_common.common.presenter.LoginPresenter; 19 | import com.senon.lib_common.utils.StatusBarUtils; 20 | import com.senon.lib_common.utils.ToastUtil; 21 | 22 | /** 23 | * 所有模块统一注册页面 24 | */ 25 | @Route(path = ConstantArouter.PATH_COMMON_REGISTERACTIVITY) 26 | public class RegisterActivity extends BaseActivity implements 27 | LoginContract.View { 28 | 29 | @Autowired 30 | String targetUrl; 31 | private EditText account_edt,password_edt,password_re_edt; 32 | private TextView login_tv; 33 | 34 | @Override 35 | public int getLayoutId() { 36 | StatusBarUtils.with(this).init(); 37 | return R.layout.common_activity_register; 38 | } 39 | 40 | @Override 41 | public LoginContract.Presenter createPresenter() { 42 | return new LoginPresenter(this); 43 | } 44 | 45 | @Override 46 | public LoginContract.View createView() { 47 | return this; 48 | } 49 | 50 | @Override 51 | public void init() { 52 | ComUtil.changeStatusBarTextColor(this,true); 53 | 54 | if (targetUrl == null) { 55 | //默认跳转到MainActivity 56 | targetUrl = ConstantLoginArouter.PATH_APP_MAINACTIVITY; 57 | } 58 | account_edt = findViewById(R.id.account_edt); 59 | password_edt = findViewById(R.id.password_edt); 60 | password_re_edt = findViewById(R.id.password_re_edt); 61 | login_tv = findViewById(R.id.login_tv); 62 | 63 | login_tv.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG); 64 | } 65 | 66 | @Override 67 | public void getLoginResult(BaseResponse data) { 68 | } 69 | 70 | @Override 71 | public void getRegisterResult(BaseResponse data) { 72 | //登录成功时 73 | //保存参数... 74 | 75 | if(targetUrl != null){ 76 | //跳转到目标页 77 | ARouter.getInstance() 78 | .build(targetUrl) 79 | .withString("msg","注册成功") 80 | .navigation(); 81 | } 82 | 83 | finish(); 84 | } 85 | 86 | public void onClick(View view) { 87 | int id = view.getId(); 88 | if(id == R.id.register_btn){ 89 | String account = account_edt.getText().toString(); 90 | String password = password_edt.getText().toString(); 91 | String repassword = password_re_edt.getText().toString(); 92 | if (account.isEmpty()) { 93 | ToastUtil.initToast("请输入账号"); 94 | return; 95 | } 96 | if (password.isEmpty()) { 97 | ToastUtil.initToast("请输入密码"); 98 | return; 99 | } 100 | if (repassword.isEmpty()) { 101 | ToastUtil.initToast("请输入确认密码"); 102 | return; 103 | } 104 | if (!password.equals(repassword)) { 105 | ToastUtil.initToast("确认密码与密码不同"); 106 | return; 107 | } 108 | getPresenter().getRegister(ComUtil.getMd5Str( 109 | new String[]{"username", "password", "repassword"}, 110 | new String[]{account.trim(), password.trim(),repassword.trim()}) 111 | , true, true); 112 | }else if(id == R.id.login_tv){ 113 | finish(); 114 | } 115 | 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/net/RequestInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.net; 2 | 3 | import org.json.JSONObject; 4 | 5 | import java.io.IOException; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | import okhttp3.Interceptor; 10 | import okhttp3.Request; 11 | import okhttp3.Response; 12 | 13 | 14 | /** 15 | * 网络统一拦截 16 | */ 17 | public class RequestInterceptor implements Interceptor { 18 | 19 | private final int DEVICE_TYPE = 1; // 设备类型 20 | 21 | private RequestInterceptor() { 22 | } 23 | 24 | private static RequestInterceptor instance; 25 | 26 | public static RequestInterceptor getInstance() { 27 | if (instance == null) { 28 | synchronized (RequestInterceptor.class) { 29 | if (instance == null) { 30 | instance = new RequestInterceptor(); 31 | } 32 | } 33 | } 34 | return instance; 35 | } 36 | 37 | @Override 38 | public Response intercept(Chain chain) throws IOException { 39 | Request original = chain.request(); 40 | Request.Builder requestBuilder = original.newBuilder() 41 | .header("data", fetchHeaderInfo()) 42 | .method(original.method(), original.body()); 43 | return chain.proceed(requestBuilder.build()); 44 | } 45 | 46 | private String fetchHeaderInfo() { 47 | // String deviceCode = MD5Utils.GetMD5Code(MobileInfo.phoneOnlyCode(context)); 48 | 49 | 50 | Map map = new HashMap<>(); 51 | // map.put("DeviceType", String.valueOf(DEVICE_TYPE)); 52 | // map.put("DeviceToken", deviceCode); 53 | // map.put("APPVersion", APP_VERSION_NAME); 54 | // map.put("RegistrationID", jpushID); 55 | // map.put("Authorization", token); 56 | JSONObject json = new JSONObject(map); 57 | return json.toString(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/net/ServerUtils.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.net; 2 | 3 | import com.senon.lib_common.AppConfig; 4 | import com.senon.lib_common.utils.ComUtil; 5 | import com.senon.lib_common.api.BaseApi; 6 | import com.senon.lib_common.utils.ConstantUtils; 7 | import java.io.IOException; 8 | import java.security.cert.CertificateException; 9 | import java.util.concurrent.TimeUnit; 10 | import javax.net.ssl.SSLContext; 11 | import javax.net.ssl.SSLSocketFactory; 12 | import javax.net.ssl.TrustManager; 13 | import javax.net.ssl.X509TrustManager; 14 | import okhttp3.CacheControl; 15 | import okhttp3.Interceptor; 16 | import okhttp3.OkHttpClient; 17 | import okhttp3.Request; 18 | import okhttp3.Response; 19 | import okhttp3.logging.HttpLoggingInterceptor; 20 | import retrofit2.Retrofit; 21 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; 22 | import retrofit2.converter.gson.GsonConverterFactory; 23 | import retrofit2.converter.scalars.ScalarsConverterFactory; 24 | 25 | /** 26 | * 网络请求 工具 27 | */ 28 | public class ServerUtils { 29 | private static final int TIME_OUT = 10 * 1000;//链接超时时间 30 | private static BaseApi mBaseApi; 31 | 32 | public static BaseApi getCommonApi() { 33 | try { 34 | if (mBaseApi == null) { 35 | synchronized (ServerUtils.class) { 36 | if (mBaseApi == null) { 37 | mBaseApi = createService(BaseApi.class, AppConfig.BASE_URL); 38 | } 39 | } 40 | } 41 | } catch (Exception e) { 42 | e.printStackTrace(); 43 | } 44 | return mBaseApi; 45 | } 46 | 47 | private static S createService(Class serviceClass, String url) throws Exception { 48 | Retrofit.Builder builder = 49 | new Retrofit.Builder() 50 | .baseUrl(url) 51 | .client(httpClient.build()) 52 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 53 | .addConverterFactory(ScalarsConverterFactory.create()) 54 | .addConverterFactory(GsonConverterFactory.create()); 55 | Retrofit retrofit = builder.build(); 56 | return retrofit.create(serviceClass); 57 | } 58 | 59 | private static Interceptor cacheInterceptor = new Interceptor() { 60 | @Override 61 | public Response intercept(Chain chain) throws IOException { 62 | Request request = chain.request(); 63 | if (!ComUtil.isNetworkConnected()) { 64 | request = request.newBuilder() 65 | .cacheControl(CacheControl.FORCE_CACHE) 66 | .build(); 67 | } 68 | Response response = chain.proceed(request); 69 | if (ComUtil.isNetworkConnected()) { 70 | int maxAge = 0; 71 | // 有网络时, 不缓存, 最大保存时长为0 72 | response.newBuilder() 73 | .header("Cache-Control", "public, max-age=" + maxAge) 74 | .removeHeader("Pragma") 75 | .build(); 76 | } else { 77 | // 无网络时,设置超时为1周 78 | int maxStale = 60 * 60 * 24 * 7; 79 | response.newBuilder() 80 | .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale) 81 | .removeHeader("Pragma") 82 | .build(); 83 | } 84 | return response; 85 | } 86 | }; 87 | 88 | private static OkHttpClient.Builder httpClient = 89 | new OkHttpClient.Builder() 90 | //设置超时 91 | .readTimeout(TIME_OUT, TimeUnit.SECONDS) 92 | .writeTimeout(TIME_OUT, TimeUnit.SECONDS) 93 | .connectTimeout(TIME_OUT, TimeUnit.SECONDS) 94 | // .addInterceptor(RequestInterceptor.getInstance())//网络请求 统一拦截 95 | .addInterceptor(getLogInterceptor()) 96 | .sslSocketFactory(getSSLSocketFactory()) 97 | //设置缓存 98 | .addNetworkInterceptor(cacheInterceptor) 99 | .addInterceptor(cacheInterceptor) 100 | .hostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); 101 | 102 | 103 | private static HttpLoggingInterceptor getLogInterceptor() { 104 | HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); 105 | if (ConstantUtils.isAppDebug()) { 106 | loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); 107 | } else { 108 | loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.NONE); 109 | } 110 | return loggingInterceptor; 111 | } 112 | 113 | /** 114 | * 不验证证书 115 | * 116 | * @return 117 | * @throws Exception 118 | */ 119 | private static SSLSocketFactory getSSLSocketFactory() { 120 | //创建一个不验证证书链的证书信任管理器。 121 | final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { 122 | @Override 123 | public void checkClientTrusted( 124 | java.security.cert.X509Certificate[] chain, 125 | String authType) throws CertificateException { 126 | } 127 | 128 | @Override 129 | public void checkServerTrusted( 130 | java.security.cert.X509Certificate[] chain, 131 | String authType) throws CertificateException { 132 | } 133 | 134 | @Override 135 | public java.security.cert.X509Certificate[] getAcceptedIssuers() { 136 | return new java.security.cert.X509Certificate[0]; 137 | } 138 | }}; 139 | 140 | final SSLContext sslContext; 141 | try { 142 | sslContext = SSLContext.getInstance("TLS"); 143 | sslContext.init(null, trustAllCerts, 144 | new java.security.SecureRandom()); 145 | return sslContext 146 | .getSocketFactory(); 147 | } catch (Exception e) { 148 | e.printStackTrace(); 149 | } 150 | return null; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/net/callback/ErrorListener.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.net.callback; 2 | 3 | 4 | 5 | public interface ErrorListener { 6 | void handleError(Throwable e); 7 | } -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/net/callback/RequestCallback.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.net.callback; 2 | 3 | import android.content.Context; 4 | import com.senon.lib_common.net.progress.ProgressCancelListener; 5 | import com.senon.lib_common.net.progress.ProgressDialogHandler; 6 | import io.reactivex.Observer; 7 | import io.reactivex.annotations.NonNull; 8 | import io.reactivex.disposables.Disposable; 9 | 10 | 11 | /** 12 | * RxJava 自定义回调 13 | */ 14 | public abstract class RequestCallback implements Observer,ProgressCancelListener { 15 | 16 | private ErrorListener errorListener; 17 | private Disposable mDisposable; 18 | private Context mContext; 19 | private boolean cancelable = false;//该dialog如果开启 则可以设置是否能够返回键取消请求 20 | private ProgressDialogHandler mProgressDialogHandler;//默认为null 即不开启dialog 21 | 22 | public RequestCallback(Context context,ErrorListener errorListener) { 23 | this(context,errorListener,false); 24 | } 25 | 26 | /** 27 | * 传这个构造默认开启dialog 28 | * @param cancelable 该dialog如果开启 则可以设置是否能够返回键取消请求 29 | */ 30 | public RequestCallback(Context context, ErrorListener errorListener, boolean cancelable) { 31 | this(context,errorListener,true,cancelable); 32 | } 33 | 34 | public RequestCallback(Context context, ErrorListener errorListener, boolean showDialog, boolean cancelable) { 35 | this.mContext = context; 36 | this.errorListener = errorListener; 37 | this.cancelable = cancelable; 38 | if(showDialog){ 39 | this.mProgressDialogHandler = new ProgressDialogHandler(context, this, cancelable); 40 | } 41 | } 42 | 43 | private void showProgressDialog() { 44 | if (mProgressDialogHandler != null) { 45 | mProgressDialogHandler.obtainMessage(ProgressDialogHandler.SHOW_PROGRESS_DIALOG).sendToTarget(); 46 | } 47 | } 48 | 49 | private void dismissProgressDialog() { 50 | if (mProgressDialogHandler != null) { 51 | mProgressDialogHandler.obtainMessage(ProgressDialogHandler.DISMISS_PROGRESS_DIALOG).sendToTarget(); 52 | mProgressDialogHandler = null; 53 | } 54 | } 55 | 56 | @Override 57 | public void onSubscribe(@NonNull Disposable d) { 58 | this.mDisposable = d; 59 | showProgressDialog(); 60 | 61 | } 62 | 63 | @Override 64 | public void onNext(@NonNull T t) { 65 | 66 | } 67 | 68 | @Override 69 | public void onError(@NonNull Throwable e) { 70 | e.printStackTrace(); 71 | dismissProgressDialog(); 72 | onCancelProgress(); 73 | 74 | if (errorListener != null) { 75 | errorListener.handleError(e); 76 | } 77 | } 78 | 79 | @Override 80 | public void onComplete() { 81 | dismissProgressDialog(); 82 | onCancelProgress(); 83 | } 84 | 85 | @Override 86 | public void onCancelProgress() { 87 | //如果处于订阅状态,则取消订阅 88 | if (!mDisposable.isDisposed()) { 89 | mDisposable.dispose(); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/net/callback/RxErrorHandler.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.net.callback; 2 | 3 | 4 | import com.senon.lib_common.utils.LogUtils; 5 | import com.senon.lib_common.utils.ToastUtil; 6 | 7 | /** 8 | * 做为网络请求异常监听 9 | * 可以在回调中做统一 处理方法,比如:收到服务器响应码为201 未登录 此时可以做直接跳转到登录注册页 10 | */ 11 | public class RxErrorHandler implements ErrorListener { 12 | 13 | private volatile static RxErrorHandler rxErrorHandler; 14 | 15 | private RxErrorHandler() { 16 | } 17 | 18 | public static RxErrorHandler getInstance() { 19 | if (rxErrorHandler == null) { 20 | synchronized (RxErrorHandler.class) { 21 | if (rxErrorHandler == null) { 22 | rxErrorHandler = new RxErrorHandler(); 23 | } 24 | } 25 | } 26 | return rxErrorHandler; 27 | } 28 | 29 | @Override 30 | public void handleError(Throwable throwable) { 31 | String errorString = throwable.toString(); 32 | LogUtils.e("网络错误信息为 ========>>>" + throwable.toString()); 33 | //返回的错误为空 34 | if (errorString == null) { 35 | ToastUtil.initToast("网络数据异常,请重试"); 36 | 37 | } else { 38 | //请求超时 39 | if (errorString.contains("TimeoutException") || errorString.contains("SocketTimeoutException")) { 40 | ToastUtil.initToast("网络请求超时,请重试"); 41 | } 42 | //能识别的请求异常,忽略不提示 43 | if (errorString.contains("SSLException")) { 44 | 45 | } 46 | //403、404等服务错误 47 | if (errorString.contains("ServiceConfigurationError") || errorString.contains("AuthenticatorException")) { 48 | ToastUtil.initToast("网络数据异常,请重试"); 49 | } 50 | //网络未连接 51 | if (errorString.contains("NetworkErrorException") || errorString.contains("NoConnectionPendingException") || errorString.contains("UnknownHostException")) { 52 | ToastUtil.initToast("您的网络不给力,请检查网络设置"); 53 | } 54 | //连接不到服务器 55 | if (errorString.contains("ConnectException")) { 56 | ToastUtil.initToast("网络连接失败"); 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/net/progress/ProgressCancelListener.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.net.progress; 2 | 3 | /** 4 | * 取消请求监听 5 | */ 6 | public interface ProgressCancelListener { 7 | void onCancelProgress(); 8 | } 9 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/net/progress/ProgressDialogHandler.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.net.progress; 2 | 3 | import android.content.Context; 4 | import android.content.DialogInterface; 5 | import android.os.Handler; 6 | import android.os.Message; 7 | 8 | import cn.pedant.SweetAlert.SweetAlertDialog; 9 | 10 | /** 11 | * Dialog的进度控制 12 | */ 13 | public class ProgressDialogHandler extends Handler { 14 | public static final int SHOW_PROGRESS_DIALOG = 1; 15 | public static final int DISMISS_PROGRESS_DIALOG = 2; 16 | 17 | private SweetAlertDialog sad; 18 | private Context context; 19 | private boolean cancelable; 20 | private ProgressCancelListener mProgressCancelListener; 21 | 22 | public ProgressDialogHandler(Context context, ProgressCancelListener mProgressCancelListener, boolean cancelable) { 23 | super(); 24 | this.context = context; 25 | this.mProgressCancelListener = mProgressCancelListener; 26 | this.cancelable = cancelable; 27 | } 28 | 29 | private void initProgressDialog() { 30 | if (sad == null) { 31 | sad = new SweetAlertDialog(context); 32 | sad.changeAlertType(SweetAlertDialog.PROGRESS_TYPE); 33 | sad.setTitleText("正在加载..."); 34 | sad.setCancelable(cancelable); 35 | 36 | if (cancelable) { 37 | sad.setOnCancelListener(new DialogInterface.OnCancelListener() { 38 | @Override 39 | public void onCancel(DialogInterface dialogInterface) { 40 | mProgressCancelListener.onCancelProgress(); 41 | } 42 | }); 43 | } 44 | 45 | if (!sad.isShowing()) { 46 | sad.show(); 47 | } 48 | } 49 | } 50 | 51 | private void dismissProgressDialog() { 52 | if (sad != null) { 53 | sad.dismiss(); 54 | sad = null; 55 | } 56 | } 57 | 58 | @Override 59 | public void handleMessage(Message msg) { 60 | switch (msg.what) { 61 | case SHOW_PROGRESS_DIALOG: 62 | initProgressDialog(); 63 | break; 64 | case DISMISS_PROGRESS_DIALOG: 65 | dismissProgressDialog(); 66 | break; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/utils/ActivityCollector.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.utils; 2 | 3 | import android.app.Activity; 4 | 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | 8 | /** 9 | * 一键退出App 10 | */ 11 | 12 | public class ActivityCollector { 13 | 14 | private static ActivityCollector activityCollector; 15 | 16 | public synchronized static ActivityCollector getInstance() { 17 | if (activityCollector == null) { 18 | activityCollector = new ActivityCollector(); 19 | } 20 | return activityCollector; 21 | } 22 | 23 | private Set allActivities; 24 | 25 | public void addActivity(Activity act) { 26 | if (allActivities == null) { 27 | allActivities = new HashSet<>(); 28 | } 29 | allActivities.add(act); 30 | } 31 | 32 | public void removeActivity(Activity act) { 33 | if (allActivities != null) { 34 | allActivities.remove(act); 35 | } 36 | } 37 | 38 | public void exitApp() { 39 | if (allActivities != null) { 40 | synchronized (allActivities) { 41 | for (Activity act : allActivities) { 42 | act.finish(); 43 | } 44 | } 45 | } 46 | android.os.Process.killProcess(android.os.Process.myPid()); 47 | System.exit(0); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/utils/ComUtil.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.utils; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.net.ConnectivityManager; 6 | import android.os.Build; 7 | import android.util.DisplayMetrics; 8 | import android.view.View; 9 | import java.text.SimpleDateFormat; 10 | import java.util.HashMap; 11 | import java.util.Iterator; 12 | import java.util.Map; 13 | import java.util.regex.Matcher; 14 | import java.util.regex.Pattern; 15 | 16 | /** 17 | * CommonUtils 18 | */ 19 | public class ComUtil { 20 | 21 | /** 22 | * 改变状态栏字体颜色 23 | * @param context 24 | * @param isBlack true黑色 false白色 25 | */ 26 | public static void changeStatusBarTextColor(Context context,boolean isBlack) { 27 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 28 | if (isBlack) { 29 | //设置状态栏黑色字体 30 | ((Activity)context).getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | 31 | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); 32 | } else { 33 | //恢复状态栏白色字体 34 | ((Activity)context).getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); 35 | 36 | } 37 | } 38 | } 39 | 40 | 41 | public static boolean isNumeric(String str) { 42 | Pattern pattern = Pattern.compile("^1[3|4|5|7|8|9][0-9]{9}$"); 43 | Matcher isNum = pattern.matcher(str); 44 | if (!isNum.matches()) { 45 | return false; 46 | } 47 | return true; 48 | } 49 | 50 | public static boolean isChinese(String str) { 51 | Pattern pattern = Pattern.compile("^[\\u4E00-\\u9FA5]+$"); 52 | Matcher isNum = pattern.matcher(str); 53 | if (!isNum.matches()) { 54 | return false; 55 | } 56 | return true; 57 | } 58 | 59 | //long型时间转换为字符串时间类型 60 | public static String longToString(Object longTime, String timeFormat) { 61 | SimpleDateFormat formatter = new SimpleDateFormat(timeFormat == null ? "yyyy-MM-dd" : timeFormat); 62 | long time = 0; 63 | if (longTime instanceof Integer || longTime instanceof Long) { 64 | return formatter.format(longTime); 65 | } else if (longTime instanceof String) { 66 | return formatter.format(Long.valueOf((String) longTime)); 67 | } 68 | return "时间获取错误"; 69 | } 70 | 71 | //检查是否有可用网络 72 | public static boolean isNetworkConnected() { 73 | ConnectivityManager connectivityManager = (ConnectivityManager) ConstantUtils.getAPPContext(). 74 | getSystemService(Context.CONNECTIVITY_SERVICE); 75 | assert connectivityManager != null; 76 | return connectivityManager.getActiveNetworkInfo() != null; 77 | } 78 | 79 | // 屏幕宽度(像素) 80 | public static int getScreenWidth(Context context) { 81 | DisplayMetrics metric = new DisplayMetrics(); 82 | ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(metric); 83 | return metric.widthPixels; 84 | } 85 | 86 | public static int getScreenHeight(Context context) { 87 | DisplayMetrics metric = new DisplayMetrics(); 88 | ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(metric); 89 | return metric.heightPixels; 90 | } 91 | 92 | public static String getMd5Str(HashMap map) { 93 | StringBuffer sb = new StringBuffer(); 94 | Iterator iterator = map.entrySet().iterator(); 95 | while (iterator.hasNext()) { 96 | Map.Entry entry = (Map.Entry) iterator.next(); 97 | sb.append(entry.getValue().toString()); 98 | } 99 | return MD5Utils.getMd5(sb.toString() ); 100 | } 101 | 102 | public static HashMap getMd5Str(String[] keyArray, String[] valueArray) { 103 | HashMap map = new HashMap<>(); 104 | StringBuffer sb = new StringBuffer(); 105 | if (keyArray.length != valueArray.length) { 106 | ToastUtil.initToast("key value长度不对应"); 107 | } else { 108 | for (int i = 0; i < keyArray.length; i++) { 109 | map.put(keyArray[i], valueArray[i]); 110 | } 111 | for (int i = 0; i < valueArray.length; i++) { 112 | sb.append(valueArray[i]); 113 | } 114 | map.put("secret", MD5Utils.getMd5(sb.toString())); 115 | return map; 116 | } 117 | return null; 118 | } 119 | 120 | 121 | } 122 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/utils/ConstantUtils.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.utils; 2 | 3 | import android.content.Context; 4 | import android.content.pm.ApplicationInfo; 5 | import android.content.pm.PackageManager; 6 | 7 | /** 8 | * 基础常量工具类 9 | * 获取工程ApplicationContext对象时强制使用 如下getAPPContext()方法获取 10 | */ 11 | public class ConstantUtils { 12 | private static Context context; 13 | /** 14 | * 初始化工具类 15 | * @param context 上下文 16 | */ 17 | public static void init(Context context) { 18 | ConstantUtils.context = context.getApplicationContext(); 19 | } 20 | 21 | /** 22 | * 获取ApplicationContext 23 | * @return ApplicationContext 24 | */ 25 | public static Context getAPPContext() { 26 | if (context != null) return context; 27 | throw new NullPointerException("u should init first"); 28 | } 29 | 30 | /** 31 | * 判断App是否是Debug版本 32 | * @return {@code true}: 是
{@code false}: 否 33 | */ 34 | public static boolean isAppDebug() { 35 | String packageName = context.getPackageName(); 36 | if (packageName == null || packageName.trim().length() == 0) 37 | return false; 38 | try { 39 | PackageManager pm = context.getPackageManager(); 40 | ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), 0); 41 | return ai != null && (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; 42 | } catch (PackageManager.NameNotFoundException e) { 43 | e.printStackTrace(); 44 | return false; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/utils/JsonServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.utils; 2 | 3 | import android.content.Context; 4 | import com.alibaba.android.arouter.facade.annotation.Route; 5 | import com.alibaba.android.arouter.facade.service.SerializationService; 6 | import com.google.gson.Gson; 7 | import java.lang.reflect.Type; 8 | 9 | 10 | // 如果需要传递自定义对象,新建一个类(并非自定义对象类),然后实现 SerializationService, 11 | // 并使用@Route注解标注(方便用户自行选择序列化方式),例如: 12 | @Route(path = "/service/json") 13 | public class JsonServiceImpl implements SerializationService { 14 | 15 | private Gson mGson; 16 | 17 | @Override 18 | public void init(Context context) { 19 | mGson = new Gson(); 20 | 21 | } 22 | 23 | @Override 24 | public T json2Object(String text, Class clazz) { 25 | checkJson(); 26 | return mGson.fromJson(text, clazz); 27 | } 28 | 29 | @Override 30 | public String object2Json(Object instance) { 31 | checkJson(); 32 | return mGson.toJson(instance); 33 | } 34 | 35 | @Override 36 | public T parseObject(String input, Type clazz) { 37 | checkJson(); 38 | return mGson.fromJson(input, clazz); 39 | } 40 | 41 | public void checkJson() { 42 | if (mGson == null) { 43 | mGson = new Gson(); 44 | } 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/utils/LogUtils.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.utils; 2 | 3 | import android.util.Log; 4 | import java.util.Locale; 5 | 6 | /** 7 | * 打印log日志工具类 8 | */ 9 | public class LogUtils { 10 | private static boolean LOG = true; 11 | private static boolean LOGV = true; 12 | private static boolean LOGD = true; 13 | private static boolean LOGI = true; 14 | private static boolean LOGW = true; 15 | private static boolean LOGE = true; 16 | 17 | public static void setLogEnable(boolean enable) { 18 | LOG = enable; 19 | } 20 | 21 | public static void v(String mess) { 22 | if (LOG && LOGV) { 23 | Log.v(getTag(), buildMessage(mess)); 24 | } 25 | } 26 | 27 | public static void d(String mess) { 28 | if (LOG && LOGD) { 29 | Log.d(getTag(), buildMessage(mess)); 30 | } 31 | } 32 | 33 | public static void i(String mess) { 34 | if (LOG && LOGI) { 35 | Log.i(getTag(), buildMessage(mess)); 36 | } 37 | } 38 | 39 | public static void w(String mess) { 40 | if (LOG && LOGW) { 41 | Log.w(getTag(), buildMessage(mess)); 42 | } 43 | } 44 | 45 | public static void e(String mess) { 46 | if (LOG && LOGE) { 47 | log(getTag(), buildMessage(mess)); 48 | } 49 | } 50 | 51 | public static void log(String tag, String msg){ 52 | if(msg.length() > 4000) { 53 | for(int i=0;i clazz = trace[i].getClass(); 69 | if (!clazz.equals(LogUtils.class)) { 70 | callingClass = trace[i].getClassName(); 71 | callingClass = callingClass.substring(callingClass 72 | .lastIndexOf('.') + 1); 73 | break; 74 | } 75 | } 76 | return callingClass; 77 | } 78 | 79 | private static String buildMessage(String msg) { 80 | StackTraceElement[] trace = new Throwable().fillInStackTrace() 81 | .getStackTrace(); 82 | String caller = ""; 83 | for (int i = 2; i < trace.length; i++) { 84 | Class clazz = trace[i].getClass(); 85 | if (!clazz.equals(LogUtils.class)) { 86 | caller = trace[i].getMethodName(); 87 | break; 88 | } 89 | } 90 | return String.format(Locale.US, "[%d] %s: %s", Thread.currentThread() 91 | .getId(), caller, msg); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/utils/MD5Utils.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.utils; 2 | 3 | import java.security.MessageDigest; 4 | import java.security.NoSuchAlgorithmException; 5 | 6 | /** 7 | * MD5工具 8 | */ 9 | public class MD5Utils { 10 | //静态方法,便于作为工具类 11 | public static String getMd5(String plainText) { 12 | try { 13 | MessageDigest md = MessageDigest.getInstance("MD5"); 14 | md.update(plainText.getBytes()); 15 | byte b[] = md.digest(); 16 | int i; 17 | StringBuffer buf = new StringBuffer(""); 18 | for (int offset = 0; offset < b.length; offset++) { 19 | i = b[offset]; 20 | if (i < 0) 21 | i += 256; 22 | if (i < 16) 23 | buf.append("0"); 24 | buf.append(Integer.toHexString(i)); 25 | } 26 | //32位加密 27 | return buf.toString(); 28 | // 16位的加密 29 | //return buf.toString().substring(8, 24); 30 | } catch (NoSuchAlgorithmException e) { 31 | e.printStackTrace(); 32 | return null; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/utils/PreferenceTool.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.utils; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import com.senon.lib_opensource.BuildConfig; 6 | import java.util.Map; 7 | 8 | /** 9 | * 共享参数工具类 10 | */ 11 | public class PreferenceTool { 12 | 13 | private static final String PREF_NAME = BuildConfig.APPLICATION_ID + "_preferences"; 14 | private static SharedPreferences.Editor editor; 15 | private static SharedPreferences pref; 16 | 17 | 18 | public static void init(Context context) { 19 | pref = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); 20 | editor = pref.edit(); 21 | } 22 | 23 | public static void clear() { 24 | if (editor == null) return; 25 | editor.clear(); 26 | } 27 | 28 | public static void commit() { 29 | if (editor == null) return; 30 | editor.commit(); 31 | } 32 | 33 | public static void apply() { 34 | if (editor == null) return; 35 | editor.apply(); 36 | } 37 | 38 | public static boolean contains(String key) { 39 | return pref != null && pref.contains(key); 40 | } 41 | 42 | public static float getFloat(String key, float defValue) { 43 | return pref != null ? pref.getFloat(key, defValue) : defValue; 44 | } 45 | 46 | public static int getInt(String key, int defValue) { 47 | return pref != null ? pref.getInt(key, defValue) : defValue; 48 | } 49 | 50 | public static long getLong(String key, long defValue) { 51 | return pref != null ? pref.getLong(key, defValue) : defValue; 52 | } 53 | 54 | public static String getString(String key) { 55 | return pref != null ? pref.getString(key, "") : ""; 56 | } 57 | 58 | public static String getString(String key, String defValue) { 59 | return pref != null ? pref.getString(key, defValue) : defValue; 60 | } 61 | 62 | public static boolean getBoolean(String key, boolean defValue) { 63 | return pref != null ? pref.getBoolean(key, defValue) : defValue; 64 | } 65 | 66 | public static Map getAll() { 67 | return pref != null ? pref.getAll() : null; 68 | } 69 | 70 | 71 | 72 | public static void putFloat(String key, float value) { 73 | if (editor == null) return; 74 | editor.putFloat(key, value); 75 | } 76 | 77 | public static void putInt(String key, int value) { 78 | if (editor == null) return; 79 | editor.putInt(key, value); 80 | } 81 | 82 | public static void putLong(String key, long value) { 83 | if (editor == null) return; 84 | editor.putLong(key, value); 85 | } 86 | 87 | public static void putString(String key, String value) { 88 | if (editor == null) return; 89 | editor.putString(key, value); 90 | } 91 | 92 | public static void putBoolean(String key, boolean value) { 93 | if (editor == null) return; 94 | editor.putBoolean(key, value); 95 | } 96 | 97 | public static void remove(String key) { 98 | if (editor == null) return; 99 | editor.remove(key); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/utils/RetryWithDelay.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.utils; 2 | 3 | 4 | import java.util.concurrent.TimeUnit; 5 | import io.reactivex.Observable; 6 | import io.reactivex.ObservableSource; 7 | import io.reactivex.annotations.NonNull; 8 | import io.reactivex.functions.Function; 9 | 10 | /** 11 | * 使用rxjava 网络重试 12 | */ 13 | public class RetryWithDelay implements Function, ObservableSource> { 14 | 15 | private final int maxRetries; 16 | private final int retryDelaySecond; 17 | private int retryCount; 18 | 19 | public RetryWithDelay(int maxRetries, int retryDelaySecond) { 20 | this.maxRetries = maxRetries; 21 | this.retryDelaySecond = retryDelaySecond; 22 | } 23 | 24 | @Override 25 | public ObservableSource apply(@NonNull Observable throwableObservable) throws Exception { 26 | return throwableObservable 27 | .flatMap(new Function>() { 28 | @Override 29 | public ObservableSource apply(@NonNull Throwable throwable) throws Exception { 30 | if (++retryCount <= maxRetries) { 31 | // When this Observable calls onNext, the original Observable will be retried (i.e. re-subscribed). 32 | LogUtils.d("get error, it will try after " + retryDelaySecond 33 | + " second, retry count " + retryCount); 34 | return Observable.timer(retryDelaySecond, 35 | TimeUnit.SECONDS); 36 | } 37 | // Max retries hit. Just pass the error along. 38 | return Observable.error(throwable); 39 | } 40 | }); 41 | } 42 | } -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/utils/RxUtils.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.utils; 2 | 3 | import com.senon.lib_common.base.BaseViewImp; 4 | import com.trello.rxlifecycle2.LifecycleTransformer; 5 | import com.trello.rxlifecycle2.components.support.RxAppCompatActivity; 6 | import com.trello.rxlifecycle2.components.support.RxFragment; 7 | 8 | import io.reactivex.Observable; 9 | import io.reactivex.ObservableSource; 10 | import io.reactivex.ObservableTransformer; 11 | import io.reactivex.android.schedulers.AndroidSchedulers; 12 | import io.reactivex.annotations.NonNull; 13 | import io.reactivex.schedulers.Schedulers; 14 | 15 | /** 16 | * 在presenter中使用生命周期管理,防止内存泄漏 17 | */ 18 | public class RxUtils { 19 | 20 | public static LifecycleTransformer bindToLifecycle(BaseViewImp view) { 21 | if (view instanceof RxAppCompatActivity) { 22 | return ((RxAppCompatActivity) view).bindToLifecycle(); 23 | } else if (view instanceof RxFragment) { 24 | return ((RxFragment) view).bindToLifecycle(); 25 | } else { 26 | throw new IllegalArgumentException("view isn't activity or fragment"); 27 | } 28 | } 29 | 30 | public static ObservableTransformer getSchedulerTransformer(){ 31 | return new ObservableTransformer() { 32 | @Override 33 | public ObservableSource apply(@NonNull Observable upstream) { 34 | return upstream 35 | .subscribeOn(Schedulers.io()) 36 | .unsubscribeOn(Schedulers.io()) 37 | .observeOn(AndroidSchedulers.mainThread()); 38 | } 39 | }; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/senon/lib_common/utils/ToastUtil.java: -------------------------------------------------------------------------------- 1 | package com.senon.lib_common.utils; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.os.Handler; 6 | import android.os.Looper; 7 | import android.support.annotation.NonNull; 8 | import android.support.v4.app.FragmentActivity; 9 | import android.view.Gravity; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.widget.TextView; 13 | import android.widget.Toast; 14 | 15 | import com.senon.lib_common.R; 16 | 17 | 18 | /** 19 | * Toast工具类 20 | */ 21 | public class ToastUtil { 22 | 23 | /** 24 | * 可以在多线程里运行的toast 25 | */ 26 | private static volatile Toast mToast; 27 | private static final Object lock = new Object(); 28 | private static Context context; 29 | private static TextView toastTv; 30 | 31 | public static void init(Context con){ 32 | context = con; 33 | } 34 | 35 | public static void initToast(String msg) { 36 | initToast(msg, context, true); 37 | } 38 | 39 | public static void initToast(int resId, Context context) { 40 | if (context instanceof Activity || context instanceof FragmentActivity) 41 | context = context.getApplicationContext(); 42 | initToast(context.getString(resId), context, true); 43 | } 44 | 45 | public static void initToast(String msg, Context context, boolean isSingleton) { 46 | if (context instanceof Activity || context instanceof FragmentActivity) 47 | context = context.getApplicationContext(); 48 | if (mToast != null && isSingleton) { 49 | toastTv.setText(msg); 50 | } else { 51 | synchronized (lock) { 52 | // mToast = Toast.makeText(context, msg, Toast.LENGTH_SHORT); 53 | mToast = showToast(context,msg,Toast.LENGTH_SHORT); 54 | } 55 | } 56 | mToast.setGravity(Gravity.CENTER, 0, 0);//默认显示位置 57 | mToast.show(); 58 | } 59 | //可以设置toast的位置 60 | public static void setGravity(int gravity, int xOffset, int yOffset) { 61 | mToast.setGravity(gravity, xOffset, yOffset); 62 | } 63 | //可以自定义toast的view 64 | public void setView(View view) { 65 | mToast.setView(view); 66 | } 67 | 68 | 69 | /** 70 | 当你在线程中使用toast时,请使用这个方法(可以控制显示多长时间) 71 | */ 72 | public static void showInThread(@NonNull final Context context, final String msg, final int length) { 73 | new Thread() { 74 | @Override 75 | public void run() { 76 | Looper.prepare();//先移除 77 | Toast.makeText(context, msg, length).show(); 78 | Looper.loop();// 进入loop中的循环,查看消息队列 79 | } 80 | }.start(); 81 | } 82 | 83 | /** 84 | * 以下全部代码为一个整体,可以控制显示时间的Toast 85 | */ 86 | private static Handler mHandler = null; 87 | private static int duration = 0; 88 | private static int currDuration = 0; 89 | private static final int DEFAULT = 2000; 90 | 91 | public static void showByDuration(Context context, String msg, int duration) { 92 | duration = duration; 93 | currDuration = DEFAULT; 94 | // mToast = Toast.makeText(context, msg, Toast.LENGTH_LONG); 95 | mToast = showToast(context,msg,Toast.LENGTH_LONG); 96 | mHandler = new Handler(context.getMainLooper()); 97 | mHandler.post(mToastThread); 98 | } 99 | 100 | private static Runnable mToastThread = new Runnable() { 101 | public void run() { 102 | mToast.show(); 103 | mHandler.postDelayed(mToastThread, DEFAULT);// 每隔2秒显示一次 104 | if (duration != 0) { 105 | if (currDuration <= duration) { 106 | currDuration += DEFAULT; 107 | } else { 108 | cancel(); 109 | } 110 | } 111 | } 112 | }; 113 | 114 | private static void cancel() { 115 | mHandler.removeCallbacks(mToastThread);// 先把显示线程删除 116 | mToast.cancel();// 把最后一个线程的显示效果cancel掉,就一了百了了 117 | currDuration = DEFAULT; 118 | } 119 | 120 | 121 | private static Toast showToast(Context context, String msg, int duration) { 122 | if (null != context) { 123 | if (mToast == null) { 124 | mToast = new Toast(context); 125 | LayoutInflater inflater = LayoutInflater.from(context); 126 | View layout = inflater.inflate(R.layout.common_toast_layout, null); 127 | toastTv = layout.findViewById(R.id.message); 128 | toastTv.setText(msg); 129 | mToast.setDuration(duration); 130 | mToast.setView(layout); 131 | }else { 132 | toastTv.setText(msg); 133 | } 134 | } 135 | return mToast; 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /lib_common/src/main/res/drawable/common_ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /lib_common/src/main/res/drawable/common_toast_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /lib_common/src/main/res/layout/common_activity_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 23 | 24 | 39 | 40 |