├── settings.gradle ├── templates ├── TinFragment │ ├── root │ │ ├── src │ │ │ └── app_package │ │ │ │ ├── TinFragment.kt.ftl │ │ │ │ ├── TinView.kt.ftl │ │ │ │ ├── TinPresenter.kt.ftl │ │ │ │ ├── TinView.java.ftl │ │ │ │ ├── TinPresenter.java.ftl │ │ │ │ └── TinFragment.java.ftl │ │ ├── AndroidManifest.xml.ftl │ │ └── res │ │ │ └── layout │ │ │ └── simple.xml.ftl │ ├── template_tin_activity.png │ ├── globals.xml.ftl │ ├── recipe.xml.ftl │ └── template.xml ├── TinActivity │ ├── root │ │ ├── src │ │ │ └── app_package │ │ │ │ ├── TinView.kt.ftl │ │ │ │ ├── TinPresenter.kt.ftl │ │ │ │ ├── TinView.java.ftl │ │ │ │ ├── TinPresenter.java.ftl │ │ │ │ ├── TinActivity.java.ftl │ │ │ │ └── TinActivity.kt.ftl │ │ ├── AndroidManifest.xml.ftl │ │ └── res │ │ │ └── layout │ │ │ └── simple.xml.ftl │ ├── template_tin_activity.png │ ├── globals.xml.ftl │ ├── recipe.xml.ftl │ └── template.xml ├── templates.zip ├── LhhActivity │ ├── template_lhh_activity.png │ ├── root │ │ ├── src │ │ │ └── app_package │ │ │ │ ├── SimpleActivity.kt.ftl │ │ │ │ └── SimpleActivity.java.ftl │ │ ├── AndroidManifest.xml.ftl │ │ └── res │ │ │ └── layout │ │ │ └── simple.xml.ftl │ ├── globals.xml.ftl │ ├── recipe.xml.ftl │ └── template.xml └── LhhFragment │ ├── root │ ├── src │ │ └── app_package │ │ │ ├── LhhFragment.kt.ftl │ │ │ └── LhhFragment.java.ftl │ └── res │ │ └── layout │ │ └── simple.xml.ftl │ ├── template_lhh_activity.png │ ├── globals.xml.ftl │ ├── recipe.xml.ftl │ └── template.xml ├── app ├── src │ ├── main │ │ ├── res │ │ │ ├── drawable-xxhdpi │ │ │ │ └── fanhui.png │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── xml │ │ │ │ └── network_security_config.xml │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── styles.xml │ │ │ │ └── dimens.xml │ │ │ ├── drawable │ │ │ │ ├── shape_rectangle_round_8.xml │ │ │ │ └── ic_launcher_background.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── layout │ │ │ │ ├── activity_main.xml │ │ │ │ ├── tool_bar.xml │ │ │ │ └── dialog_progress.xml │ │ │ └── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ ├── java │ │ │ └── me │ │ │ │ └── soushin │ │ │ │ └── tinmvp │ │ │ │ ├── Utils │ │ │ │ ├── DataUtils.java │ │ │ │ ├── TimeUtils.java │ │ │ │ ├── DialogUtils.java │ │ │ │ ├── TitleBarUtils.java │ │ │ │ ├── GsonUtils.java │ │ │ │ ├── AppUtils.java │ │ │ │ ├── SharedUtils.java │ │ │ │ └── ActivityUtils.java │ │ │ │ ├── module │ │ │ │ └── main │ │ │ │ │ ├── MainView.java │ │ │ │ │ ├── MainPresenter.java │ │ │ │ │ └── MainActivity.java │ │ │ │ ├── Configure.java │ │ │ │ ├── base │ │ │ │ ├── BaseView.java │ │ │ │ ├── BasePresenter.java │ │ │ │ ├── BaseDialog.java │ │ │ │ ├── BaseActivity.java │ │ │ │ └── BaseFragment.java │ │ │ │ ├── model │ │ │ │ ├── ResultModel.java │ │ │ │ └── SubcribeModel.java │ │ │ │ ├── widget │ │ │ │ ├── ToolHolder.java │ │ │ │ ├── ProgressDialog.java │ │ │ │ ├── ToastStyle.java │ │ │ │ └── InitProvider.java │ │ │ │ ├── App.java │ │ │ │ ├── ActivityLifeCycleCallBackIml.java │ │ │ │ └── network │ │ │ │ ├── HttpClient.java │ │ │ │ └── TokenInterceptor.java │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── me │ │ │ └── soushin │ │ │ └── tinmvp │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── me │ │ └── soushin │ │ └── tinmvp │ │ └── ExampleInstrumentedTest.java ├── proguard-rules.pro └── build.gradle ├── gradle.properties ├── gradlew.bat ├── README.md ├── gradlew └── config.gradle /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /templates/TinFragment/root/src/app_package/TinFragment.kt.ftl: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /templates/TinActivity/root/src/app_package/TinView.kt.ftl: -------------------------------------------------------------------------------- 1 | package ${contractPackageName} 2 | 3 | -------------------------------------------------------------------------------- /templates/TinFragment/root/src/app_package/TinView.kt.ftl: -------------------------------------------------------------------------------- 1 | package ${contractPackageName} 2 | 3 | -------------------------------------------------------------------------------- /templates/templates.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/932707629/TinMvp/HEAD/templates/templates.zip -------------------------------------------------------------------------------- /templates/TinActivity/root/src/app_package/TinPresenter.kt.ftl: -------------------------------------------------------------------------------- 1 | package ${presenterPackageName} 2 | 3 | -------------------------------------------------------------------------------- /templates/TinFragment/root/src/app_package/TinPresenter.kt.ftl: -------------------------------------------------------------------------------- 1 | package ${presenterPackageName} 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/fanhui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/932707629/TinMvp/HEAD/app/src/main/res/drawable-xxhdpi/fanhui.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/932707629/TinMvp/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/932707629/TinMvp/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/932707629/TinMvp/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/932707629/TinMvp/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/932707629/TinMvp/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /templates/LhhActivity/template_lhh_activity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/932707629/TinMvp/HEAD/templates/LhhActivity/template_lhh_activity.png -------------------------------------------------------------------------------- /templates/LhhFragment/root/src/app_package/LhhFragment.kt.ftl: -------------------------------------------------------------------------------- 1 | package ${fragmentPackageName} 2 | 3 | class ${pageName}Fragment{ 4 | 5 | } 6 | -------------------------------------------------------------------------------- /templates/LhhFragment/template_lhh_activity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/932707629/TinMvp/HEAD/templates/LhhFragment/template_lhh_activity.png -------------------------------------------------------------------------------- /templates/TinActivity/template_tin_activity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/932707629/TinMvp/HEAD/templates/TinActivity/template_tin_activity.png -------------------------------------------------------------------------------- /templates/TinFragment/template_tin_activity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/932707629/TinMvp/HEAD/templates/TinFragment/template_tin_activity.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/932707629/TinMvp/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/932707629/TinMvp/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/932707629/TinMvp/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/932707629/TinMvp/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/932707629/TinMvp/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /templates/TinActivity/root/src/app_package/TinView.java.ftl: -------------------------------------------------------------------------------- 1 | package ${viewPackageName}; 2 | 3 | public interface ${pageName}View extends BaseView { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /templates/TinFragment/root/src/app_package/TinView.java.ftl: -------------------------------------------------------------------------------- 1 | package ${viewPackageName}; 2 | 3 | public interface ${pageName}View extends BaseView { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /templates/LhhActivity/root/src/app_package/SimpleActivity.kt.ftl: -------------------------------------------------------------------------------- 1 | package ${ativityPackageName}; 2 | 3 | 4 | class ${pageName}Activity : BaseActivity() { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /templates/TinActivity/root/src/app_package/TinPresenter.java.ftl: -------------------------------------------------------------------------------- 1 | package ${presenterPackageName}; 2 | 3 | public class ${pageName}Presenter extends BasePresenter<${pageName}View> { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /templates/TinFragment/root/src/app_package/TinPresenter.java.ftl: -------------------------------------------------------------------------------- 1 | package ${presenterPackageName}; 2 | 3 | public class ${pageName}Presenter extends BasePresenter<${pageName}View> { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | TinMvp 3 | 4 | 5 | Hello blank fragment 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/Utils/DataUtils.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.Utils; 2 | 3 | 4 | /** 5 | * 数据工具类 6 | * 7 | * @author SouShin 8 | * @time 2018/11/14 14:51 9 | */ 10 | public class DataUtils { 11 | 12 | 13 | 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/module/main/MainView.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.module.main; 2 | 3 | import me.soushin.tinmvp.base.BaseView; 4 | 5 | /** 6 | * Created by SouShin on 2018/10/171342. 7 | */ 8 | public interface MainView extends BaseView { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_rectangle_round_8.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /templates/TinFragment/root/AndroidManifest.xml.ftl: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/Configure.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp; 2 | 3 | 4 | /** 5 | * 全局配置类 6 | * @author SouShin 7 | * @time 2018/11/19 14:45 8 | */ 9 | public class Configure { 10 | public static String BASE_URL="http://www.weather.com.cn/data/sk/";///101110101.html 11 | 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/module/main/MainPresenter.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.module.main; 2 | 3 | import me.soushin.tinmvp.base.BasePresenter; 4 | 5 | /** 6 | * Created by SouShin on 2018/10/171343. 7 | * Presenter 8 | */ 9 | public class MainPresenter extends BasePresenter { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /templates/LhhActivity/root/AndroidManifest.xml.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/base/BaseView.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.base; 2 | 3 | import me.soushin.tinmvp.widget.ProgressDialog; 4 | 5 | /** 6 | * View处理基类 7 | * @author SouShin 8 | * @time 2018/11/15 14:19 9 | */ 10 | public interface BaseView { 11 | void initView(); 12 | void showToasty(String msg); 13 | ProgressDialog getLoading(String msg); 14 | } 15 | -------------------------------------------------------------------------------- /templates/TinActivity/root/AndroidManifest.xml.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /templates/LhhActivity/root/res/layout/simple.xml.ftl: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /templates/LhhFragment/root/res/layout/simple.xml.ftl: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /templates/TinActivity/root/res/layout/simple.xml.ftl: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /templates/TinFragment/root/res/layout/simple.xml.ftl: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /templates/LhhFragment/globals.xml.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <#include "root://activities/common/common_globals.xml.ftl" /> 9 | 10 | -------------------------------------------------------------------------------- /templates/TinFragment/globals.xml.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <#include "root://activities/common/common_globals.xml.ftl" /> 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #2F98E9 4 | #2F98E9 5 | #FF4081 6 | #fff 7 | #000 8 | 9 | #333333 10 | #232323 11 | #bbbbbb 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/test/java/me/soushin/tinmvp/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp; 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() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /templates/LhhActivity/globals.xml.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | <#include "../common/common_globals.xml.ftl" /> 11 | 12 | -------------------------------------------------------------------------------- /templates/LhhActivity/root/src/app_package/SimpleActivity.java.ftl: -------------------------------------------------------------------------------- 1 | package ${ativityPackageName}; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | 6 | public class ${pageName}Activity extends BaseActivity { 7 | 8 | @Override 9 | protected int getLayout() { 10 | return R.layout.${activityLayoutName}; 11 | } 12 | 13 | @Override 14 | protected void initView(@Nullable Bundle savedInstanceState) { 15 | 16 | } 17 | 18 | @Override 19 | protected void initListener() { 20 | 21 | } 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /templates/TinActivity/globals.xml.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | <#include "../common/common_globals.xml.ftl" /> 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/model/ResultModel.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.model; 2 | 3 | import com.zhouyou.http.model.ApiResult; 4 | 5 | /** 6 | * 说好的返回Api 7 | * @author SouShin 8 | * @time 2018/11/21 13:50 9 | */ 10 | public class ResultModel extends ApiResult { 11 | T weatherinfo; 12 | 13 | @Override 14 | public T getData() { 15 | return weatherinfo; 16 | } 17 | 18 | @Override 19 | public void setData(T data) { 20 | this.weatherinfo=data; 21 | } 22 | 23 | @Override 24 | public boolean isOk() { 25 | return weatherinfo!=null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/Utils/TimeUtils.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.Utils; 2 | 3 | import android.annotation.SuppressLint; 4 | 5 | import java.text.SimpleDateFormat; 6 | import java.util.Date; 7 | 8 | /** 9 | * 时间转换类 10 | * 11 | * Author: nanchen 12 | * Email: liushilin520@foxmail.com 13 | * Date: 2017-06-23 13:19 14 | */ 15 | 16 | public class TimeUtils { 17 | 18 | public static String getNowStrTime(){ 19 | long time = System.currentTimeMillis(); 20 | @SuppressLint("SimpleDateFormat") 21 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 22 | return sdf.format(new Date(time)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /templates/TinActivity/root/src/app_package/TinActivity.java.ftl: -------------------------------------------------------------------------------- 1 | package ${ativityPackageName}; 2 | 3 | 4 | public class ${pageName}Activity extends BaseActivity<${pageName}View, ${pageName}Presenter> implements ${pageName}View { 5 | @Override 6 | protected ${pageName}Presenter createPresenter() { 7 | return new ${pageName}Presenter(); 8 | } 9 | @Override 10 | protected int getLayoutId() { 11 | return R.layout.${activityLayoutName}; 12 | } 13 | 14 | @Override 15 | protected void bindView() { 16 | getPresenter().subscribe(this); 17 | } 18 | 19 | @Override 20 | public void initView() { 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /templates/TinFragment/root/src/app_package/TinFragment.java.ftl: -------------------------------------------------------------------------------- 1 | package ${fragmentPackageName}; 2 | 3 | 4 | public class ${pageName}Fragment extends BaseFragment<${pageName}View, ${pageName}Presenter> implements ${pageName}View { 5 | 6 | @Override 7 | protected ${pageName}Presenter createPresenter() { 8 | return new ${pageName}Presenter(); 9 | } 10 | @Override 11 | protected int getLayoutId() { 12 | return R.layout.${fragmentLayoutName}; 13 | } 14 | 15 | @Override 16 | protected void bindView() { 17 | getPresenter().subscribe(this); 18 | } 19 | 20 | @Override 21 | public void initView() { 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/base/BasePresenter.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.base; 2 | 3 | /** 4 | * 逻辑处理基类 5 | */ 6 | public abstract class BasePresenter { 7 | private V View; 8 | 9 | /** 10 | * 绑定View 11 | * 设置状态栏 12 | * 调用initView() 13 | * @param View 14 | */ 15 | public void subscribe(V View){ 16 | this.View= View; 17 | getView().initView(); 18 | } 19 | 20 | /** 21 | * 解绑 22 | */ 23 | public void unsubscribe(){ 24 | View = null; 25 | } 26 | 27 | /** 28 | * 获取View 29 | * @return 30 | */ 31 | protected V getView(){ 32 | return View; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | -------------------------------------------------------------------------------- /templates/LhhFragment/recipe.xml.ftl: -------------------------------------------------------------------------------- 1 | 2 | <#import "root://activities/common/kotlin_macros.ftl" as kt> 3 | 4 | 5 | <#if needFragment && generateFragmentLayout> 6 | 8 | 9 | 10 | 11 | <#if needFragment> 12 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /templates/TinActivity/root/src/app_package/TinActivity.kt.ftl: -------------------------------------------------------------------------------- 1 | package ${escapeKotlinIdentifiers(packageName)} 2 | 3 | import ${superClassFqcn} 4 | import android.os.Bundle 5 | <#if (includeCppSupport!false) && generateLayout> 6 | import kotlinx.android.synthetic.main.${layoutName}.* 7 | 8 | 9 | class ${activityClass} : ${superClass}() { 10 | 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | super.onCreate(savedInstanceState) 13 | <#if generateLayout> 14 | setContentView(R.layout.${layoutName}) 15 | <#include "../../../../common/jni_code_usage.kt.ftl"> 16 | <#elseif includeCppSupport!false> 17 | 18 | // Example of a call to a native method 19 | android.util.Log.d("${activityClass}", stringFromJNI()) 20 | 21 | } 22 | <#include "../../../../common/jni_code_snippet.kt.ftl"> 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/widget/ToolHolder.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.widget; 2 | 3 | import android.support.constraint.ConstraintLayout; 4 | import android.view.View; 5 | import android.widget.ImageView; 6 | import android.widget.TextView; 7 | import butterknife.BindView; 8 | import butterknife.ButterKnife; 9 | import butterknife.Unbinder; 10 | import me.soushin.tinmvp.R; 11 | 12 | /** 13 | * 标题栏 14 | * @auther SouShin 15 | * @time 2019/1/24 09:11 16 | */ 17 | public class ToolHolder { 18 | @BindView(R.id.img_back) 19 | public ImageView imgBack; 20 | @BindView(R.id.tv_title) 21 | public TextView tvTitle; 22 | @BindView(R.id.toolbar) 23 | public ConstraintLayout toolbar; 24 | public Unbinder unbinder; 25 | 26 | public ToolHolder(View view) { 27 | unbinder=ButterKnife.bind(this, view); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/androidTest/java/me/soushin/tinmvp/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp; 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() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("soushin.mvpdemo", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/model/SubcribeModel.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.model; 2 | 3 | import io.reactivex.disposables.Disposable; 4 | 5 | /** 6 | * 订阅关系 7 | * @auther SouShin 8 | * @time 2019/2/25 14:20 9 | */ 10 | public class SubcribeModel { 11 | 12 | public SubcribeModel() { 13 | } 14 | 15 | public SubcribeModel(String className, Disposable mDisposable) { 16 | this.className = className; 17 | this.mDisposable = mDisposable; 18 | } 19 | 20 | private String className; 21 | private Disposable mDisposable; 22 | 23 | 24 | public String getClassName() { 25 | return className; 26 | } 27 | 28 | public void setClassName(String className) { 29 | this.className = className; 30 | } 31 | 32 | public Disposable getmDisposable() { 33 | return mDisposable; 34 | } 35 | 36 | public void setmDisposable(Disposable mDisposable) { 37 | this.mDisposable = mDisposable; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/widget/ProgressDialog.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.widget; 2 | 3 | import android.app.Dialog; 4 | import android.content.Context; 5 | import android.widget.ProgressBar; 6 | import android.widget.TextView; 7 | 8 | import com.zhouyou.http.subsciber.IProgressDialog; 9 | 10 | import butterknife.BindView; 11 | import me.soushin.tinmvp.R; 12 | import me.soushin.tinmvp.base.BaseDialog; 13 | 14 | /** 15 | * 加载等待框 16 | * 17 | * @author SouShin 18 | * @time 2018/10/31 9:49 19 | */ 20 | public class ProgressDialog extends BaseDialog implements IProgressDialog { 21 | 22 | @BindView(R.id.pb_progress) 23 | ProgressBar pbProgress; 24 | @BindView(R.id.tv_progress) 25 | TextView tvProgress; 26 | 27 | public ProgressDialog(Context context, String msg) { 28 | super(context, R.layout.dialog_progress); 29 | tvProgress.setText(msg); 30 | setCancelable(false); 31 | setCanceledOnTouchOutside(false); 32 | } 33 | 34 | @Override 35 | public Dialog getDialog() { 36 | return this; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /templates/LhhActivity/recipe.xml.ftl: -------------------------------------------------------------------------------- 1 | 2 | <#import "root://activities/common/kotlin_macros.ftl" as kt> 3 | 4 | <#include "../common/recipe_manifest.xml.ftl" /> 5 | <@kt.addAllKotlinDependencies /> 6 | 7 | <#if needActivity> 8 | 10 | 11 | 12 | <#if needActivity && generateActivityLayout> 13 | 15 | 16 | 17 | 18 | <#if needActivity> 19 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/module/main/MainActivity.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.module.main; 2 | 3 | import me.soushin.tinmvp.R; 4 | import me.soushin.tinmvp.Utils.ActivityUtils; 5 | import me.soushin.tinmvp.Utils.AppUtils; 6 | import me.soushin.tinmvp.base.BaseActivity; 7 | 8 | /** 9 | * View 10 | */ 11 | public class MainActivity extends BaseActivity implements MainView { 12 | 13 | @Override 14 | protected MainPresenter createPresenter() { 15 | return new MainPresenter(); 16 | } 17 | 18 | @Override 19 | protected int getLayoutId() { 20 | return R.layout.activity_main; 21 | } 22 | 23 | @Override 24 | protected void bindView() { 25 | getPresenter().subscribe(this); 26 | } 27 | 28 | @Override 29 | public void initView() { 30 | 31 | } 32 | 33 | @Override 34 | public void onBackPressedSupport() { 35 | // onBackPressed()已停用 请使用onBackPressedSupport代替 36 | if (AppUtils.doubleClickExit()) { 37 | ActivityUtils.AppExit(getActivity()); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 21 | 22 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/tool_bar.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 19 | 20 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/App.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.support.multidex.MultiDex; 6 | 7 | import com.blankj.ALog; 8 | import com.hjq.toast.ToastUtils; 9 | import com.zhouyou.http.EasyHttp; 10 | import com.zhouyou.http.cache.converter.SerializableDiskConverter; 11 | import com.zhouyou.http.cookie.CookieManger; 12 | 13 | import io.reactivex.Observable; 14 | import io.reactivex.Observer; 15 | import io.reactivex.disposables.Disposable; 16 | import io.reactivex.functions.Consumer; 17 | import io.reactivex.schedulers.Schedulers; 18 | import io.reactivex.subjects.Subject; 19 | import me.jessyan.autosize.AutoSizeConfig; 20 | import me.soushin.tinmvp.network.TokenInterceptor; 21 | import me.soushin.tinmvp.widget.ToastStyle; 22 | 23 | /** 24 | * @auther SouShin 25 | * @time 2019/4/18 17:13 26 | */ 27 | public class App extends Application { 28 | private static App app; 29 | 30 | @Override 31 | public void onCreate() { 32 | super.onCreate(); 33 | app = this; 34 | } 35 | 36 | /** 37 | * 获取app示例 38 | * 39 | * @return 40 | */ 41 | public static App getApp() { 42 | return app; 43 | } 44 | 45 | @Override 46 | protected void attachBaseContext(Context base) { 47 | super.attachBaseContext(base); 48 | MultiDex.install(base); 49 | } 50 | 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/base/BaseDialog.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.base; 2 | 3 | import android.app.Dialog; 4 | import android.content.Context; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | 8 | import butterknife.ButterKnife; 9 | import butterknife.Unbinder; 10 | import me.soushin.tinmvp.R; 11 | import me.soushin.tinmvp.Utils.DialogUtils; 12 | 13 | /** 14 | * Created by SouShin on 2018/8/111736. 15 | * 自定义dialog的基类 16 | */ 17 | public class BaseDialog extends Dialog { 18 | private Context mContext; 19 | private Unbinder unbinder; 20 | 21 | public BaseDialog(Context context, int layoutId) { 22 | this(context, layoutId, R.style.CustomDialog); 23 | } 24 | 25 | public BaseDialog(Context context, int layoutId, int styleId) { 26 | super(context, styleId); 27 | this.mContext = context; 28 | LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 29 | View view = inflater.inflate(layoutId, null); 30 | this.setContentView(view); 31 | DialogUtils.addDialog(this); 32 | unbinder = ButterKnife.bind(this); 33 | } 34 | 35 | protected Context getContext_() { 36 | return this.mContext; 37 | } 38 | 39 | @Override 40 | public void dismiss() { 41 | super.dismiss(); 42 | if (unbinder != null) { 43 | unbinder.unbind(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/Utils/DialogUtils.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.Utils; 2 | 3 | import android.app.Dialog; 4 | 5 | import com.zhouyou.http.EasyHttp; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Iterator; 9 | import java.util.List; 10 | 11 | import me.soushin.tinmvp.base.BaseDialog; 12 | import me.soushin.tinmvp.model.SubcribeModel; 13 | import me.soushin.tinmvp.widget.ProgressDialog; 14 | 15 | /** 16 | * @auther SouShin 17 | * @time 2019/2/22 10:37 18 | */ 19 | public class DialogUtils { 20 | private static List dialogList = new ArrayList<>(); 21 | 22 | /** 23 | * @param msg 24 | * @return 25 | */ 26 | public static ProgressDialog getLoading(String msg) { 27 | return new ProgressDialog(ActivityUtils.currentActivity(), msg); 28 | } 29 | 30 | /** 31 | * 关闭弹出框 32 | * 最好在onDestroy里调用一下这个方法 33 | */ 34 | public static void disDialog() { 35 | Iterator it = dialogList.iterator(); 36 | while (it.hasNext()) { 37 | BaseDialog dialog = it.next(); 38 | disDialog(dialog); 39 | it.remove(); 40 | } 41 | } 42 | 43 | public static void addDialog(BaseDialog dialog) { 44 | dialogList.add(dialog); 45 | } 46 | 47 | public static void disDialog(Dialog dialog) { 48 | if (dialog != null && dialog.isShowing()) { 49 | dialog.dismiss(); 50 | } 51 | } 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /templates/LhhFragment/root/src/app_package/LhhFragment.java.ftl: -------------------------------------------------------------------------------- 1 | package ${fragmentPackageName}; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | 6 | public class ${pageName}Fragment extends BaseFragment { 7 | 8 | @Override 9 | public int initLayout() { 10 | return R.layout.${fragmentLayoutName}; 11 | } 12 | 13 | @Override 14 | protected void initView(@Nullable Bundle savedInstanceState) { 15 | 16 | } 17 | 18 | @Override 19 | protected void initListener() { 20 | 21 | } 22 | 23 | @Override 24 | public void onLazyInitView(@Nullable Bundle savedInstanceState) { 25 | super.onLazyInitView(savedInstanceState); 26 | // 只有第一次可见时调用 27 | // ALog.i("懒加载",getClass().getSimpleName()); 28 | } 29 | 30 | @Override 31 | public void onSupportVisible() { 32 | super.onSupportVisible(); 33 | // ALog.i("fragment可见",getClass().getSimpleName()); 34 | } 35 | 36 | @Override 37 | public void onSupportInvisible() { 38 | super.onSupportInvisible(); 39 | // ALog.i("fragment隐藏",getClass().getSimpleName()); 40 | } 41 | 42 | @Override 43 | public void initImmersionBar() { 44 | // 新手指引: fragment里设置沉浸标题栏之前必须先在宿主Activity里调用ImmersionBar.with(this).init(); 45 | } 46 | 47 | @Override 48 | public boolean immersionBarEnabled() { 49 | // 当为true的时候才可以执行initImmersionBar方法 50 | return false; 51 | } 52 | 53 | 54 | } -------------------------------------------------------------------------------- /templates/TinFragment/recipe.xml.ftl: -------------------------------------------------------------------------------- 1 | 2 | <#import "root://activities/common/kotlin_macros.ftl" as kt> 3 | 4 | <@kt.addAllKotlinDependencies /> 5 | 6 | <#if needFragment && generateFragmentLayout> 7 | 9 | 10 | 11 | 12 | <#if needFragment> 13 | 15 | 16 | 17 | 18 | <#if needView> 19 | 21 | 22 | 23 | 24 | <#if needPresenter> 25 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/widget/ToastStyle.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.widget; 2 | 3 | import android.view.Gravity; 4 | 5 | import com.hjq.toast.IToastStyle; 6 | 7 | /** 8 | * 自定义Toast样式 9 | * 10 | * @auther SouShin 11 | * @time 2018/12/27 09:45 12 | */ 13 | public class ToastStyle implements IToastStyle { 14 | 15 | @Override 16 | public int getGravity() { 17 | return Gravity.CENTER_HORIZONTAL|Gravity.BOTTOM; 18 | } 19 | 20 | @Override 21 | public int getXOffset() { 22 | return 0; 23 | } 24 | 25 | @Override 26 | public int getYOffset() { 27 | return 240; 28 | } 29 | 30 | @Override 31 | public int getZ() { 32 | return 30; 33 | } 34 | 35 | @Override 36 | public int getCornerRadius() { 37 | return 5; 38 | } 39 | 40 | @Override 41 | public int getBackgroundColor() { 42 | return 0XEE575757; 43 | } 44 | 45 | @Override 46 | public int getTextColor() { 47 | return 0XFFFFFFFF; 48 | } 49 | 50 | @Override 51 | public float getTextSize() { 52 | return 14; 53 | } 54 | 55 | @Override 56 | public int getMaxLines() { 57 | return 3; 58 | } 59 | 60 | @Override 61 | public int getPaddingLeft() { 62 | return 20; 63 | } 64 | 65 | @Override 66 | public int getPaddingTop() { 67 | return 8; 68 | } 69 | 70 | @Override 71 | public int getPaddingRight() { 72 | return getPaddingLeft(); 73 | } 74 | 75 | @Override 76 | public int getPaddingBottom() { 77 | return getPaddingTop(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /templates/TinActivity/recipe.xml.ftl: -------------------------------------------------------------------------------- 1 | 2 | <#import "root://activities/common/kotlin_macros.ftl" as kt> 3 | 4 | <@kt.addAllKotlinDependencies /> 5 | <#if needActivity> 6 | 8 | 9 | 10 | <#if needActivity && generateActivityLayout> 11 | 13 | 14 | 15 | 16 | <#if needActivity> 17 | 19 | 20 | 21 | 22 | <#if needView> 23 | 25 | 26 | 27 | 28 | <#if needPresenter> 29 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/base/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.base; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | 7 | import com.blankj.ALog; 8 | import com.hjq.toast.ToastUtils; 9 | 10 | import butterknife.ButterKnife; 11 | import butterknife.Unbinder; 12 | import me.soushin.tinmvp.Utils.DialogUtils; 13 | import me.soushin.tinmvp.widget.ProgressDialog; 14 | import me.yokeyword.fragmentation.SupportActivity; 15 | 16 | /** 17 | * Activity基类 18 | * 19 | * @author SouShin 20 | * @time 2018/11/16 14:05 21 | */ 22 | public abstract class BaseActivity> extends SupportActivity { 23 | private P presenter; 24 | private Unbinder unbinder; 25 | 26 | @Override 27 | protected void onCreate(@Nullable Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(getLayoutId()); 30 | unbinder = ButterKnife.bind(this); 31 | presenter = createPresenter(); 32 | bindView(); 33 | } 34 | 35 | protected abstract P createPresenter(); 36 | 37 | protected abstract int getLayoutId(); 38 | 39 | //这个方法是为了让用户不要忘了绑定BaseView 40 | protected abstract void bindView(); 41 | 42 | protected P getPresenter() { 43 | return presenter; 44 | } 45 | 46 | public AppCompatActivity getActivity() { 47 | return this; 48 | } 49 | 50 | public void showToasty(String msg) { 51 | ToastUtils.show(msg); 52 | } 53 | 54 | public ProgressDialog getLoading(String msg){ 55 | return DialogUtils.getLoading(msg); 56 | } 57 | 58 | @Override 59 | protected void onDestroy() { 60 | super.onDestroy(); 61 | unbinder.unbind(); 62 | if (getPresenter() != null) { 63 | getPresenter().unsubscribe(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_progress.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 20 | 21 | 31 | 32 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /templates/LhhActivity/template.xml: -------------------------------------------------------------------------------- 1 | 2 | 74 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 18 | 19 | 22 | 23 | 26 | 27 | 30 | 31 | 34 | 35 | 38 | 41 | 42 | 47 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /templates/LhhFragment/template.xml: -------------------------------------------------------------------------------- 1 | 2 | 74 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/Utils/TitleBarUtils.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.Utils; 2 | 3 | import android.app.Activity; 4 | import android.view.View; 5 | 6 | import com.gyf.barlibrary.ImmersionBar; 7 | 8 | import me.soushin.tinmvp.R; 9 | import me.soushin.tinmvp.widget.ToolHolder; 10 | 11 | /** 12 | * 标题栏工具类 13 | * 标题栏样式较多,可以把所有样式放到这里,方便调用 14 | * 15 | * @auther SouShin 16 | * @time 2019/1/24 09:16 17 | */ 18 | public class TitleBarUtils { 19 | private ToolHolder toolHolder; 20 | /** 21 | * 适用于有标题栏有状态栏的情况 22 | * BarParams里面有ImmersionBar各项设置详细说明 23 | * ImmersionBar是设置标题栏和状态栏背景的工具类(https://github.com/gyf-dev/ImmersionBar) 24 | * 支持设置标题栏和状态栏渐变/纯色/白底黑字 25 | * 26 | * @param activity 上下文 27 | * @param isBack 需/不需要返回按钮 28 | * @param title 标题 29 | */ 30 | 31 | public void setToolBar(final Activity activity, boolean isBack, String title) { 32 | View toolBar = activity.findViewById(R.id.toolbar); 33 | if (toolBar==null){ 34 | throw new NullPointerException("新手指引:请在activity布局文件里添加"); 35 | } 36 | ImmersionBar.with(activity) 37 | .statusBarColor(R.color.colorPrimary) 38 | .titleBar(toolBar) 39 | .init(); 40 | toolHolder = new ToolHolder(toolBar); 41 | toolHolder.tvTitle.setText(title); 42 | toolHolder.imgBack.setVisibility(isBack ? View.VISIBLE : View.GONE); 43 | toolHolder.imgBack.setOnClickListener(new View.OnClickListener() { 44 | @Override 45 | public void onClick(View v) { 46 | activity.onBackPressed(); 47 | } 48 | }); 49 | } 50 | 51 | /** 52 | * 适用于无标题栏只有状态栏的情况 53 | * 包括设置状态栏透明 54 | * statusBarDarkFont(true,1) 状态栏黑字 55 | * fitsSystemWindows(true)解决布局与状态栏重叠问题 56 | * @param activity 上下文 57 | */ 58 | public void setToolBar(Activity activity){ 59 | ImmersionBar.with(activity) 60 | .statusBarColor(R.color.colorAccent) 61 | .fitsSystemWindows(true) 62 | .init(); 63 | } 64 | 65 | /** 66 | * 解除绑定 67 | */ 68 | public void Unbind(){ 69 | if (toolHolder!=null){ 70 | toolHolder.unbinder.unbind(); 71 | toolHolder.unbinder=null; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16dp 5 | 16dp 6 | 7 | 0dp 8 | 1dp 9 | 2dp 10 | 3dp 11 | 4dp 12 | 5dp 13 | 8dp 14 | 10dp 15 | 12dp 16 | 13dp 17 | 18 | 15dp 19 | 18dp 20 | 21 | 20dp 22 | 25dp 23 | 30dp 24 | 32dp 25 | 35dp 26 | 40dp 27 | 45dp 28 | 50dp 29 | 55dp 30 | 31 | 60dp 32 | 70dp 33 | 74dp 34 | 75dp 35 | 78dp 36 | 80dp 37 | 85dp 38 | 39 | 90dp 40 | 100dp 41 | 105dp 42 | 120dp 43 | 125dp 44 | 150dp 45 | 180dp 46 | 200dp 47 | 240dp 48 | 250dp 49 | 300dp 50 | 51 | 52 | 10sp 53 | 12sp 54 | 13sp 55 | 14sp 56 | 15sp 57 | 16sp 58 | 17sp 59 | 18sp 60 | 20sp 61 | 24sp 62 | 30sp 63 | 64 | 1px 65 | 66 | -------------------------------------------------------------------------------- /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 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 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 Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.android["compileSdkVersion"] 5 | defaultConfig { 6 | applicationId "me.soushin.tinmvp" 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 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | multiDexEnabled true 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | android.applicationVariants.all { variant -> 21 | variant.outputs.all { 22 | // 自定义文件名{示例:AppName-Flavor-debug-v1.0.0_201807301409} 23 | outputFileName = "tinmvp-${variant.flavorName}-${variant.buildType.name}-v${variant.versionName}_${buildTime()}.apk" 24 | } 25 | } 26 | } 27 | 28 | def buildTime() { 29 | def date = new Date() 30 | def formattedDate = date.format('yyyyMMdd') 31 | return formattedDate 32 | } 33 | 34 | dependencies { 35 | implementation fileTree(dir: 'libs', include: ['*.jar']) 36 | implementation rootProject.ext.dependencies["appcompat-v7"] 37 | implementation rootProject.ext.dependencies["recyclerview-v7"] 38 | implementation rootProject.ext.dependencies["constraint-layout"] 39 | implementation rootProject.ext.dependencies["alog"] 40 | implementation rootProject.ext.dependencies["autosize"] 41 | implementation rootProject.ext.dependencies["BaseRecyclerViewAdapterHelper"] 42 | implementation rootProject.ext.dependencies["rxeasyhttp"] 43 | implementation rootProject.ext.dependencies["glide"] 44 | implementation rootProject.ext.dependencies["eventbus"] 45 | implementation rootProject.ext.dependencies["rxpermissions"] 46 | implementation rootProject.ext.dependencies["immersionbar"] 47 | implementation rootProject.ext.dependencies["butterknife"] 48 | implementation rootProject.ext.dependencies["fragmentation"] 49 | implementation rootProject.ext.dependencies["ToastUtils"] 50 | annotationProcessor rootProject.ext.dependencies["butterknife-compiler"] 51 | testImplementation rootProject.ext.dependencies["junit"] 52 | androidTestImplementation rootProject.ext.dependencies["runner"] 53 | androidTestImplementation rootProject.ext.dependencies["espresso-core"] 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/ActivityLifeCycleCallBackIml.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp; 2 | 3 | import android.app.Activity; 4 | import android.app.Application; 5 | import android.os.Bundle; 6 | import com.blankj.ALog; 7 | import com.gyf.barlibrary.ImmersionBar; 8 | import me.soushin.tinmvp.Utils.ActivityUtils; 9 | import me.soushin.tinmvp.Utils.AppUtils; 10 | import me.soushin.tinmvp.Utils.DialogUtils; 11 | import me.soushin.tinmvp.Utils.TitleBarUtils; 12 | import me.soushin.tinmvp.module.main.MainActivity; 13 | import me.soushin.tinmvp.network.HttpClient; 14 | 15 | /** 16 | * activity生命周期管理类 17 | * 你想象力有多丰富,这里就有多强大, 18 | * 以前放到BaseActivity的操作都可以放到这里 19 | * 使用:registerActivityLifecycleCallbacks(new ActivityLifeCycleCallBackIml()); 20 | * 21 | * @author SouShin 22 | * @time 2018/12/10 15:38 23 | */ 24 | public class ActivityLifeCycleCallBackIml implements Application.ActivityLifecycleCallbacks { 25 | private TitleBarUtils titleBar; 26 | @Override 27 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) { 28 | ALog.i("activity生命周期管理类",activity.getLocalClassName()); 29 | ActivityUtils.addActivity(activity); 30 | } 31 | 32 | @Override 33 | public void onActivityStarted(final Activity activity) { 34 | // ALog.i("activity生命周期管理类", "onActivityStarted"); 35 | //这里根据不同的activity显示不同的topBar 36 | titleBar=new TitleBarUtils(); 37 | if (activity instanceof MainActivity) { 38 | titleBar.setToolBar(activity,false,AppUtils.getString(R.string.app_name)); 39 | } 40 | } 41 | 42 | @Override 43 | public void onActivityResumed(Activity activity) { 44 | // ALog.i("activity生命周期管理类", "onActivityResumed"); 45 | } 46 | 47 | @Override 48 | public void onActivityPaused(Activity activity) { 49 | // ALog.i("activity生命周期管理类", "onActivityPaused"); 50 | } 51 | 52 | @Override 53 | public void onActivityStopped(Activity activity) { 54 | // ALog.i("activity生命周期管理类", "onActivityStopped"); 55 | } 56 | 57 | @Override 58 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) { 59 | // ALog.i("activity生命周期管理类", "onActivitySaveInstanceState"); 60 | } 61 | 62 | @Override 63 | public void onActivityDestroyed(Activity activity) { 64 | ALog.i("activity生命周期管理类", "onActivityDestroyed"); 65 | ImmersionBar.with(activity).destroy(); //必须调用该方法,防止内存泄漏 66 | ActivityUtils.removeActivity(activity); 67 | HttpClient.disposeRequest(activity.getLocalClassName()); 68 | DialogUtils.disDialog(); 69 | titleBar.Unbind(); 70 | titleBar=null; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/Utils/GsonUtils.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.Utils; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonArray; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonParser; 7 | import com.google.gson.reflect.TypeToken; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | /** 14 | * author : guof 15 | * Description : 16 | * date : 2018/8/6 17 | * Copyright (c) 2014-2017 银湾技术二部 18 | */ 19 | 20 | public class GsonUtils { 21 | private static Gson gson; 22 | 23 | public static Gson getGson() { 24 | if (gson == null) { 25 | gson = new Gson(); 26 | } 27 | return gson; 28 | } 29 | 30 | /** 31 | * 将object对象转成json字符串 32 | * 33 | * @param object 34 | * @return 35 | */ 36 | public static String toString(Object object) { 37 | return getGson().toJson(object); 38 | } 39 | 40 | /** 41 | * 将gsonString转成泛型bean 42 | * 43 | * @param gsonString 44 | * @param cls 45 | * @return 46 | */ 47 | public static T toBean(String gsonString, Class cls) { 48 | return getGson().fromJson(gsonString, cls); 49 | } 50 | 51 | /** 52 | * 转成list 53 | * 此方法不可用 54 | * 泛型在编译期类型被擦除导致报错 55 | * @param gsonString 56 | * @param cls 57 | * @return 58 | public static List toList(String gsonString, Class cls) { 59 | return getGson().fromJson(gsonString, new TypeToken>() {}.getType()); 60 | }*/ 61 | 62 | /** 63 | * 转成list 64 | * 解决泛型问题 65 | * @param json 66 | * @param cls 67 | * @param 68 | * @return 69 | */ 70 | public static List toList(String json, Class cls) { 71 | List list = new ArrayList(); 72 | JsonArray array = new JsonParser().parse(json).getAsJsonArray(); 73 | for(final JsonElement elem : array){ 74 | list.add(getGson().fromJson(elem, cls)); 75 | } 76 | return list; 77 | } 78 | 79 | /** 80 | * 转成list中有map的 81 | * 82 | * @param gsonString 83 | * @return 84 | */ 85 | public static List> toListMaps(String gsonString) { 86 | return getGson().fromJson(gsonString, new TypeToken>>(){}.getType()); 87 | } 88 | 89 | /** 90 | * 转成map的 91 | * 92 | * @param gsonString 93 | * @return 94 | */ 95 | public static Map toMaps(String gsonString) { 96 | return getGson().fromJson(gsonString, new TypeToken>(){}.getType()); 97 | } 98 | 99 | 100 | } 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## TinMvp ## 2 | 3 | ### 说在前面 ### 4 | 虽然已经在项目中添加了比较详细的代码注释,但是肯定还有很多需要注意的地方没有解释清楚,希望大家在使用的过程中如果发现了什么问题,及时提出来,大家共同解决. 5 | 6 | ### 功能列表 ### 7 | 8 | 1. 解决屏幕适配问题,适配全面屏/刘海屏(AndroidAutoSize是代替AndroidAutoLayout的屏幕适配框架,原理是基于今日头条的适配方案) 9 | 2. 代码解耦,给baseActivity/baseFragment减压 10 | 3. 使用堆栈对activity进行统一管理,ActivityUtil封装了各种常用方法 11 | 4. Activity标题栏统一设置,支持标题栏和状态栏统一设置背景color/shape/drawable 12 | 5. 支持fragment设置状态栏沉浸式,多fragment无缝切换 13 | 6. 解决fragment重叠的bug,fragment任务栈统一管理 14 | 7. 提供懒加载onLazyInitView()/fragment可见性onSupportVisible()/onSupportInvisible()方法回调 15 | 8. SmartRefresh和BaseRecyclerViewAdapterHelper相结合,提供封装的baseAdapter,列表刷新加载更简单 16 | 9. 使用RxEasyHttp网络框架链式调用,与Rxjava2相结合,线程智能控制 17 | 10. 解决Toast禁用通知权限不能弹出的bug,不分主次线程,可自定义Toast样式 18 | 11. 使用bottombar优化用户体验 19 | 12. 支持新手指引,编程中使用不规范的地方,会引导你正确使用 20 | 13. 使用插件一键生成Activity/Fragment 21 | 14. 依赖RxPermission,权限申请更简单 22 | 15. 使用EventBus,事件传递更加清晰 23 | 24 | 25 | ### 开发准备 ### 26 | 27 | 必须的项目配置,框架初始化都已添加,所以直接复制本项目更改报名,即可进行开发使用 28 | 29 | ### 开发指南 ### 30 | 31 | 使用的第三方框架: 32 | 33 | [BottomBar](https://github.com/roughike/BottomBar "BottomBar") 34 | 35 | [Fragmentation](https://github.com/YoKeyword/Fragmentation "Fragmentation") 36 | 37 | [ImmersionBar](https://github.com/gyf-dev/ImmersionBar "ImmersionBar") 38 | 39 | [AndroidAutoSize](https://github.com/JessYanCoding/AndroidAutoSize) 40 | 41 | [RxEasyHttp](https://github.com/zhou-you/RxEasyHttp "RxEasyHttp") 42 | 43 | [ToastUtils](https://github.com/getActivity/ToastUtils "ToastUtils") 44 | 45 | [BaseRecyclerViewAdapterHelper](https://github.com/CymChad/BaseRecyclerViewAdapterHelper "BaseRecyclerViewAdapterHelper") 46 | 47 | [butterknife](https://github.com/JakeWharton/butterknife "butterknife") 48 | 49 | [SmartRefreshLayout](https://github.com/scwang90/SmartRefreshLayout "SmartRefreshLayout") 50 | 51 | [ALog](https://github.com/Blankj/ALog "ALog") 52 | 53 | [RxPermissions](https://github.com/tbruyelle/RxPermissions "RxPermissions") 54 | 55 | [EventBus](https://github.com/greenrobot/EventBus "EventBus") 56 | 57 | ### 使用插件一键生成Activity/Fragment ### 58 | 59 | templates.zip这个压缩文件里放着TinActivity和TinFragment两个文件夹 60 | 61 | 首先解压得到这两个文件夹 62 | 63 | TinActivity放到AS安装目录\plugins\android\lib\templates\activities里 64 | 65 | TinFragment放到AS安装目录\plugins\android\lib\templates\other里 66 | 67 | 放进去之后重启AS,然后在需要建Activity/Fragment的地方,右键--New--Activity/Fragment--TinActivity/TinFragment,然后给Activity/Fragment取个名字,点击Finish,欧克,打完收功! 68 | 69 | ### 注意: ### 70 | 71 | 72 | 1. 设置标题栏和状态栏是在ActivityLifeCycleCallBackIml类里实现的,还进行了其他初始化与销毁业务,实现了对baseActivity的解耦,另外,设置标题栏时要在activity对应的layout里include标题栏布局 73 | 74 | 2. 如果要在fragment里设置状态栏沉浸,可以让该fragment实现SimpleImmersionOwner接口,或者实现ImmersionOwner接口,具体实现可以参考ImmersionBar的demo使用 75 | 76 | 3. 对于一个activity加载多fragment或者一个fragment加载多fragment的需求,可以参考MainActivity里的代码,activity与fragment场景使用方法大同小异,其他需求可参考Fragmentation的demo使用 77 | 78 | 4. 三方库的初始化是在Application里进行的,建议能在子线程初始化的在子线程初始化,RxEasyHttp的初始化默认被注释了,请先设置BASE_URL之后,再取消这段代码的注释. 79 | 80 | 5. 项目的基本用法演示都会放在Demo文件夹中供大家随时查阅. 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /templates/TinFragment/template.xml: -------------------------------------------------------------------------------- 1 | 2 | 107 | -------------------------------------------------------------------------------- /templates/TinActivity/template.xml: -------------------------------------------------------------------------------- 1 | 2 | 108 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/widget/InitProvider.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.widget; 2 | 3 | import android.app.Application; 4 | import android.content.ContentProvider; 5 | import android.content.ContentValues; 6 | import android.database.Cursor; 7 | import android.net.Uri; 8 | import android.support.annotation.NonNull; 9 | import android.support.annotation.Nullable; 10 | 11 | import com.blankj.ALog; 12 | import com.hjq.toast.ToastUtils; 13 | import com.zhouyou.http.EasyHttp; 14 | import com.zhouyou.http.cache.converter.SerializableDiskConverter; 15 | import com.zhouyou.http.cookie.CookieManger; 16 | 17 | import me.soushin.tinmvp.ActivityLifeCycleCallBackIml; 18 | import me.soushin.tinmvp.BuildConfig; 19 | import me.soushin.tinmvp.Configure; 20 | import me.soushin.tinmvp.network.TokenInterceptor; 21 | 22 | /** 23 | * 通过声明 {@link ContentProvider} 自动完成应用初始化 24 | * @auther SouShin 25 | * @time 2019/4/18 17:04 26 | */ 27 | public class InitProvider extends ContentProvider { 28 | @Override 29 | public boolean onCreate() { 30 | initApp((Application) getContext().getApplicationContext()); 31 | return true; 32 | } 33 | 34 | /** 35 | * 这些初始化操作在主线程会拖延App启动速度 36 | * 建议在子线程进行 37 | */ 38 | private void initApp(Application app) { 39 | initALog(app); 40 | initEasyHttp(app); 41 | ToastUtils.init(app, new ToastStyle()); 42 | //注册生命周期回调 43 | app.registerActivityLifecycleCallbacks(new ActivityLifeCycleCallBackIml()); 44 | } 45 | 46 | // init it in ur application 47 | public void initALog(Application app) { 48 | ALog.init(app) 49 | .setLogSwitch(BuildConfig.DEBUG)// 设置log总开关,包括输出到控制台和文件,默认开 50 | .setConsoleSwitch(BuildConfig.DEBUG)// 设置是否输出到控制台开关,默认开 51 | .setBorderSwitch(false);// 输出日志是否带边框开关,默认开 52 | } 53 | 54 | private void initEasyHttp(Application app) { 55 | EasyHttp.init(app); 56 | EasyHttp.getInstance() 57 | .debug("NetWork--INFO", BuildConfig.DEBUG) 58 | .setReadTimeOut(20 * 1000) 59 | .setWriteTimeOut(20 * 1000) 60 | .setConnectTimeout(20 * 1000) 61 | .setRetryCount(1)//默认网络不好自动重试3次 62 | .setBaseUrl(Configure.BASE_URL) 63 | .setCacheDiskConverter(new SerializableDiskConverter())//默认缓存使用序列化转化 64 | .setCacheMaxSize(50 * 1024 * 1024)//设置缓存大小为50M 65 | .setCacheVersion(1)//缓存版本为1 66 | .addInterceptor(new TokenInterceptor()) 67 | .setCookieStore(new CookieManger(app)) 68 | .setCertificates();//信任所有证书 69 | } 70 | 71 | @Nullable 72 | @Override 73 | public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { 74 | return null; 75 | } 76 | 77 | @Nullable 78 | @Override 79 | public String getType(@NonNull Uri uri) { 80 | return null; 81 | } 82 | 83 | @Nullable 84 | @Override 85 | public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { 86 | return null; 87 | } 88 | 89 | @Override 90 | public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { 91 | return 0; 92 | } 93 | 94 | @Override 95 | public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { 96 | return 0; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/base/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.base; 2 | 3 | import android.content.res.Configuration; 4 | import android.os.Bundle; 5 | import android.support.annotation.NonNull; 6 | import android.support.annotation.Nullable; 7 | import android.support.v4.app.FragmentActivity; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | 12 | import com.gyf.barlibrary.SimpleImmersionOwner; 13 | import com.gyf.barlibrary.SimpleImmersionProxy; 14 | import com.hjq.toast.ToastUtils; 15 | 16 | import butterknife.ButterKnife; 17 | import butterknife.Unbinder; 18 | import me.soushin.tinmvp.Utils.DialogUtils; 19 | import me.soushin.tinmvp.widget.ProgressDialog; 20 | import me.yokeyword.fragmentation.SupportFragment; 21 | 22 | /** 23 | * fragment基类 24 | * 25 | * @auther SouShin 26 | * @time 2018/12/26 17:27 27 | */ 28 | public abstract class BaseFragment> extends SupportFragment implements SimpleImmersionOwner { 29 | private P presenter; 30 | private Unbinder unbinder; 31 | //ImmersionBar代理类 32 | private SimpleImmersionProxy mSimpleImmersionProxy = new SimpleImmersionProxy(this); 33 | 34 | @Nullable 35 | @Override 36 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 37 | return inflater.inflate(getLayoutId(), container, false); 38 | } 39 | 40 | @Override 41 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 42 | super.onViewCreated(view, savedInstanceState); 43 | unbinder = ButterKnife.bind(this, view); 44 | presenter = createPresenter(); 45 | bindView(); 46 | } 47 | 48 | @Override 49 | public void setUserVisibleHint(boolean isVisibleToUser) { 50 | super.setUserVisibleHint(isVisibleToUser); 51 | mSimpleImmersionProxy.setUserVisibleHint(isVisibleToUser); 52 | } 53 | 54 | @Override 55 | public void onActivityCreated(@Nullable Bundle savedInstanceState) { 56 | super.onActivityCreated(savedInstanceState); 57 | mSimpleImmersionProxy.onActivityCreated(savedInstanceState); 58 | } 59 | 60 | @Override 61 | public void onHiddenChanged(boolean hidden) { 62 | super.onHiddenChanged(hidden); 63 | mSimpleImmersionProxy.onHiddenChanged(hidden); 64 | } 65 | 66 | @Override 67 | public void onConfigurationChanged(Configuration newConfig) { 68 | super.onConfigurationChanged(newConfig); 69 | mSimpleImmersionProxy.onConfigurationChanged(newConfig); 70 | } 71 | 72 | protected abstract P createPresenter(); 73 | 74 | protected abstract int getLayoutId(); 75 | 76 | //这个方法是为了让用户不要忘了绑定BaseView 77 | protected abstract void bindView(); 78 | 79 | @Override 80 | public void initImmersionBar() { 81 | 82 | } 83 | 84 | @Override 85 | public boolean immersionBarEnabled() { 86 | // 当为true的时候才可以执行initImmersionBar方法 87 | return false; 88 | } 89 | 90 | protected P getPresenter() { 91 | return presenter; 92 | } 93 | 94 | public FragmentActivity getFragActivity() { 95 | return _mActivity; 96 | } 97 | 98 | public void showToasty(String msg) { 99 | ToastUtils.show(msg); 100 | } 101 | 102 | public ProgressDialog getLoading(String msg){ 103 | return DialogUtils.getLoading(msg); 104 | } 105 | 106 | @Override 107 | public void onDestroyView() { 108 | unbinder.unbind(); 109 | if (presenter != null) { 110 | presenter.unsubscribe(); 111 | } 112 | super.onDestroyView(); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/network/HttpClient.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.network; 2 | 3 | import com.blankj.ALog; 4 | import com.zhouyou.http.EasyHttp; 5 | import com.zhouyou.http.body.ProgressResponseCallBack; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Iterator; 9 | import java.util.List; 10 | import java.util.Stack; 11 | 12 | import io.reactivex.disposables.Disposable; 13 | import me.soushin.tinmvp.Utils.ActivityUtils; 14 | import me.soushin.tinmvp.Utils.AppUtils; 15 | import me.soushin.tinmvp.model.SubcribeModel; 16 | 17 | /** 18 | * 接口访问 19 | * 20 | * @author SouShin 21 | * @time 2018/11/16 17:31 22 | */ 23 | public class HttpClient { 24 | public static List disposList = new ArrayList<>(); 25 | /** 26 | * file上传结果回调 27 | */ 28 | private static ProgressResponseCallBack fileProgressCallBack = new ProgressResponseCallBack() { 29 | @Override 30 | public void onResponseProgress(long bytesWritten, long contentLength, boolean done) { 31 | ALog.e("file上传回调", bytesWritten, contentLength, done); 32 | } 33 | }; 34 | 35 | /** 36 | * 使用示例1 带有加载框的请求 37 | * ResultModel:ResultModel是与后台约定的数据返回类型 Object是需要在回调中返回的对象 38 | * ProgressSubscriber要求传入一个dialog对象 39 | * 注意这个dialog必须实现IProgressDialog接口 40 | * 错误回调 super.onError(e);必须写 因为里面加了订阅关系的处理 41 | * 42 | * @param phone 手机号 43 | * @param path 文件路径 44 | * @param progressCallBack 带有加载框的回调 45 | 46 | public static void getPhoneCode(String phone,String path, ProgressSubscriber progressCallBack) { 47 | EasyHttp.post("get_code") 48 | .params("phone", phone) 49 | .params("file",new File(path),fileProgressCallBack) 50 | .execute(new CallClazzProxy, Object>(Object.class) { 51 | }).subscribe(progressCallBack); 52 | }*/ 53 | 54 | /** 55 | * 使用示例2 普通网络请求 56 | * ResultModel:ResultModel是与后台约定的数据返回类型 Object是需要在回调中返回的对象 57 | * 返回Disposable对象,方便取消网络请求 记得调用addDisposable(disposable) 把订阅添加到集合中 58 | * ActivityLifeCycleCallBackIml中已经做了解除订阅的处理 59 | * 60 | * @param phone 手机号 61 | * @param path 文件路径 62 | * @param simpleCallBack 没有加载框的回调 63 | 64 | public static void getPhoneCode(String phone, String path, SimpleCallBack simpleCallBack) { 65 | Disposable disposable=EasyHttp.post("get_code") 66 | .params("phone", phone) 67 | .params("file",new File(path),fileProgressCallBack) 68 | .execute(new CallBackProxy, Object>(simpleCallBack) { 69 | }); 70 | addDisposable(disposable); 71 | }*/ 72 | 73 | /** 74 | * 使用示例3 请求返回Observable 75 | * ResultModel:ResultModel是与后台约定的数据返回类型 Object是需要在回调中返回的对象 76 | * 返回Observable对象,方便使用Rxjava2进行自己的逻辑操作 77 | * 78 | * @param phone 手机号 79 | * @param path 文件路径 80 | public static Observable getPhoneCode(String phone, String path) { 81 | return EasyHttp.post("get_code") 82 | .params("phone", phone) 83 | .params("file",new File(path),fileProgressCallBack) 84 | .execute(new CallClazzProxy, Object>(Object.class) { 85 | }); 86 | }*/ 87 | 88 | /** 89 | * 添加订阅关系 90 | * 91 | * @param disposable 92 | */ 93 | public static void addDisposable(Disposable disposable) { 94 | disposList.add(new SubcribeModel(ActivityUtils.currentActivity().getLocalClassName() + System.currentTimeMillis(), disposable)); 95 | ALog.e("添加订阅", disposList.size()); 96 | } 97 | 98 | /** 99 | * 在onDestroy里执行 100 | * 取消所有订阅的网络请求 101 | */ 102 | /** 103 | * 在onDestroy里执行 104 | * 取消所有订阅的网络请求 105 | */ 106 | public static synchronized void disposeRequest(String className) { 107 | Iterator it = disposList.iterator(); 108 | while (it.hasNext()) { 109 | SubcribeModel subcribeModel = it.next(); 110 | String key = subcribeModel.getClassName(); 111 | if (!AppUtils.isEmpty(key) && key.startsWith(className)) { 112 | EasyHttp.cancelSubscription(subcribeModel.getmDisposable()); 113 | subcribeModel=null; 114 | it.remove(); 115 | } 116 | } 117 | ALog.e("取消订阅", disposList.size()); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/network/TokenInterceptor.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.network; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.blankj.ALog; 6 | import com.zhouyou.http.interceptor.BaseExpiredInterceptor; 7 | import com.zhouyou.http.utils.HttpLog; 8 | 9 | import java.io.IOException; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.TreeMap; 13 | 14 | import me.soushin.tinmvp.Utils.ActivityUtils; 15 | import okhttp3.FormBody; 16 | import okhttp3.HttpUrl; 17 | import okhttp3.Request; 18 | import okhttp3.Response; 19 | import me.soushin.tinmvp.Utils.GsonUtils; 20 | import me.soushin.tinmvp.model.ResultModel; 21 | 22 | /** 23 | * 处理token过期等问题 24 | * 25 | * @author SouShin 26 | * @time 2018/12/10 13:32 27 | */ 28 | public class TokenInterceptor extends BaseExpiredInterceptor { 29 | private ResultModel resultModel; 30 | 31 | @Override 32 | public boolean isResponseExpired(Response response, String bodyString) { 33 | resultModel = GsonUtils.toBean(bodyString, ResultModel.class); 34 | /*if (resultModel != null) { 35 | ALog.e("token拦截isResponseExpired", bodyString); 36 | return resultModel.getData() != null; 37 | }*/ 38 | return false; 39 | } 40 | 41 | /** 42 | * 只有上面配置的过期情况才会执行这里(return true;) 43 | * token 过期处理方案 44 | * 1 跳转登录页 45 | * 2 自动请求登录接口刷新token 并继续该接口的业务 46 | * @param chain 47 | * @param bodyString 48 | * @return 49 | */ 50 | @Override 51 | public Response responseExpired(Chain chain, String bodyString) { 52 | ALog.e("token拦截responseExpired", bodyString); 53 | HttpClient.disposeRequest(ActivityUtils.currentActivity().getLocalClassName()); 54 | // 第一种处理方式:直接跳转登录页 55 | // App.goLogin(); 56 | if (resultModel != null) { 57 | try { 58 | return chain.proceed(chain.request()); 59 | } catch (IOException e) { 60 | e.printStackTrace(); 61 | } 62 | } 63 | // 第二种处理方式: 自动请求登录接口刷新token 并继续该接口的业务 64 | /*try { 65 | refreshToken(); 66 | if (resultModel != null) { 67 | return processAccessTokenError(chain, chain.request()); 68 | } 69 | }catch (Exception e){ 70 | 71 | }*/ 72 | return null; 73 | } 74 | 75 | 76 | /** 77 | * 执行登录请求刷新本地token 78 | * 请求成功后一定要把token保存下来 79 | * @throws IOException 80 | */ 81 | public void refreshToken() throws IOException { 82 | 83 | } 84 | 85 | /** 86 | * 重新拼装并执行Request 87 | */ 88 | private Response processAccessTokenError(Chain chain, Request request) throws IOException { 89 | // create a new request and modify it accordingly using the new token 90 | String method = request.method(); 91 | FormBody oldBody = (FormBody) request.body(); 92 | if (oldBody == null) { 93 | if (method.equalsIgnoreCase("GET")) { 94 | oldBody = getRequestParams(request.url().query()); 95 | } else { 96 | return chain.proceed(request); 97 | } 98 | } 99 | FormBody.Builder newBody = new FormBody.Builder(); 100 | for (int i = 0; i < oldBody.size(); i++) { 101 | String name = oldBody.encodedName(i); 102 | String value = oldBody.encodedValue(i); 103 | newBody.add(name, value); 104 | } 105 | 106 | Request newRequest; 107 | if (method.equalsIgnoreCase("GET")) { 108 | String url = packageParams(newBody.build()); 109 | HttpLog.i("uuok.GET.Error.newUrl:" + url); 110 | HttpUrl newHrrpIrl = request.url().newBuilder().query(url).build(); 111 | newRequest = request.newBuilder().url(newHrrpIrl).get().build(); 112 | } else { 113 | newRequest = request.newBuilder().post(newBody.build()).build(); 114 | } 115 | return chain.proceed(newRequest); 116 | } 117 | 118 | /** 119 | * 将GET请求的参数封装成FormBody 120 | */ 121 | private FormBody getRequestParams(String params) { 122 | if (params == null) 123 | return null; 124 | String[] strArr = params.split("&"); 125 | if (strArr == null) { 126 | return null; 127 | } 128 | TreeMap map = new TreeMap<>(); 129 | FormBody.Builder fBulder = new FormBody.Builder(); 130 | for (String s : strArr) { 131 | String[] sArr = s.split("="); 132 | if (sArr.length < 2) 133 | continue; 134 | map.put(sArr[0], sArr[1]); 135 | fBulder.add(sArr[0], sArr[1]); 136 | } 137 | FormBody formBody = fBulder.build(); 138 | return formBody; 139 | } 140 | 141 | /** 142 | * 封装参数 143 | */ 144 | private String packageParams(FormBody oldBody) { 145 | List namesAndValues = new ArrayList<>(); 146 | for (int i = 0; i < oldBody.size(); i++) { 147 | String name = oldBody.encodedName(i); 148 | String value = oldBody.encodedValue(i); 149 | if (!TextUtils.isEmpty(name)) { 150 | namesAndValues.add(name); 151 | namesAndValues.add(value); 152 | } 153 | } 154 | StringBuilder sb = new StringBuilder(); 155 | namesAndValuesToQueryString(sb, namesAndValues); 156 | return sb.toString(); 157 | } 158 | 159 | /** 160 | * 合并GET参数 161 | */ 162 | private void namesAndValuesToQueryString(StringBuilder out, List namesAndValues) { 163 | for (int i = 0, size = namesAndValues.size(); i < size; i += 2) { 164 | String name = namesAndValues.get(i); 165 | String value = namesAndValues.get(i + 1); 166 | if (i > 0) out.append('&'); 167 | out.append(name); 168 | if (value != null) { 169 | out.append('='); 170 | out.append(value); 171 | } 172 | } 173 | } 174 | 175 | 176 | } 177 | -------------------------------------------------------------------------------- /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/java/me/soushin/tinmvp/Utils/AppUtils.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.Utils; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.view.MotionEvent; 7 | import android.view.View; 8 | import android.view.inputmethod.InputMethodManager; 9 | import android.widget.EditText; 10 | import android.widget.TextView; 11 | 12 | import com.hjq.toast.ToastUtils; 13 | 14 | import java.security.MessageDigest; 15 | import java.security.NoSuchAlgorithmException; 16 | import java.util.List; 17 | 18 | import me.soushin.tinmvp.App; 19 | 20 | /** 21 | * 工具类 22 | * 23 | * @author SouShin 24 | * @time 2018/11/16 13:54 25 | */ 26 | public class AppUtils { 27 | /** 28 | * 退出登录 29 | * 30 | * @param context 31 | * @return 32 | */ 33 | private static long mExitTime; 34 | public static boolean doubleClickExit() { 35 | if ((System.currentTimeMillis() - mExitTime) > 2000) { 36 | mExitTime = System.currentTimeMillis(); 37 | ToastUtils.show("再按一次退出"); 38 | return false; 39 | } 40 | return true; 41 | } 42 | 43 | /** 44 | * 避免快速重复点击 45 | * @return 46 | */ 47 | private static long lastClickTime = 0;//上次点击的时间 48 | private static int spaceTime = 1000;//时间间隔 49 | public static boolean isFastClick() { 50 | long currentTime = System.currentTimeMillis();//当前系统时间 51 | boolean isAllowClick;//是否允许点击 52 | if (currentTime - lastClickTime > spaceTime) { 53 | isAllowClick = true; 54 | } else { 55 | isAllowClick = false; 56 | } 57 | lastClickTime = currentTime; 58 | return isAllowClick; 59 | } 60 | 61 | //--------------------------MD5加密------------------------------- 62 | /** 63 | * 生成MD5加密32位字符串 64 | * 65 | * @param MStr :需要加密的字符串 66 | * @return 67 | */ 68 | public static String Md5(String MStr) { 69 | try { 70 | final MessageDigest mDigest = MessageDigest.getInstance("MD5"); 71 | mDigest.update(MStr.getBytes()); 72 | return bytesToHexString(mDigest.digest()); 73 | } catch (NoSuchAlgorithmException e) { 74 | return String.valueOf(MStr.hashCode()); 75 | } 76 | } 77 | 78 | /** 79 | * MD5内部算法-------不能修改! 80 | * @param bytes 81 | * @return 82 | */ 83 | private static String bytesToHexString(byte[] bytes) { 84 | // http://stackoverflow.com/questions/332079 85 | StringBuilder sb = new StringBuilder(); 86 | for (int i = 0; i < bytes.length; i++) { 87 | String hex = Integer.toHexString(0xFF & bytes[i]); 88 | if (hex.length() == 1) { 89 | sb.append('0'); 90 | } 91 | sb.append(hex); 92 | } 93 | return sb.toString(); 94 | } 95 | //--------------------------MD5加密------------------------------- 96 | 97 | /** 98 | * 倒计时 99 | * 100 | * @param textView 控件 101 | * @param waitTime 倒计时总时长 102 | * @param interval 倒计时的间隔时间 103 | */ 104 | public static void countDown(final TextView textView, long waitTime, long interval) { 105 | textView.setEnabled(false); 106 | android.os.CountDownTimer timer = new android.os.CountDownTimer(waitTime, interval) { 107 | @SuppressLint("DefaultLocale") 108 | @Override 109 | public void onTick(long millisUntilFinished) { 110 | textView.setText(millisUntilFinished / 1000+"s"); 111 | } 112 | 113 | @Override 114 | public void onFinish() { 115 | textView.setEnabled(true); 116 | textView.setText("重新获取"); 117 | } 118 | }; 119 | timer.start(); 120 | } 121 | 122 | //--------------------------处理EditText显示隐藏输入法------------------------------- 123 | 124 | /** 125 | * 处理EditText显示隐藏输入法 126 | * 点击EditText显示输入法 点击其他View隐藏输入法 127 | * @param activity 128 | * @param event 129 | */ 130 | public static void dispatchEditText(Activity activity, MotionEvent event) { 131 | View v = activity.getCurrentFocus(); 132 | if (event.getAction() == MotionEvent.ACTION_DOWN && isShouldHideKeyboard(v, event)) { 133 | hideSoftInput(v); 134 | } 135 | } 136 | 137 | /** 138 | * 是否隐藏软键盘 139 | * 140 | * @param v 141 | * @param event 142 | * @return 143 | */ 144 | public static boolean isShouldHideKeyboard(View v, MotionEvent event) { 145 | if (v instanceof EditText) { 146 | int[] l = {0, 0}; 147 | v.getLocationInWindow(l); 148 | int left = l[0], top = l[1], bottom = top + v.getHeight(), right = left + v.getWidth(); 149 | return !(event.getX() > left && event.getX() < right 150 | && event.getY() > top && event.getY() < bottom); 151 | } 152 | return false; 153 | } 154 | 155 | /** 156 | * 隐藏输入法 157 | * 158 | * @param v 159 | */ 160 | public static void hideSoftInput(View v) { 161 | InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 162 | if (imm.isActive()) { 163 | imm.hideSoftInputFromWindow(v.getApplicationWindowToken(), 0); 164 | } 165 | } 166 | 167 | //--------------------------处理EditText显示隐藏输入法------------------------------- 168 | 169 | /** 170 | * 字符串判空 171 | * 172 | * @param str 173 | * @return 174 | */ 175 | public static boolean isEmpty(String str) { 176 | return str == null || str.trim().length() == 0; 177 | } 178 | 179 | /** 180 | * 数组判空 181 | * 182 | * @param list 183 | * @return 184 | */ 185 | public static boolean isEmpty(List list) { 186 | return list == null || list.size() == 0; 187 | } 188 | 189 | /** 190 | * 对象判空 191 | * 192 | * @param ob 193 | * @return 194 | */ 195 | public static boolean isEmpty(Object ob) { 196 | return ob == null || isEmpty(ob.toString()); 197 | } 198 | 199 | public static String getError(int code, String msg) { 200 | return msg + ":" + code; 201 | } 202 | 203 | public static int getColor(int color) { 204 | return App.getApp().getResources().getColor(color); 205 | } 206 | 207 | public static String getString(int res) { 208 | return App.getApp().getString(res); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/Utils/SharedUtils.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.Utils; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.support.annotation.NonNull; 6 | 7 | import com.google.gson.JsonArray; 8 | import com.google.gson.JsonElement; 9 | import com.google.gson.JsonObject; 10 | import com.google.gson.JsonParser; 11 | 12 | import java.util.ArrayList; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | import java.util.Set; 17 | 18 | /** 19 | * Created by SouShin on 2018/8/270916. 20 | * 可以存取Boolean/Long/Integer/Float/Object/List/HashMap 21 | */ 22 | public class SharedUtils { 23 | private static String filename = "cookie"; 24 | private static SharedPreferences.Editor editor; 25 | private static SharedPreferences preferences; 26 | 27 | /** 28 | * 需要初始化 29 | * @param context 30 | */ 31 | public static void initShared(Context context){ 32 | preferences = context.getSharedPreferences(filename, context.MODE_PRIVATE); 33 | editor = preferences.edit(); 34 | } 35 | 36 | /** 37 | * 保存数据到SharedPreferences 38 | * 39 | * @param key 键 40 | * @param value 需要保存的数据 41 | * @return 保存结果 42 | */ 43 | public static boolean putData(String key, Object value) { 44 | String type = value.getClass().getSimpleName(); 45 | try { 46 | switch (type) { 47 | case "Boolean": 48 | editor.putBoolean(key, (Boolean) value); 49 | break; 50 | case "Long": 51 | editor.putLong(key, (Long) value); 52 | break; 53 | case "Float": 54 | editor.putFloat(key, (Float) value); 55 | break; 56 | case "String": 57 | editor.putString(key, (String) value); 58 | break; 59 | case "Integer": 60 | editor.putInt(key, (Integer) value); 61 | break; 62 | default: 63 | String json = GsonUtils.toString(value); 64 | editor.putString(key, json); 65 | break; 66 | } 67 | } catch (Exception e) { 68 | return false; 69 | } 70 | return editor.commit(); 71 | } 72 | 73 | /** 74 | * 获取SharedPreferences中保存的数据 75 | * 76 | * @param key 键 77 | * @param defaultValue 获取失败默认值 78 | * @return 从SharedPreferences读取的数据 79 | */ 80 | public static Object getData(String key, @NonNull Object defaultValue) { 81 | Object result; 82 | String type = defaultValue.getClass().getSimpleName(); 83 | try { 84 | switch (type) { 85 | case "Boolean": 86 | result = preferences.getBoolean(key, (Boolean) defaultValue); 87 | break; 88 | case "Long": 89 | result = preferences.getLong(key, (Long) defaultValue); 90 | break; 91 | case "Float": 92 | result = preferences.getFloat(key, (Float) defaultValue); 93 | break; 94 | case "String": 95 | result = preferences.getString(key, (String) defaultValue); 96 | break; 97 | case "Integer": 98 | result = preferences.getInt(key, (Integer) defaultValue); 99 | break; 100 | default: 101 | String json = preferences.getString(key, ""); 102 | if (!json.equals("") && json.length() > 0) { 103 | result = GsonUtils.getGson().fromJson(json, defaultValue.getClass()); 104 | } else { 105 | result = defaultValue; 106 | } 107 | break; 108 | } 109 | } catch (Exception e) { 110 | result = defaultValue; 111 | } 112 | return result; 113 | } 114 | 115 | /** 116 | * 获取字符串数据 117 | * @param key 118 | * @return 119 | */ 120 | public static String getStringData(String key){ 121 | return (String) getData(key,""); 122 | } 123 | 124 | /** 125 | * 获取int型数据 126 | * @param key 127 | * @return 128 | */ 129 | public static int getIntData(String key){ 130 | return (int) getData(key,0); 131 | } 132 | 133 | /** 134 | * 获取布尔型数据 135 | * @param key 136 | * @return 137 | */ 138 | public static boolean getBooleanData(String key){ 139 | return (boolean) getData(key,false); 140 | } 141 | 142 | /** 143 | * 用于保存集合 144 | * 145 | * @param key key 146 | * @param list 集合数据 147 | * @return 保存结果 148 | */ 149 | public static boolean putListData(String key, List list) { 150 | String type = list.get(0).getClass().getSimpleName(); 151 | JsonArray array = new JsonArray(); 152 | try { 153 | switch (type) { 154 | case "Boolean": 155 | for (int i = 0; i < list.size(); i++) { 156 | array.add((Boolean) list.get(i)); 157 | } 158 | break; 159 | case "Long": 160 | for (int i = 0; i < list.size(); i++) { 161 | array.add((Long) list.get(i)); 162 | } 163 | break; 164 | case "Float": 165 | for (int i = 0; i < list.size(); i++) { 166 | array.add((Float) list.get(i)); 167 | } 168 | break; 169 | case "String": 170 | for (int i = 0; i < list.size(); i++) { 171 | array.add((String) list.get(i)); 172 | } 173 | break; 174 | case "Integer": 175 | for (int i = 0; i < list.size(); i++) { 176 | array.add((Integer) list.get(i)); 177 | } 178 | break; 179 | default: 180 | for (int i = 0; i < list.size(); i++) { 181 | JsonElement obj = GsonUtils.getGson().toJsonTree(list.get(i)); 182 | array.add(obj); 183 | } 184 | break; 185 | } 186 | editor.putString(key, array.toString()); 187 | } catch (Exception e) { 188 | e.printStackTrace(); 189 | return false; 190 | } 191 | return editor.commit(); 192 | } 193 | 194 | /** 195 | * 获取保存的List 196 | * 197 | * @param key key 198 | * @return 对应的Lis集合 199 | */ 200 | public static List getListData(String key, Class cls) { 201 | List list = new ArrayList<>(); 202 | String json = preferences.getString(key, ""); 203 | if (!json.equals("") && json.length() > 0) { 204 | JsonArray array = new JsonParser().parse(json).getAsJsonArray(); 205 | for (JsonElement elem : array) { 206 | list.add(GsonUtils.getGson().fromJson(elem, cls)); 207 | } 208 | } 209 | return list; 210 | } 211 | 212 | /** 213 | * 用于保存集合 214 | * 215 | * @param key key 216 | * @param map map数据 217 | * @return 保存结果 218 | */ 219 | public static boolean putHashMapData(String key, Map map) { 220 | boolean result; 221 | try { 222 | String json = GsonUtils.toString(map); 223 | editor.putString(key, json); 224 | result = true; 225 | } catch (Exception e) { 226 | result = false; 227 | e.printStackTrace(); 228 | } 229 | editor.apply(); 230 | return result; 231 | } 232 | 233 | /** 234 | * 用于保存集合 235 | * 236 | * @param key key 237 | * @return HashMap 238 | */ 239 | public static HashMap getHashMapData(String key, Class clsV) { 240 | String json = preferences.getString(key, ""); 241 | HashMap map = new HashMap<>(); 242 | 243 | JsonObject obj = new JsonParser().parse(json).getAsJsonObject(); 244 | Set> entrySet = obj.entrySet(); 245 | for (Map.Entry entry : entrySet) { 246 | String entryKey = entry.getKey(); 247 | JsonObject value = (JsonObject) entry.getValue(); 248 | map.put(entryKey, GsonUtils.getGson().fromJson(value, clsV)); 249 | } 250 | return map; 251 | } 252 | 253 | /** 254 | * 清除缓存的数据 255 | * @return 256 | */ 257 | public static Boolean clearCache() { 258 | try { 259 | editor.clear(); 260 | return editor.commit(); 261 | }catch (Exception e){ 262 | e.printStackTrace(); 263 | return false; 264 | } 265 | } 266 | 267 | } 268 | -------------------------------------------------------------------------------- /app/src/main/java/me/soushin/tinmvp/Utils/ActivityUtils.java: -------------------------------------------------------------------------------- 1 | package me.soushin.tinmvp.Utils; 2 | 3 | import android.app.Activity; 4 | import android.app.ActivityManager; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.pm.PackageManager; 8 | import android.content.pm.ResolveInfo; 9 | import android.os.Bundle; 10 | 11 | import java.util.List; 12 | import java.util.Stack; 13 | 14 | import me.soushin.tinmvp.base.BaseFragment; 15 | 16 | /** 17 | * 封装Activity相关工具类 18 | * 19 | * @author SouShin 20 | * @time 2018/10/29 14:45 21 | */ 22 | public class ActivityUtils { 23 | 24 | private static Stack activityStack; 25 | 26 | /** 27 | * 添加Activity 到栈 28 | * 29 | * @param activity 30 | */ 31 | public static void addActivity(Activity activity) { 32 | if (activityStack == null) { 33 | activityStack = new Stack<>(); 34 | } 35 | activityStack.add(activity); 36 | } 37 | 38 | /** 39 | * 获取当前的Activity(堆栈中最后一个压入的) 40 | */ 41 | public static Activity currentActivity() { 42 | Activity activity = activityStack.lastElement(); 43 | return activity; 44 | } 45 | 46 | /** 47 | * 结束当前Activity(堆栈中最后一个压入的) 48 | */ 49 | public static void finishActivity() { 50 | Activity activity = activityStack.lastElement(); 51 | if (activity != null) { 52 | activityStack.remove(activity); 53 | activity.finish(); 54 | } 55 | } 56 | 57 | /** 58 | * 移除指定的Activity 59 | * 60 | * @param activity 61 | */ 62 | public static void removeActivity(Activity activity) { 63 | if (activity != null) { 64 | activityStack.remove(activity); 65 | } 66 | } 67 | 68 | /** 69 | * 结束指定的Activity 70 | * 71 | * @param activity 72 | */ 73 | public static void finishActivity(Activity activity) { 74 | if (activity != null) { 75 | activityStack.remove(activity); 76 | activity.finish(); 77 | activity = null; 78 | } 79 | } 80 | 81 | /** 82 | * 结束指定类名的Activity 83 | */ 84 | public static void finishActivity(Class cls) { 85 | for (int i = 0; i < activityStack.size(); i++) { 86 | if (activityStack.get(i).getClass().equals(cls)) { 87 | finishActivity(activityStack.get(i)); 88 | } 89 | } 90 | } 91 | 92 | /** 93 | * 结束所有的Activity 94 | */ 95 | public static void finishAllActivity() { 96 | int size = activityStack.size(); 97 | for (int i = 0; i < size; i++) { 98 | if (null != activityStack.get(i)) { 99 | activityStack.get(i).finish(); 100 | } 101 | } 102 | activityStack.clear(); 103 | } 104 | 105 | /** 106 | * 退出app 107 | * 108 | * @param context 109 | */ 110 | public static void AppExit(Context context) { 111 | try { 112 | finishAllActivity(); 113 | ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 114 | activityManager.restartPackage(context.getPackageName()); 115 | System.exit(0); 116 | } catch (Exception e) { 117 | } 118 | } 119 | 120 | public static Stack getActivityStack() { 121 | return activityStack; 122 | } 123 | 124 | /** 125 | * 判断是否存在指定Activity 126 | * 127 | * @param context 上下文 128 | * @param packageName 包名 129 | * @param className activity全路径类名 130 | * @return {@code true}: 是
{@code false}: 否 131 | */ 132 | public static boolean isExistActivity(Context context, String packageName, String className) { 133 | Intent intent = new Intent(); 134 | intent.setClassName(packageName, className); 135 | return !(context.getPackageManager().resolveActivity(intent, 0) == null || 136 | intent.resolveActivity(context.getPackageManager()) == null || 137 | context.getPackageManager().queryIntentActivities(intent, 0).size() == 0); 138 | } 139 | 140 | /** 141 | * 要求最低API为11 142 | * Activity 跳转 143 | * 跳转后Finish之前所有的Activity 144 | * 145 | * @param activity 146 | * @param goal 147 | */ 148 | public static void goAndFinishAll(Activity activity, Class goal, Bundle bundle) { 149 | Intent intent = new Intent(activity, goal); 150 | intent.putExtras(bundle); 151 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 152 | activity.startActivity(intent); 153 | activity.finish(); 154 | } 155 | 156 | /** 157 | * 要求最低API为11 158 | * Activity 跳转 159 | * 跳转后Finish之前所有的Activity 160 | * 161 | * @param activity 162 | * @param goal 163 | */ 164 | public static void goAndFinishAll(Activity activity, Class goal) { 165 | Intent intent = new Intent(activity, goal); 166 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 167 | activity.startActivity(intent); 168 | activity.finish(); 169 | } 170 | 171 | /** 172 | * Activity 跳转 173 | * 174 | * @param activity 175 | * @param goal 176 | */ 177 | public static void goAndFinish(Activity activity, Class goal, Bundle bundle) { 178 | Intent intent = new Intent(activity, goal); 179 | intent.putExtras(bundle); 180 | activity.startActivity(intent); 181 | activity.finish(); 182 | } 183 | 184 | /** 185 | * Activity 跳转 186 | * 187 | * @param activity 188 | * @param goal 189 | */ 190 | public static void goAndFinish(Activity activity, Class goal) { 191 | activity.startActivity(new Intent(activity, goal)); 192 | activity.finish(); 193 | } 194 | 195 | /** 196 | * Activity 跳转 197 | * 198 | * @param activity 199 | * @param goal 200 | */ 201 | public static void goTo(Activity activity, Class goal) { 202 | Intent intent = new Intent(activity, goal); 203 | activity.startActivity(intent); 204 | } 205 | 206 | /** 207 | * Activity 跳转 208 | * 209 | * @param activity 210 | * @param goal 211 | */ 212 | public static void goTo(Activity activity, Class goal,Intent intent) { 213 | intent.setClass(activity,goal); 214 | activity.startActivity(intent); 215 | } 216 | 217 | public static void goForResult(Activity activity, Class goal, int requestCode) { 218 | Intent intent = new Intent(activity, goal); 219 | activity.startActivityForResult(intent, requestCode); 220 | } 221 | 222 | public static void goForResult(BaseFragment fragment, Class goal, int requestCode) { 223 | Intent intent = new Intent(fragment.getFragActivity(), goal); 224 | fragment.startActivityForResult(intent, requestCode); 225 | } 226 | 227 | public static void goForResult(Activity activity, Class goal, Intent intent, int requestCode) { 228 | intent.setClass(activity,goal); 229 | activity.startActivityForResult(intent, requestCode); 230 | } 231 | 232 | public static void goForResult(BaseFragment fragment, Class goal, Intent intent, int requestCode) { 233 | intent.setClass(fragment.getFragActivity(),goal); 234 | fragment.startActivityForResult(intent, requestCode); 235 | } 236 | /** 237 | * 打开App 238 | * 239 | * @param activity activity 240 | * @param packageName 包名 241 | */ 242 | public static void launchApp(Activity activity, String packageName) { 243 | if (AppUtils.isEmpty(packageName)){ 244 | return; 245 | } 246 | activity.startActivity(activity.getPackageManager().getLaunchIntentForPackage(packageName)); 247 | } 248 | 249 | /** 250 | * 使用系统发送分享数据 251 | * 252 | * @param context 上下文 253 | * @param text 要分享的文本 254 | */ 255 | public static void share(Context context, String text) { 256 | Intent sendIntent = new Intent(); 257 | sendIntent.setAction(Intent.ACTION_SEND); 258 | sendIntent.putExtra(Intent.EXTRA_TEXT, text); 259 | sendIntent.setType("text/plain"); 260 | context.startActivity(Intent.createChooser(sendIntent, "分享到")); 261 | } 262 | 263 | /** 264 | * 获取launcher activity 265 | * 266 | * @param context 上下文 267 | * @param packageName 包名 268 | * @return launcher activity 269 | */ 270 | public static String getLauncherActivity(Context context, String packageName) { 271 | Intent intent = new Intent(Intent.ACTION_MAIN, null); 272 | intent.addCategory(Intent.CATEGORY_LAUNCHER); 273 | PackageManager pm = context.getPackageManager(); 274 | List infos = pm.queryIntentActivities(intent, 0); 275 | for (ResolveInfo info : infos) { 276 | if (info.activityInfo.packageName.equals(packageName)) { 277 | return info.activityInfo.name; 278 | } 279 | } 280 | return "no " + packageName; 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /config.gradle: -------------------------------------------------------------------------------- 1 | //这里配置了开发项目常用的第三方库以及SDK的版本号 可以按照项目需求进行选择或者版本升级 2 | //config.gradle配置方法: 3 | //在项目的根目录的build.gradle里添加 apply from: "config.gradle"支持 然后就可以直接使用了 4 | //具体使用方法看项目app里的build.gradle文件 5 | //打包混淆所需要的命令都已添加到proguard-rules.pro文件里 6 | 7 | ext { 8 | android = [ 9 | compileSdkVersion: 28, 10 | buildToolsVersion: "27.0.3", 11 | minSdkVersion : 16, 12 | targetSdkVersion : 27, 13 | versionCode : 1, 14 | versionName : "1.0" 15 | ] 16 | 17 | version = [ 18 | androidSupportSdkVersion: "27.1.1", 19 | retrofitSdkVersion : "2.4.0", 20 | glideSdkVersion : "4.8.0", 21 | butterknifeSdkVersion : "8.8.1", 22 | dagger2SdkVersion : "2.15", 23 | espressoSdkVersion : "3.0.2", 24 | alogSdkVersion : "1.9.0", 25 | ] 26 | 27 | 28 | dependencies = [ 29 | //support 30 | "appcompat-v7" : "com.android.support:appcompat-v7:${version["androidSupportSdkVersion"]}", 31 | "design" : "com.android.support:design:${version["androidSupportSdkVersion"]}", 32 | "support-v4" : "com.android.support:support-v4:${version["androidSupportSdkVersion"]}", 33 | "cardview-v7" : "com.android.support:cardview-v7:${version["androidSupportSdkVersion"]}", 34 | "annotations" : "com.android.support:support-annotations:${version["androidSupportSdkVersion"]}", 35 | "recyclerview-v7" : "com.android.support:recyclerview-v7:${version["androidSupportSdkVersion"]}", 36 | 37 | //network 38 | "retrofit" : "com.squareup.retrofit2:retrofit:${version["retrofitSdkVersion"]}", 39 | "retrofit-converter-gson" : "com.squareup.retrofit2:converter-gson:${version["retrofitSdkVersion"]}", 40 | "retrofit-adapter-rxjava" : "com.squareup.retrofit2:adapter-rxjava:${version["retrofitSdkVersion"]}", 41 | "retrofit-adapter-rxjava2" : "com.squareup.retrofit2:adapter-rxjava2:${version["retrofitSdkVersion"]}", 42 | "logging-interceptor" : "com.squareup.okhttp3:logging-interceptor:3.10.0", 43 | "okhttp3" : "com.squareup.okhttp3:okhttp:3.10.0", 44 | "okhttp-urlconnection" : "com.squareup.okhttp:okhttp-urlconnection:2.0.0", 45 | "glide" : "com.github.bumptech.glide:glide:${version["glideSdkVersion"]}", 46 | "glide-compiler" : "com.github.bumptech.glide:compiler:${version["glideSdkVersion"]}", 47 | "glide-loader-okhttp3" : "com.github.bumptech.glide:okhttp3-integration:${version["glideSdkVersion"]}", 48 | "picasso" : "com.squareup.picasso:picasso:2.71828", 49 | "fresco" : "com.facebook.fresco:fresco:1.9.0", 50 | "photodraweeview" : "me.relex:photodraweeview:1.1.3", 51 | "EasyFlipView" : "com.wajahatkarim3.EasyFlipView:EasyFlipView:2.1.1", 52 | 53 | //view 54 | "constraint-layout" : "com.android.support.constraint:constraint-layout:1.1.3", 55 | "autolayout" : "com.zhy:autolayout:1.4.5", 56 | "butterknife" : "com.jakewharton:butterknife:${version["butterknifeSdkVersion"]}", 57 | "butterknife-compiler" : "com.jakewharton:butterknife-compiler:${version["butterknifeSdkVersion"]}", 58 | "pickerview" : "com.contrarywind:Android-PickerView:4.1.6", 59 | "photoview" : "com.github.chrisbanes.photoview:library:1.2.3", 60 | "numberprogressbar" : "com.daimajia.numberprogressbar:library:1.2@aar", 61 | "nineoldandroids" : "com.nineoldandroids:library:2.4.0", 62 | "paginate" : "com.github.markomilos:paginate:0.5.1", 63 | "vlayout" : "com.alibaba.android:vlayout:1.1.0@aar", 64 | "smartrefresh" : "com.scwang.smartrefresh:SmartRefreshLayout:1.0.5.1", 65 | //没有使用特殊Header,可以不加这个 66 | "smartrefresh_header" : "com.scwang.smartrefresh:SmartRefreshHeader:1.0.5.1", 67 | "BaseRecyclerViewAdapterHelper": "com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.42", 68 | //状态栏解决方案 69 | "statusbarutil" : "com.jaeger.statusbarutil:library:1.5.1", 70 | //封装webview 71 | "agentweb" : "com.just.agentweb:agentweb:4.0.2", 72 | "download" : "com.just.agentweb:download:4.0.2", 73 | "filechooser" : "com.just.agentweb:filechooser:4.0.2", 74 | "bottom-bar" : "com.roughike:bottom-bar:2.3.1", 75 | "banner" : "com.youth.banner:banner:1.4.10", 76 | "marquee" : "com.gongwen:marqueelibrary:1.1.3", 77 | "pictureselector" : "com.github.LuckSiege.PictureSelector:picture_library:v2.2.3", 78 | "flycotablayout" : "com.flyco.tablayout:FlycoTabLayout_Lib:2.1.2@aar", 79 | "swipeRecyclerView" : "com.yanzhenjie:recyclerview-swipe:1.2.0", 80 | "superTextView" : "com.github.lygttpod:SuperTextView:2.1.8", 81 | "photoviewer" : "com.github.wanglu1209:PhotoViewer:0.43", 82 | "swipeMenu" : "com.daimajia.swipelayout:library:1.2.0@aar", 83 | "fragmentation" : "me.yokeyword:fragmentation:1.3.6", 84 | "ToastUtils" : "com.hjq:toast:5.2", 85 | "autosize" : "me.jessyan:autosize:1.1.2", 86 | "immersionbar" : "com.gyf.immersionbar:immersionbar:2.3.3-beta15", 87 | 88 | //rx2 89 | "rxandroid2" : "io.reactivex.rxjava2:rxandroid:2.0.2", 90 | "rxjava2" : "io.reactivex.rxjava2:rxjava:2.1.12", 91 | "rxlifecycle2" : "com.trello.rxlifecycle2:rxlifecycle:${version["rxlifecycle2SdkVersion"]}", 92 | "rxlifecycle2-android" : "com.trello.rxlifecycle2:rxlifecycle-android:${version["rxlifecycle2SdkVersion"]}", 93 | "rxlifecycle2-components" : "com.trello.rxlifecycle2:rxlifecycle-components:${version["rxlifecycle2SdkVersion"]}", 94 | "rxcache2" : "com.github.VictorAlbertos.RxCache:runtime:1.8.3-2.x", 95 | "rxeasyhttp" : "com.zhouyou:rxeasyhttp:2.1.2", 96 | "rxpermissions" : "com.github.tbruyelle:rxpermissions:0.10.2", 97 | 98 | //tools 99 | "dagger2" : "com.google.dagger:dagger:${version["dagger2SdkVersion"]}", 100 | "dagger2-android" : "com.google.dagger:dagger-android:${version["dagger2SdkVersion"]}", 101 | "dagger2-android-support" : "com.google.dagger:dagger-android-support:${version["dagger2SdkVersion"]}", 102 | "dagger2-compiler" : "com.google.dagger:dagger-compiler:${version["dagger2SdkVersion"]}", 103 | "dagger2-android-processor" : "com.google.dagger:dagger-android-processor:${version["dagger2SdkVersion"]}", 104 | "androideventbus" : "org.simple:androideventbus:1.0.5.1", 105 | "otto" : "com.squareup:otto:1.3.8", 106 | "gson" : "com.google.code.gson:gson:2.8.5", 107 | "javax.annotation" : "javax.annotation:jsr250-api:1.0", 108 | "arouter" : "com.alibaba:arouter-api:1.3.1", 109 | "arouter-compiler" : "com.alibaba:arouter-compiler:1.1.4", 110 | "progressmanager" : "me.jessyan:progressmanager:1.5.0", 111 | "retrofit-url-manager" : "me.jessyan:retrofit-url-manager:1.4.0", 112 | "lifecyclemodel" : "me.jessyan:lifecyclemodel:1.0.1", 113 | "luban" : "top.zibin:Luban:1.1.2", 114 | "eventbus" : "org.greenrobot:eventbus:3.1.1", 115 | "litepal" : "org.litepal.android:core:2.0.0", 116 | "andpermission" : "com.yanzhenjie:permission:2.0.0-rc12", 117 | "MPAndroidChart" : "com.github.PhilJay:MPAndroidChart:v3.0.3", 118 | "umeng-analytics" : "com.umeng.analytics:analytics:6.0.1", 119 | 120 | //test 121 | "junit" : "junit:junit:4.12", 122 | "androidJUnitRunner" : "android.support.test.runner.AndroidJUnitRunner", 123 | "runner" : "com.android.support.test:runner:1.0.2", 124 | "espresso-core" : "com.android.support.test.espresso:espresso-core:${version["espressoSdkVersion"]}", 125 | "espresso-contrib" : "com.android.support.test.espresso:espresso-contrib:${version["espressoSdkVersion"]}", 126 | "espresso-intents" : "com.android.support.test.espresso:espresso-intents:${version["espressoSdkVersion"]}", 127 | "mockito-core" : "org.mockito:mockito-core:1.+", 128 | "timber" : "com.jakewharton.timber:timber:4.7.0", 129 | "alog" : "com.blankj:alog:${version["alogSdkVersion"]}", 130 | "canary-debug" : "com.squareup.leakcanary:leakcanary-android:${version["canarySdkVersion"]}", 131 | "canary-release" : "com.squareup.leakcanary:leakcanary-android-no-op:${version["canarySdkVersion"]}", 132 | 133 | ] 134 | 135 | } --------------------------------------------------------------------------------