├── .gitattributes
├── .gitignore
├── .idea
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── gradle.xml
├── misc.xml
├── runConfigurations.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── example
│ │ └── lanyixin
│ │ └── myapplication
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ └── lanyixin
│ │ │ └── myapplication
│ │ │ ├── MainActivity.kt
│ │ │ ├── MyApplication.kt
│ │ │ ├── TxtActivity.java
│ │ │ ├── api
│ │ │ ├── ApiService.kt
│ │ │ ├── ResultBase.java
│ │ │ └── RetrofitManager.kt
│ │ │ ├── contract
│ │ │ └── DemoContract.kt
│ │ │ ├── format
│ │ │ ├── CFUtils.java
│ │ │ ├── CacheHelper.java
│ │ │ ├── CacheManager.java
│ │ │ ├── PriceUtils.java
│ │ │ └── RateBean.java
│ │ │ ├── model
│ │ │ └── DemoModel.kt
│ │ │ └── presenter
│ │ │ └── DemoPresenter.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ └── activity_txt.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── 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
│ │ ├── raw
│ │ ├── default_coin.json
│ │ ├── default_coin_en.json
│ │ ├── default_coin_tw.json
│ │ ├── hexie
│ │ ├── huilv.json
│ │ ├── huilv_count.json
│ │ ├── img.json
│ │ ├── plat.json
│ │ ├── price.json
│ │ ├── price_en.json
│ │ ├── price_hk.json
│ │ └── price_tw.json
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── example
│ └── lanyixin
│ └── myapplication
│ └── ExampleUnitTest.kt
├── build.gradle
├── common_lib
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── kotlinmvp
│ │ ├── Extensions.kt
│ │ ├── base
│ │ ├── BaseActivity.kt
│ │ ├── BaseFragment.kt
│ │ ├── BaseFragmentAdapter.kt
│ │ ├── BasePresenter.kt
│ │ ├── IBaseView.kt
│ │ └── IPresenter.kt
│ │ ├── glide
│ │ ├── CustomAppGlideModule.kt
│ │ ├── CustomBaseGlideUrlLoader.kt
│ │ └── GlideRoundTransform.kt
│ │ ├── net
│ │ ├── BaseResponse.kt
│ │ └── exception
│ │ │ ├── ApiException.kt
│ │ │ ├── ErrorStatus.kt
│ │ │ └── ExceptionHandle.kt
│ │ ├── rx
│ │ └── scheduler
│ │ │ ├── BaseScheduler.kt
│ │ │ ├── ComputationMainScheduler.kt
│ │ │ ├── IoMainScheduler.kt
│ │ │ ├── NewThreadMainScheduler.kt
│ │ │ ├── SchedulerUtils.kt
│ │ │ ├── SingleMainScheduler.kt
│ │ │ └── TrampolineMainScheduler.kt
│ │ ├── utils
│ │ ├── AppUtils.kt
│ │ ├── CleanLeakUtils.kt
│ │ ├── DisplayManager.kt
│ │ ├── GsonUtils.java
│ │ ├── NetworkUtil.kt
│ │ └── Preference.kt
│ │ └── view
│ │ ├── LoadingView.java
│ │ ├── ViewAnimUtils.kt
│ │ └── recyclerview
│ │ ├── MultipleType.kt
│ │ ├── ViewHolder.kt
│ │ └── adapter
│ │ ├── CommonAdapter.kt
│ │ ├── OnItemClickListener.kt
│ │ └── OnItemLongClickListener.kt
│ └── res
│ └── values
│ ├── attrs.xml
│ ├── colors.xml
│ ├── dimens.xml
│ ├── ids.xml
│ ├── strings.xml
│ └── styles.xml
├── config.gradle
├── demo.gif
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── multiple-status-view
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── classic
│ │ └── common
│ │ └── MultipleStatusView.java
│ └── res
│ ├── layout
│ ├── empty_view.xml
│ ├── error_view.xml
│ ├── loading_view.xml
│ └── no_network_view.xml
│ └── values
│ ├── attrs.xml
│ ├── ids.xml
│ ├── strings.xml
│ └── styles.xml
└── settings.gradle
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches/build_file_checksums.ser
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | .DS_Store
9 | /build
10 | /captures
11 | .externalNativeBuild
12 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 本demo纯学习之用 不可作为任何商业用途
2 | # 一套完整的数字货币与法币汇率转换以及货币符号匹配展示规则demo
3 | # KotlinMvp 纯demo 无杂质 的MVP基础框架
4 |
5 | 
6 |
7 | #### 1. 法币价格数据展示规则
8 | - x >= 1000 0 0000 无小数 //容错,暂时没有可能发生
9 | - 100 <= x < 1000 0 0000 2位小数 //例:$12345.12
10 | - 0.1<= x < 100 4小数 //例:$0.1234
11 | - 0.0001 <= x < 0.1 6位小数 //例:$0.001234
12 | - 0.00000001 <= x < 0.0001 8位小数 //例:$0.00001234
13 | - x < 0.00000001 展示为0 //容错,暂时没有可能发生
14 | - 没有数据时用"--"表示,有货币符号的数据不要带货币符号 //例:--
15 | - 小数直接截断不4舍5入,小数点末位为0的不省略 //例:$54321.10
16 | - 小数点全部为0的,隐藏 //例:$54321
17 |
18 |
19 | #### 2. 非法币价格数据展示规则
20 | - x >= 1000 0 0000 无小数 //容错,暂时没有可能发生
21 | - 1000 <= x < 100000000 4位小数 //例:12345.1234 Ht
22 | - 0.0001 <= x < 1000 6位小数 //例: 12.123456 Eth
23 | - 0.000001<= x < 0.0001 8位小数 //例: ฿0.00001234
24 | - 0.0000000001 <= x < 0.000001 10位小数 //例:฿0.0000001234
25 | - x < 0.0000000001 展示为0 //容错,暂时没有可能发生
26 | - 没有数据时用"--"表示,有货币符号的数据不要带货币符号 //例:--
27 | - 小数直接截断不4舍5入,小数点末位为0的不省略 //例:0.0123400 Eth
28 | - 小数点全部为0的,隐藏 //例:4321 Eth
29 | - 币作为单位时,前面留一个空格 //例:1.2345 Eth
30 | - 币作为单位时,第一位字母大写,后面都是小写 //例: 1.2345 Eth
31 |
32 |
33 | #### 3. 涨跌幅百分比(+/-x%)数据展示规则
34 | - x >= 10000 无小数点 //容错,小机率发生
35 | - 0.01 <= x < 10000 保留2位小数
36 | - x < 0.01 展示为0
37 | - *没有数据时用"--"表示 //例:--
38 | - *x=0时展示“0.00%”, 不展示"+/-"符号 //例:0.00%
39 | - 小数直接截断不4舍5入,小数点末位为0的不省略 //例:12.10%
40 | - 小数点全部为0的,展示 //例:12.00%
41 | > *在旧版中,返回数据不能辨别x=0与没有数据,如果还是不能解决,按x=0处理,web端展示为"0%",客户端为”0.00%“。
42 |
43 |
44 | #### 4. 基数词头的适配规则
45 | - 万 104 //默认>=105 时适配,法币价格>=106 时才适配
46 | - 亿 108
47 | - 万亿 1012 //国标;台,韩,日标准中称为“兆”
48 | - 支持基数词头适配的字段:
49 | 流通市值 | 流通数量 | 成交额 | 总市值 | 法币价格
50 | - 基数词头适配后,保留2位小数 //例:123.12万
51 | - 小数直接截断不4舍5入,小数点末位为0的不省略 //例:123.10万
52 | - 小数点全部为0的,隐藏 //例:123万
53 |
54 |
55 | #### 5. 基数的三位逗号分隔符的适配规则
56 | - *在简体中文语境下,没有适配基数词头的数字不采用三位逗号分隔。//例:54321; 5,432.12万
57 | - 在简体中文语境下,非行情数据展示的数字不采用逗号分隔。//例:阅读数 54321;关注数 5432.12万
58 | > *行情数据在其它非简体中文语境中,采用三位逗号分隔机制
59 |
60 |
61 | #### 6. 陆,港,台,日,韩,英文的基数词头的规范
62 | NUM | CN | HK | TW | JP | KR | EN
63 | ---|---|---|---|---|---|---
64 | 104 |1万 | 1萬 | 1萬 | 1万 | 1만 | 10K,1K=103
65 | 108 |1亿 | 1億 | 1億 | 1億 | 1억 | 100M,1M=106
66 | 1012 |1万亿 | 1萬億 | 1兆 | 1兆 | 1조 | 1000B,1B=109
67 |
68 |
69 | #### 7. 货币选顶中的货币符号
70 | 代码 | 货币 | 符号 | 范例
71 | ---|---|---|---
72 | xn5
73 | > *除ETH,BTC外,其它适用法币展示规则
74 |
75 |
76 | #### 8. 交易对中的其它法币符号
77 | - ##### 约等于法币价值的稳定代币, 可以使用法币符号替代,适用法币数据展示规则
78 | 代码 | 货币 | 对应法币 | 可替代符号
79 | ---|---|---|---
80 | BITCNY | 比特元 | 人民币 | ¥
81 | CNYT | 未知 | 人民币 | ¥
82 | CNH | 离岸人民币 | 人民币 | ¥
83 | BITEUR |未知 | 欧元 | €
84 | BITUSD | 未知 | 美元 | $
85 | CK.USD | 未知 | 美元 | $
86 | CKUSD | 未知 | 美元 | $
87 | USDT | 泰达币 | 美元 | $
88 | GUSD | 双子星美元 | 美元 | $
89 | TUSD | 未知 | 美元 | $
90 | USDC | 未知 | 美元 | $
91 | PAX | 未知 | 美元 | $
92 |
93 | - ##### 交易对中的其它法币符号,适用法币数据展示规则
94 | 代码 | 货币 | 符号 | 范例
95 | ---|---|---|---
96 | CLP | 智利比索 | CLP | CLP100
97 | COP | 哥伦比亚比索 | Col$ | Col$100
98 | ILS | 以色列新谢克尔 | ₪ | ₪100
99 | ISK | 冰岛克郎 | kr | kr100
100 | MYR | 马元 | RM | RM100
101 | NGN | 尼日利亚奈拉 | ₦ | ₦100
102 | NZD | 新西兰元 | NZ$ | NZ$100
103 | PEN | 秘鲁新索尔 | S/. | S/.100
104 | PHP | 菲律宾比索 | ₱ | ₱100
105 | PLN | 波兰兹罗提 | zł | zł100
106 | SGD | 新加坡元 | SG$ | SG$100
107 | THB | 泰铢* | ฿ | ฿100
108 | TRY | 土耳其里拉 | ₺ | ₺100
109 | UAH | 乌克兰格里夫纳 | ₴ | ₴100
110 | VND | 越南盾 | ₫ | ₫100
111 | ZAR | 南非兰特 | R | R100
112 |
113 | > *比特币货币符号฿是借用泰铢法币符号,会与泰铢符号产生岐义,泰铁符号使用"T฿"来区分。
114 |
115 | - 主价格为可切换的价格 (当前价格切换中的货币见“条目7”)
116 | - 副价格为原价 (全球指数默认是USD,交易对为平台价)
117 | - 当主,副价格为同一货币单位时,副价格展示为USD; 当同为USD时,副价格展示为CNY。(这条规则包含且不限于使用货币符号为单位的稳定代币)
118 |
119 |
120 | #### 10. 交易所数据请求统一的国家代码
121 | 代码 | 国家 | EN | TW | HK | JP
122 | ---|---|---|---|---|---
123 | cn | 中国 | China | 中國 | 中國 | 中国
124 | uk | 英国 | United Kingdom | 英國 | 英國 | イギリス
125 | us | 美国 | United States | 美國 | 美國 | アメリカ
126 | jp | 日本 | Japan | 日本 | 日本 | 日本
127 | kr | 韩国 | Korea | 韓國 | 韓國 | 韓国
128 | hk | 中国香港 | China Hong Kong | 中國香港 | 中國香港 | 香港、中国
129 | pl | 波兰 | Poland | 波蘭 | 波蘭 | ポーランド
130 | ca | 加拿大 | Canada | 加拿大 | 加拿大 | カナダ
131 | au | 澳洲 | Australia | 澳洲 | 澳洲 | オーストラリア
132 | th | 泰国 | Thailand | 泰國 | 泰國 | タイ
133 | in | 印度 | India | 印度 | 印度 | インド
134 | de | 德国 | Germany | 德國 | 德國 | ドイツ
135 | ua | 乌克兰 | Ukraine | 烏克蘭 | 烏克蘭 | ウクライナ
136 | nz | 新西兰 | new Zealand | 紐西蘭 | 新西蘭 | ニュージーランド
137 | ru | 俄罗斯 | Russia | 俄羅斯 | 俄羅斯 | ロシア
138 | dk | 丹麦 | Denmark | 丹麥 | 丹麥 | デンマーク
139 | sc | 塞舌尔 | Seychelles | 塞席爾 | 塞舌爾 | セイシェル
140 | nl | 荷兰 | Netherlands | 荷蘭 | 荷蘭 | オランダ
141 | es | 西班牙 | Spain | 西班牙 | 西班牙 | スペイン
142 | mv | 马耳他 | Malta | 馬爾他 | 馬爾他 | マルタ
143 | tz | 坦桑尼亚 | Tanzania | 坦尚尼亞 | 坦桑尼亞 | タンザニア
144 | mx | 墨西哥 | Mexico | 墨西哥 | 墨西哥 | メキシコ
145 | ch | 瑞士 | Switzerland | 瑞士 | 瑞士 | スイス
146 | br | 巴西 | Brazil | 巴西 | 巴西 | ブラジル
147 | tr | 土耳其 | Turkey | 土耳其 | 土耳其 | トルコ
148 | il | 以色列 | Israel | 以色列 | 以色列 | イスラエル
149 | tw | 中国台湾 | China Taiwan | 台灣 | 中國台灣 | 台湾、中国
150 | hr | 克罗地亚 | Croatia | 克羅埃西亞 | 克羅地亞 | クロアチア
151 | sg | 新加坡 | Singapore | 新加坡 | 新加坡 | シンガポール
152 | cl | 智利 | Chile | 智利 | 智利 | チリ
153 | ph | 菲律宾 | Philippines | 菲律賓 | 菲律賓 | フィリピン
154 | za | 南非 | South Africa | 南非 | 南非 | 南アフリカ
155 | mn | 蒙古国 | Mongolia | 蒙古 | 蒙古 | モンゴル
156 | ee | 爱沙尼亚 | Estonia | 愛沙尼亞 | 愛沙尼亞 | エストニア
157 | jo | 约旦 | Jordan | 約旦 | 約旦 | ヨルダン
158 | it | 意大利 | Italy | 義大利 | 意大利 | イタリア
159 | vn | 越南 | Vietnam | 越南 | 越南 | ベトナム
160 | ae | 阿联酋 | United Arab Emirates | 阿聯 | 阿聯酋 | アラブ首長国連邦
161 | ky | 开曼群岛 | Cayman Islands | 開曼群島 | 開曼群島 | ケイマン諸島
162 | my | 马来西亚 | Malaysia | 馬來西亞 | 馬來西亞 | マレーシア
163 | ws | 萨摩亚 | Samoa | 薩摩亞 | 薩摩亞 | サモア
164 | unknown | 未知 | unknown | 未知 | 未知 | 不明
165 | > 地域选项的命名一律不能称为“国家”,必须展示为“国家/地区”;
166 | 台湾地区命名必须使用“中国台湾”(台湾正体译文除外);
167 |
168 |
169 |
170 |
171 |
172 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | apply plugin: 'kotlin-android'
4 |
5 | apply plugin: 'kotlin-android-extensions'
6 |
7 | android {
8 | compileSdkVersion 28
9 | defaultConfig {
10 | applicationId "com.example.lanyixin.myapplication"
11 | minSdkVersion 21
12 | targetSdkVersion 28
13 | versionCode 1
14 | versionName "1.0"
15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
16 | multiDexEnabled true
17 | }
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | compileOptions {
25 | sourceCompatibility JavaVersion.VERSION_1_8
26 | targetCompatibility JavaVersion.VERSION_1_8
27 | }
28 | }
29 |
30 | dependencies {
31 | api fileTree(dir: 'libs', include: ['*.jar'])
32 | api 'com.android.support:multidex:1.0.1'
33 | api"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
34 | api 'com.android.support:appcompat-v7:28.0.0'
35 | api 'com.android.support.constraint:constraint-layout:1.1.3'
36 | testImplementation 'junit:junit:4.12'
37 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
38 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
39 | // spanbuilder
40 | api 'com.zrq:spanbuilder:1.0.5'
41 | api ('com.zzhoujay.richtext:richtext:3.0.7')
42 | api 'com.android.support:design:27.1.1'
43 | api project(':common_lib')
44 | }
45 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/example/lanyixin/myapplication/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.lanyixin.myapplication
2 |
3 | import android.support.test.InstrumentationRegistry
4 | import android.support.test.runner.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getTargetContext()
22 | assertEquals("com.example.lanyixin.myapplication", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/lanyixin/myapplication/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.lanyixin.myapplication
2 |
3 | import android.content.Intent
4 | import android.os.Build
5 | import android.text.Editable
6 | import android.text.TextWatcher
7 | import android.util.Log
8 | import com.example.lanyixin.myapplication.api.ResultBase
9 | import com.example.lanyixin.myapplication.format.CFUtils
10 | import com.kotlinmvp.mvp.contract.DemoContract
11 | import com.kotlinmvp.base.BaseActivity
12 | import com.kotlinmvp.mvp.presenter.DemoPresenter
13 | import kotlinx.android.synthetic.main.activity_main.*
14 | import java.lang.StringBuilder
15 | import java.util.*
16 | import java.util.regex.Pattern
17 |
18 | class MainActivity : BaseActivity(), DemoContract.View {
19 |
20 | override fun initData() {
21 | mPresenter.requestHotWordData();
22 | }
23 |
24 | override fun initView() {
25 |
26 | title = "一套完整的数字货币与法币汇率转换以及货币符号匹配展示规则demo"
27 |
28 | val res = this.getResources()
29 | val config = res.getConfiguration()
30 | var locale = res.getConfiguration().locale
31 | locale = Locale.SIMPLIFIED_CHINESE
32 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
33 | config.setLocale(locale)
34 | } else {
35 | config.locale = locale
36 | }
37 | this
38 | .getResources()
39 | .updateConfiguration(
40 | config,
41 | this.getResources().getDisplayMetrics()
42 | )
43 |
44 | switch1.setOnCheckedChangeListener { buttonView, isChecked ->
45 | val res = this.getResources()
46 | val config = res.getConfiguration()
47 | var locale = res.getConfiguration().locale
48 |
49 | if (isChecked) {
50 | switch1.text = Locale.ENGLISH.language
51 | locale = Locale.ENGLISH
52 | } else {
53 | switch1.text = Locale.SIMPLIFIED_CHINESE.language
54 | locale = Locale.SIMPLIFIED_CHINESE
55 | }
56 |
57 | Log.i("df", locale.getCountry() + " " + locale.getLanguage())
58 |
59 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
60 | config.setLocale(locale)
61 | } else {
62 | config.locale = locale
63 | }
64 |
65 | this
66 | .getResources()
67 | .updateConfiguration(
68 | config,
69 | this.getResources().getDisplayMetrics()
70 | )
71 | }
72 |
73 | floatingActionButton.setOnClickListener {
74 | val intent = Intent()
75 | intent.setClass(this@MainActivity, TxtActivity::class.java!!)
76 | startActivity(intent)
77 | }
78 |
79 | editText.addTextChangedListener(object : TextWatcher {
80 | override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
81 |
82 | }
83 |
84 | override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
85 |
86 | }
87 |
88 | override fun afterTextChanged(s: Editable) {
89 | val str = s.toString()
90 |
91 | if (str.isEmpty()) return
92 | if (str.length > 30) return
93 | if (!isNumeric(str)) return
94 |
95 | val vol = java.lang.Double.valueOf(str)
96 | num.text = str
97 |
98 | val builder = CFUtils.Builder(application)
99 |
100 | //法币
101 | fb1.text = builder.setnumber(vol)
102 | .setlega(true)
103 | .setword(true)
104 | .setformat(true)
105 | .build()
106 | .cfRules(application)
107 | fb2.text = builder.setnumber(vol)
108 | .setlega(true)
109 | .setword(true)
110 | .setformat(false)
111 | .build()
112 | .cfRules(application)
113 | fb3.text = builder.setnumber(vol)
114 | .setlega(true)
115 | .setword(false)
116 | .setformat(true)
117 | .build()
118 | .cfRules(application)
119 | fb4.text = builder.setnumber(vol)
120 | .setlega(true)
121 | .setword(false)
122 | .setformat(false)
123 | .build()
124 | .cfRules(application)
125 |
126 | //虚拟货币
127 | xn1.text = builder.setnumber(vol)
128 | .setlega(false)
129 | .setword(true)
130 | .setformat(true)
131 | .build()
132 | .cfRules(application)
133 | xn2.text = builder.setnumber(vol)
134 | .setlega(false)
135 | .setword(true)
136 | .setformat(false)
137 | .build()
138 | .cfRules(application)
139 | xn3.text = builder.setnumber(vol)
140 | .setlega(false)
141 | .setword(false)
142 | .setformat(true)
143 | .build()
144 | .cfRules(application)
145 | xn4.text = builder.setnumber(vol)
146 | .setlega(false)
147 | .setword(false)
148 | .setformat(false)
149 | .build()
150 | .cfRules(application)
151 |
152 | xn5.text = CFUtils.percentage(vol * 100)
153 | }
154 | })
155 |
156 | }
157 |
158 | //判断是否是数字
159 | fun isNumeric(str: String): Boolean {
160 | //Pattern pattern = Pattern.compile("-?[0-9]+.?[0-9]+");//这个有问题,一位的整数不能通过
161 | val pattern = Pattern.compile("^(\\-|\\+)?\\d+(\\.\\d+)?$")//这个是对的
162 | val isNum = pattern.matcher(str)
163 | return isNum.matches()
164 | }
165 |
166 | override fun start() {
167 | }
168 |
169 | override fun showError(errorMsg: String, errorCode: Int) {
170 | }
171 |
172 | override fun setHotWordData(string: ResultBase>) {
173 |
174 | val stringBuffer = StringBuilder()
175 |
176 | for (item in string.data) {
177 | println(item)
178 | stringBuffer.append(item + " ")
179 | }
180 | test.text = stringBuffer.toString()
181 | }
182 |
183 | override fun showLoading() {
184 | }
185 |
186 | override fun dismissLoading() {
187 | }
188 |
189 | override fun layoutId(): Int = R.layout.activity_main
190 |
191 | private val mPresenter by lazy { DemoPresenter() }
192 |
193 | init {
194 | mPresenter.attachView(this)
195 | }
196 |
197 | }
198 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/lanyixin/myapplication/MyApplication.kt:
--------------------------------------------------------------------------------
1 | package com.example.lanyixin.myapplication
2 |
3 | import android.app.Activity
4 | import android.app.Application
5 | import android.content.Context
6 | import android.os.Bundle
7 | import android.util.Log
8 | import com.facebook.stetho.Stetho
9 | import com.orhanobut.logger.AndroidLogAdapter
10 | import com.orhanobut.logger.Logger
11 | import com.orhanobut.logger.PrettyFormatStrategy
12 | import kotlin.properties.Delegates
13 |
14 |
15 | class MyApplication : Application(){
16 |
17 |
18 | companion object {
19 |
20 | private val TAG = "MyApplication"
21 |
22 | var context: Context by Delegates.notNull()
23 | private set
24 |
25 | }
26 |
27 | override fun onCreate() {
28 | super.onCreate()
29 | context = applicationContext
30 | initConfig()
31 | registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks)
32 |
33 | //Stetho
34 | Stetho.initialize(
35 | Stetho.newInitializerBuilder(context)
36 | .enableDumpapp(Stetho.defaultDumperPluginsProvider(context))
37 | .enableWebKitInspector(Stetho.defaultInspectorModulesProvider(context))
38 | .build()
39 | )
40 |
41 | }
42 |
43 | /**
44 | * 初始化配置
45 | */
46 | private fun initConfig() {
47 |
48 | val formatStrategy = PrettyFormatStrategy.newBuilder()
49 | .showThreadInfo(false) // 隐藏线程信息 默认:显示
50 | .methodCount(0) // 决定打印多少行(每一行代表一个方法)默认:2
51 | .methodOffset(7) // (Optional) Hides internal method calls up to offset. Default 5
52 | .tag("hao_zz") // (Optional) Global tag for every log. Default PRETTY_LOGGER
53 | .build()
54 | Logger.addLogAdapter(object : AndroidLogAdapter(formatStrategy) {
55 | override fun isLoggable(priority: Int, tag: String?): Boolean {
56 | return BuildConfig.DEBUG
57 | }
58 | })
59 | }
60 |
61 |
62 | private val mActivityLifecycleCallbacks = object : Application.ActivityLifecycleCallbacks {
63 | override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
64 | Log.d(TAG, "onCreated: " + activity.componentName.className)
65 | }
66 |
67 | override fun onActivityStarted(activity: Activity) {
68 | Log.d(TAG, "onStart: " + activity.componentName.className)
69 | }
70 |
71 | override fun onActivityResumed(activity: Activity) {
72 |
73 | }
74 |
75 | override fun onActivityPaused(activity: Activity) {
76 |
77 | }
78 |
79 | override fun onActivityStopped(activity: Activity) {
80 |
81 | }
82 |
83 | override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
84 |
85 | }
86 |
87 | override fun onActivityDestroyed(activity: Activity) {
88 | Log.d(TAG, "onDestroy: " + activity.componentName.className)
89 | }
90 | }
91 |
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/lanyixin/myapplication/api/ApiService.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.api
2 |
3 | import com.example.lanyixin.myapplication.api.ResultBase
4 | import retrofit2.http.GET
5 | import java.util.*
6 | import io.reactivex.Observable
7 |
8 | interface ApiService{
9 |
10 | @GET("api/coin/hotsearch")
11 | fun getHotsearch(): Observable>>
12 |
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/lanyixin/myapplication/api/ResultBase.java:
--------------------------------------------------------------------------------
1 | package com.example.lanyixin.myapplication.api;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | * @author easin on 2018/6/22 18:25
7 | */
8 |
9 | public class ResultBase implements Serializable {
10 | /**
11 | * status : true
12 | * code : 0
13 | * action : add
14 | * msg : 关注成功
15 | * data :
16 | */
17 |
18 | private String status;
19 | private String code;
20 | private String action;
21 | private String msg;
22 | private T data;
23 |
24 | public String getStatus() {
25 | return status;
26 | }
27 |
28 | public void setStatus(String status) {
29 | this.status = status;
30 | }
31 |
32 | public String getCode() {
33 | return code;
34 | }
35 |
36 | public void setCode(String code) {
37 | this.code = code;
38 | }
39 |
40 | public String getAction() {
41 | return action;
42 | }
43 |
44 | public void setAction(String action) {
45 | this.action = action;
46 | }
47 |
48 | public String getMsg() {
49 | return msg;
50 | }
51 |
52 | public void setMsg(String msg) {
53 | this.msg = msg;
54 | }
55 |
56 | public T getData() {
57 | return data;
58 | }
59 |
60 | public void setData(T data) {
61 | this.data = data;
62 | }
63 |
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/lanyixin/myapplication/api/RetrofitManager.kt:
--------------------------------------------------------------------------------
1 | package com.example.lanyixin.myapplication.api
2 |
3 | import android.content.Context
4 | import android.support.constraint.solver.Cache
5 | import com.example.lanyixin.myapplication.MyApplication
6 | import com.facebook.stetho.okhttp3.StethoInterceptor
7 | import com.kotlinmvp.api.ApiService
8 | import com.kotlinmvp.utils.AppUtils
9 | import com.kotlinmvp.utils.NetworkUtil
10 | import com.kotlinmvp.utils.Preference
11 | import okhttp3.CacheControl
12 | import okhttp3.Interceptor
13 | import okhttp3.OkHttpClient
14 | import okhttp3.Request
15 | import okhttp3.logging.HttpLoggingInterceptor
16 | import retrofit2.Retrofit
17 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
18 | import retrofit2.converter.gson.GsonConverterFactory
19 | import java.io.File
20 | import java.util.concurrent.TimeUnit
21 |
22 | object RetrofitManager {
23 |
24 | val service: ApiService by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
25 | getRetrofit().create(ApiService::class.java)
26 | }
27 |
28 | private var token: String by Preference(MyApplication.context, "token", "")
29 |
30 | /**
31 | * 设置头
32 | */
33 | private fun addHeaderInterceptor(): Interceptor {
34 | return Interceptor { chain ->
35 | val originalRequest = chain.request()
36 | val requestBuilder = originalRequest.newBuilder()
37 | // Provide your custom header here
38 | .header("token", token)
39 | .method(originalRequest.method(), originalRequest.body())
40 | val request = requestBuilder.build()
41 | chain.proceed(request)
42 | }
43 | }
44 |
45 | /**
46 | * 设置缓存
47 | */
48 | private fun addCacheInterceptor(context: Context): Interceptor {
49 | return Interceptor { chain ->
50 | var request = chain.request()
51 | if (!NetworkUtil.isNetworkAvailable(context)) {
52 | request = request.newBuilder()
53 | .cacheControl(CacheControl.FORCE_CACHE)
54 | .build()
55 | }
56 | val response = chain.proceed(request)
57 | if (NetworkUtil.isNetworkAvailable(context)) {
58 | val maxAge = 0
59 | // 有网络时 设置缓存超时时间0个小时 ,意思就是不读取缓存数据,只对get有用,post没有缓冲
60 | response.newBuilder()
61 | .header("Cache-Control", "public, max-age=" + maxAge)
62 | .removeHeader("Retrofit")// 清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效
63 | .build()
64 | } else {
65 | // 无网络时,设置超时为4周 只对get有用,post没有缓冲
66 | val maxStale = 60 * 60 * 24 * 28
67 | response.newBuilder()
68 | .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
69 | .removeHeader("nyn")
70 | .build()
71 | }
72 | response
73 | }
74 | }
75 |
76 | private fun getRetrofit(): Retrofit {
77 | // 获取retrofit的实例
78 | return Retrofit.Builder()
79 | .baseUrl("https://app.fxh.io/") //自己配置
80 | .client(getOkHttpClient(MyApplication.context))
81 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
82 | .addConverterFactory(GsonConverterFactory.create())
83 | .build()
84 | }
85 |
86 | private fun getOkHttpClient(context: Context): OkHttpClient {
87 |
88 | //添加一个log拦截器,打印所有的log
89 | val httpLoggingInterceptor = HttpLoggingInterceptor()
90 | //可以设置请求过滤的水平,body,basic,headers
91 | httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
92 |
93 | //设置 请求的缓存的大小跟位置
94 | // val cacheFile = File(context.cacheDir, "cache")
95 | // val cache = Cache(cacheFile, 1024 * 1024 * 50) //50Mb 缓存的大小
96 |
97 | return OkHttpClient.Builder()
98 | // .addInterceptor(addQueryParameterInterceptor()) //参数添加
99 | .addInterceptor(addHeaderInterceptor()) // token过滤
100 | // .addInterceptor(addCacheInterceptor())
101 | .addInterceptor(httpLoggingInterceptor) //日志,所有的请求响应度看到
102 | // .cache(cache) //添加缓存
103 | .addNetworkInterceptor(StethoInterceptor())//抓包
104 | .connectTimeout(60L, TimeUnit.SECONDS)
105 | .readTimeout(60L, TimeUnit.SECONDS)
106 | .writeTimeout(60L, TimeUnit.SECONDS)
107 | .build()
108 | }
109 |
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/lanyixin/myapplication/contract/DemoContract.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.mvp.contract
2 |
3 | import com.example.lanyixin.myapplication.api.ResultBase
4 | import com.kotlinmvp.base.IBaseView
5 | import com.kotlinmvp.base.IPresenter
6 |
7 |
8 | /**
9 | * 契约类
10 | */
11 | interface DemoContract {
12 |
13 | interface View : IBaseView {
14 |
15 | fun showError(errorMsg: String,errorCode:Int)
16 |
17 | /**
18 | * 获取数据
19 | */
20 | fun setHotWordData(string: ResultBase>)
21 | }
22 |
23 |
24 | interface Presenter : IPresenter {
25 | /**
26 | * 显示数据
27 | */
28 | fun requestHotWordData()
29 |
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/lanyixin/myapplication/format/CacheHelper.java:
--------------------------------------------------------------------------------
1 | package com.example.lanyixin.myapplication.format;
2 |
3 | import android.content.Context;
4 | import android.support.v4.util.ArrayMap;
5 | import android.text.TextUtils;
6 | import com.example.lanyixin.myapplication.R;
7 | import com.google.gson.reflect.TypeToken;
8 | import com.kotlinmvp.utils.GsonUtils;
9 |
10 | import java.util.List;
11 | import java.util.Map;
12 |
13 | public class CacheHelper {
14 |
15 | static CacheHelper mInstance;
16 |
17 | /**
18 | * Application#getApplicationContext
19 | */
20 | public static CacheHelper getInstance(Context context) {
21 | if (mInstance == null) {
22 | mInstance = new CacheHelper(context);
23 | }
24 | return mInstance;
25 | }
26 |
27 | public CacheHelper(Context context) {
28 | initHuilv(context);
29 | mLocalIcon = new ArrayMap();
30 | initLocalIcons();
31 | }
32 |
33 | /**
34 | * 汇率生成
35 | */
36 | private Map mHuilvCache = new ArrayMap<>();
37 |
38 | public void putHuilv(String key, double num) {
39 | this.mHuilvCache.put(key, num);
40 | }
41 |
42 | public double getHuilv(String key) {
43 | Double obj = mHuilvCache.get(key);
44 | return obj != null ? obj.doubleValue() : 0;
45 | }
46 |
47 | // 初始化汇率集合
48 | public void initHuilv(Context context) {
49 | //保存的汇率列表
50 | List rateBeans = CacheManager.get("rate_list");
51 | if (isEmpty(rateBeans)) {
52 | rateBeans =
53 | GsonUtils.changeGsonToList(PriceUtils.getConfigJson(R.raw.huilv,context),
54 | new TypeToken>() {
55 | }.getType());
56 | }
57 | for (RateBean rateBean : rateBeans) {
58 | putHuilv(rateBean.getSymbol().toUpperCase(), rateBean.getRate());
59 | }
60 | }
61 |
62 | public static boolean isEmpty(List paramList) {
63 | if ((paramList == null) || paramList.isEmpty()) {
64 | return true;
65 | }
66 | return false;
67 | }
68 |
69 | // 当前的货币单位
70 | private String mCurrenPriceShowType = "CNY";
71 |
72 | // 根据货币单位 获取货币符号
73 | public String getUnit(String market) {
74 | if ("BTC".equalsIgnoreCase(market)) {
75 | return "฿";
76 | } else if ("ETH".equalsIgnoreCase(market)) {
77 | return "ETH";
78 | } else if ("EUR".equalsIgnoreCase(market) || "BITEUR".equalsIgnoreCase(market)) {
79 | return "€";
80 | } else if ("CNY".equalsIgnoreCase(market)
81 | || "BITCNY".equalsIgnoreCase(market)
82 | || "QC".equalsIgnoreCase(market)
83 | || "CNH".equalsIgnoreCase(market)
84 | || "CNYT".equalsIgnoreCase(market)) {
85 | return "¥";
86 | } else if ("KRW".equalsIgnoreCase(market)) {
87 | return "₩";
88 | } else if ("RUB".equalsIgnoreCase(market)) {
89 | return "\u20BD";
90 | } else if ("JPY".equalsIgnoreCase(market)) {
91 | return "円";
92 | } else if ("AUD".equalsIgnoreCase(market)) {
93 | return "A$";
94 | } else if ("GBP".equalsIgnoreCase(market)) {
95 | return "£";
96 | } else if ("SGD".equalsIgnoreCase(market)) {
97 | return "S$";
98 | } else if ("USD".equalsIgnoreCase(market)
99 | || "PAX".equalsIgnoreCase(market)
100 | || "USDT".equalsIgnoreCase(market)
101 | || "GUSD".equalsIgnoreCase(market)
102 | || "TUSD".equalsIgnoreCase(market)
103 | || "USDC".equalsIgnoreCase(market)
104 | || "BITUSD".equalsIgnoreCase(market)
105 | || "CK.USD".equalsIgnoreCase(market)
106 | || "CKUSD".equalsIgnoreCase(market)) {
107 | return "$";
108 | } else if ("INR".equalsIgnoreCase(market)) {
109 | return "₹";
110 | } else if ("CHF".equalsIgnoreCase(market)) {
111 | return "₣";
112 | } else if ("CAD".equalsIgnoreCase(market)) {
113 | return "C$";
114 | } else if ("IDR".equalsIgnoreCase(market)) {
115 | return "Rp";
116 | } else if ("BRL".equalsIgnoreCase(market)) {
117 | return "R$";
118 | } else if ("MXN".equalsIgnoreCase(market)) {
119 | return "MXN";
120 | } else if ("CNY".equalsIgnoreCase(market) || "CN".equalsIgnoreCase(market)) {
121 | return "¥";
122 | } else if ("HKD".equalsIgnoreCase(market)) {
123 | return "HK$";
124 | } else if ("CLP".equalsIgnoreCase(market)) {
125 | return "CLP";
126 | } else if ("COP".equalsIgnoreCase(market)) {
127 | return "Col$";
128 | } else if ("ILS".equalsIgnoreCase(market)) {
129 | return "₪";
130 | } else if ("ISK".equalsIgnoreCase(market)) {
131 | return "kr";
132 | } else if ("MYR".equalsIgnoreCase(market)) {
133 | return "RM";
134 | } else if ("NGN".equalsIgnoreCase(market)) {
135 | return "₦";
136 | } else if ("NZD".equalsIgnoreCase(market)) {
137 | return "NZ$";
138 | } else if ("PEN".equalsIgnoreCase(market)) {
139 | return "S/.";
140 | } else if ("PHP".equalsIgnoreCase(market)) {
141 | return "₱";
142 | } else if ("PLN".equalsIgnoreCase(market)) {
143 | return "zł";
144 | } else if ("THB".equalsIgnoreCase(market)) {
145 | return "T฿";
146 | } else if ("TRY".equalsIgnoreCase(market)) {
147 | return "₺";
148 | } else if ("TWD".equalsIgnoreCase(market)) {
149 | return "NT$";
150 | } else if ("UAH".equalsIgnoreCase(market)) {
151 | return "₴";
152 | } else if ("VND".equalsIgnoreCase(market)) {
153 | return "₫";
154 | } else {
155 | return market;
156 | }
157 | }
158 |
159 | /**
160 | * 汇率换算
161 | *
162 | * @param currencyValue 金额
163 | * @param market1 的货币类型
164 | * @param market2 要兑换的货币类型
165 | */
166 | public double currencyExchange(Context context,double currencyValue, String market1,
167 | String market2) {
168 |
169 | if (market1.equalsIgnoreCase(market2)) {
170 | // 传入的货币==用户设置的货币
171 | return currencyValue;
172 | } else {
173 | // 传入的是美元返回1,否者尝试取得汇率
174 | double rate = market1.equalsIgnoreCase("USD") ? 1 :
175 | CacheHelper.getInstance(context).getHuilv(market1.toUpperCase());
176 | if (rate == 0) {
177 | return Double.NaN;
178 | }
179 | // 得到以美元为基准的转化后的原始金额数值
180 | double usdValue = currencyValue / rate;
181 | if ("USD".equalsIgnoreCase(market2)) {
182 | return usdValue;
183 | }
184 | // 不是美元 获取要越换的汇率
185 | rate = CacheHelper.getInstance(context).getHuilv(market2.toUpperCase());
186 | if (rate == 0) {
187 | return Double.NaN;
188 | }
189 | return usdValue * rate;
190 | }
191 | }
192 |
193 | //国家国旗
194 | private Map mLocalIcon;
195 |
196 | private void initLocalIcons() {
197 | mLocalIcon.put("asny", "ic_aishaniya.png");
198 | mLocalIcon.put("alq", "ic_alq.png");
199 | mLocalIcon.put("aus", "ic_aus.png");
200 | mLocalIcon.put("brazil", "ic_brazil.png");
201 | mLocalIcon.put("pola", "ic_pola.png");
202 | mLocalIcon.put("danmark", "ic_danmarki.png");
203 | mLocalIcon.put("de", "ic_de.png");
204 | mLocalIcon.put("els", "ic_els.png");
205 | mLocalIcon.put("philippines", "ic_feilvbin.png");
206 | mLocalIcon.put("kr", "ic_kr.png");
207 | mLocalIcon.put("holland", "ic_holland.png");
208 | mLocalIcon.put("can", "ic_can.png");
209 | mLocalIcon.put("cayman", "ic_cayman.png");
210 | mLocalIcon.put("kldy", "ic_kldy.png");
211 | mLocalIcon.put("malta", "ic_malta.png");
212 | mLocalIcon.put("malaysia", "ic_malaysia.png");
213 | mLocalIcon.put("us", "ic_us.png");
214 | mLocalIcon.put("mengu", "ic_mengu.png");
215 | mLocalIcon.put("mesica", "ic_mesica.png");
216 | mLocalIcon.put("nanfei", "ic_nanfei.png");
217 | mLocalIcon.put("jp", "ic_jp.png");
218 | mLocalIcon.put("switzerland", "ic_switzerland.png");
219 | mLocalIcon.put("sser", "ic_sser.png");
220 | mLocalIcon.put("samora", "ic_samora.png");
221 | mLocalIcon.put("thailand", "ic_thailand.png");
222 | mLocalIcon.put("tw", "ic_tw.png");
223 | mLocalIcon.put("tsny", "ic_tsny.png");
224 | mLocalIcon.put("teq", "ic_teq.png");
225 | mLocalIcon.put("ic_unknow", "ic_unknow.png");
226 | mLocalIcon.put("wkl", "ic_wkl.png");
227 | mLocalIcon.put("hongkong", "ic_hongkong.png");
228 | mLocalIcon.put("spain", "ic_spain.png");
229 | mLocalIcon.put("singepo", "ic_singepo.png");
230 | mLocalIcon.put("newzeland", "ic_newzeland.png");
231 | mLocalIcon.put("ydl", "ic_ydl.png");
232 | mLocalIcon.put("india", "ic_india.png");
233 | mLocalIcon.put("uk", "ic_uk.png");
234 | mLocalIcon.put("ysl", "ic_yiselie.png");
235 | mLocalIcon.put("yuedan", "ic_yuedan.png");
236 | mLocalIcon.put("vietnam", "ic_yuenan.png");
237 | mLocalIcon.put("zl", "ic_zl.png");
238 | mLocalIcon.put("cn", "ic_cn.png");
239 | }
240 |
241 | public String getLocalIcon(String key) {
242 | return mLocalIcon.get(key);
243 | }
244 | }
245 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/lanyixin/myapplication/format/CacheManager.java:
--------------------------------------------------------------------------------
1 | package com.example.lanyixin.myapplication.format;
2 |
3 | import android.text.TextUtils;
4 | import com.orhanobut.logger.Logger;
5 |
6 | import java.io.*;
7 | import java.security.MessageDigest;
8 |
9 | /**
10 | * 文件序列化缓存管理器
11 | * 缓存路径:data/data//Cache_Serialization
12 | *
13 | * @author weige
14 | */
15 | public class CacheManager {
16 |
17 | /**
18 | * 缓存数据目录
19 | */
20 | private static final String CACHE_SERIALIZATION_DIR = "Cache_Serialization";
21 |
22 | private static final String CACHE_VERSION = "CACHE_VERSION";
23 |
24 | private static final String TAG = "CacheManager";
25 |
26 | private static File cacheDir;
27 |
28 | private CacheManager() {
29 | }
30 |
31 | private static void deleteOldCacheDir(final File oldCacheDir) {
32 | // new MyAsyncTask() {
33 | // @Override
34 | // protected Void doInBackground(Void... params) {
35 | // deleteFile(oldCacheDir);
36 | // return null;
37 | // }
38 | // }.execute();
39 | }
40 |
41 | public static String md5(String str) {
42 | try {
43 | MessageDigest md5 = MessageDigest.getInstance("MD5");
44 | md5.update(str.getBytes("UTF-8"));
45 | byte[] encryption = md5.digest();
46 |
47 | StringBuilder strBuf = new StringBuilder();
48 | for (byte b : encryption) {
49 | if (Integer.toHexString(0xff & b).length() == 1) {
50 | strBuf.append("0").append(Integer.toHexString(0xff & b));
51 | } else {
52 | strBuf.append(Integer.toHexString(0xff & b));
53 | }
54 | }
55 | return strBuf.toString();
56 | } catch (Exception e) {
57 | Logger.e(TAG, "makeKey# error=", e);
58 | return "";
59 | }
60 | }
61 |
62 | /**
63 | * 存储
64 | *
65 | * @param key 唯一key
66 | * @param data data
67 | */
68 | public static void save(String key, Object data) {
69 | if (TextUtils.isEmpty(key)) {
70 | return;
71 | }
72 | String md5 = md5(key);
73 | Logger.i(TAG, "save# key=" + key + ", md5=" + md5);
74 | serialization(md5, data);
75 | }
76 |
77 | /**
78 | * 读取
79 | *
80 | * @param key 唯一key
81 | * @return Object
82 | */
83 | @SuppressWarnings("unchecked")
84 | public static T get(String key) {
85 | if (TextUtils.isEmpty(key)) {
86 | return null;
87 | }
88 | String md5 = md5(key);
89 | Logger.i(TAG, "get# key=" + key + ", md5=" + md5);
90 | return (T) deserialization(md5);
91 | }
92 |
93 | /**
94 | * 删除
95 | *
96 | * @param key 唯一key
97 | */
98 | public static void delete(String key) {
99 | if (TextUtils.isEmpty(key)) {
100 | return;
101 | }
102 | String md5 = md5(key);
103 | Logger.i(TAG, "delete# key=" + key + ", md5=" + md5);
104 | File file = new File(cacheDir, md5);
105 | deleteFile(file);
106 | }
107 |
108 |
109 | /**
110 | * serialize to file
111 | *
112 | * @param key 文件名
113 | * @param obj Object
114 | */
115 | private static void serialization(String key, Object obj) {
116 | File file = new File(cacheDir, key);
117 | if (file.exists()) {
118 | deleteFile(file);
119 | }
120 | ObjectOutputStream out = null;
121 | try {
122 | out = new ObjectOutputStream(new FileOutputStream(file));
123 | out.writeObject(obj);
124 | } catch (Throwable e) {
125 | deleteFile(file);
126 | Logger.e(TAG, "serialization#", e);
127 | } finally {
128 | if (out != null) {
129 | try {
130 | out.close();
131 | } catch (Throwable e) {
132 | Logger.e(TAG, "serialization#", e);
133 | }
134 | }
135 | }
136 | }
137 |
138 | /**
139 | * deserialization from file
140 | *
141 | * @param key 文件名
142 | * @return Object
143 | */
144 | private static Object deserialization(String key) {
145 | File file = new File(cacheDir, key);
146 | if (!file.exists()) {
147 | return null;
148 | }
149 | ObjectInputStream in = null;
150 | try {
151 | in = new ObjectInputStream(new FileInputStream(file));
152 | return in.readObject();
153 | } catch (Throwable e) {
154 | deleteFile(file);
155 | Logger.e(TAG, "deserialization#", e);
156 | } finally {
157 | if (in != null) {
158 | try {
159 | in.close();
160 | } catch (Throwable e) {
161 | Logger.e(TAG, "deserialization#", e);
162 | }
163 | }
164 | }
165 | return null;
166 | }
167 |
168 | /**
169 | * delete file
170 | *
171 | * @param file file
172 | * @return true if delete success
173 | */
174 | private static boolean deleteFile(File file) {
175 | if (!file.exists()) {
176 | return true;
177 | }
178 | if (file.isDirectory()) {
179 | File[] files = file.listFiles();
180 | for (File f : files) {
181 | deleteFile(f);
182 | }
183 | }
184 | return file.delete();
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/lanyixin/myapplication/format/PriceUtils.java:
--------------------------------------------------------------------------------
1 | package com.example.lanyixin.myapplication.format;
2 |
3 |
4 | import android.content.Context;
5 | import com.example.lanyixin.myapplication.R;
6 |
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.io.UnsupportedEncodingException;
10 | import java.util.Locale;
11 |
12 |
13 | public class PriceUtils {
14 |
15 | /**
16 | * 获取配置json 位置:Raw Resource
17 | */
18 | public static String getConfigJson(int res, Context context) {
19 | try {
20 | InputStream is = context.getResources().openRawResource(res);
21 | byte[] buf = new byte[is.available()];//读取整个文件
22 | is.read(buf);
23 | is.close();
24 | return byteToString(buf);
25 | } catch (IOException e) {
26 | e.printStackTrace();
27 | }
28 | return "";
29 | }
30 |
31 | public static String byteToString(byte[] data) {
32 | String result = "";
33 | try {
34 | result = new String(data, "utf-8");
35 | } catch (UnsupportedEncodingException e) {
36 | }
37 | return result;
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/lanyixin/myapplication/format/RateBean.java:
--------------------------------------------------------------------------------
1 | package com.example.lanyixin.myapplication.format;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | * Created by jiongjiong on 2018/12/11.
7 | */
8 | public class RateBean implements Serializable {
9 | private String symbol;
10 |
11 | private double rate;
12 |
13 | public String getSymbol() {
14 | return symbol;
15 | }
16 |
17 | public void setSymbol(String symbol) {
18 | this.symbol = symbol;
19 | }
20 |
21 | public double getRate() {
22 | return rate;
23 | }
24 |
25 | public void setRate(double rate) {
26 | this.rate = rate;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/lanyixin/myapplication/model/DemoModel.kt:
--------------------------------------------------------------------------------
1 | package com.example.lanyixin.myapplication.model
2 |
3 | import com.example.lanyixin.myapplication.api.ResultBase
4 | import com.example.lanyixin.myapplication.api.RetrofitManager
5 | import com.kotlinmvp.rx.scheduler.SchedulerUtils
6 | import java.util.*
7 | import kotlin.collections.ArrayList
8 | import io.reactivex.Observable
9 |
10 | class DemoModel {
11 |
12 | /**
13 | * 请求数据
14 | */
15 | fun requestHotWordData(): Observable>> {
16 | return RetrofitManager.service.getHotsearch()
17 | .compose(SchedulerUtils.ioToMain())
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/lanyixin/myapplication/presenter/DemoPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.mvp.presenter
2 |
3 | import com.example.lanyixin.myapplication.model.DemoModel
4 | import com.kotlinmvp.mvp.contract.DemoContract
5 | import com.kotlinmvp.base.BasePresenter
6 | import com.kotlinmvp.net.exception.ExceptionHandle
7 |
8 |
9 | class DemoPresenter : BasePresenter(), DemoContract.Presenter {
10 |
11 | private val mModel by lazy { DemoModel() }
12 |
13 | /**
14 | * 获取数据
15 | */
16 | override fun requestHotWordData() {
17 |
18 | addSubscription(disposable = mModel.requestHotWordData()
19 | .subscribe({ string ->
20 | mRootView?.apply {
21 | setHotWordData(string)
22 | }
23 | }, { throwable ->
24 | mRootView?.apply {
25 | //处理异常
26 | showError(ExceptionHandle.handleException(throwable),ExceptionHandle.errorCode)
27 | }
28 | }))
29 | }
30 |
31 | }
--------------------------------------------------------------------------------
/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/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
10 |
12 |
14 |
16 |
18 |
20 |
22 |
24 |
26 |
28 |
30 |
32 |
34 |
36 |
38 |
40 |
42 |
44 |
46 |
48 |
50 |
52 |
54 |
56 |
58 |
60 |
62 |
64 |
66 |
68 |
70 |
72 |
74 |
75 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
32 |
42 |
51 |
60 |
69 |
79 |
88 |
98 |
107 |
118 |
128 |
138 |
148 |
158 |
166 |
176 |
184 |
192 |
200 |
201 |
211 |
221 |
231 |
232 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_txt.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bayshier/MVPKotlin_Commonlib/d9aa24e8f99520d99ec5d3cacd5936fc1ecdba53/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bayshier/MVPKotlin_Commonlib/d9aa24e8f99520d99ec5d3cacd5936fc1ecdba53/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bayshier/MVPKotlin_Commonlib/d9aa24e8f99520d99ec5d3cacd5936fc1ecdba53/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bayshier/MVPKotlin_Commonlib/d9aa24e8f99520d99ec5d3cacd5936fc1ecdba53/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bayshier/MVPKotlin_Commonlib/d9aa24e8f99520d99ec5d3cacd5936fc1ecdba53/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bayshier/MVPKotlin_Commonlib/d9aa24e8f99520d99ec5d3cacd5936fc1ecdba53/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bayshier/MVPKotlin_Commonlib/d9aa24e8f99520d99ec5d3cacd5936fc1ecdba53/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bayshier/MVPKotlin_Commonlib/d9aa24e8f99520d99ec5d3cacd5936fc1ecdba53/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bayshier/MVPKotlin_Commonlib/d9aa24e8f99520d99ec5d3cacd5936fc1ecdba53/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bayshier/MVPKotlin_Commonlib/d9aa24e8f99520d99ec5d3cacd5936fc1ecdba53/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/raw/default_coin.json:
--------------------------------------------------------------------------------
1 | {
2 | "status": "true",
3 | "code": "0",
4 | "msg": "",
5 | "time": 1517537524,
6 | "plat": [
7 | {
8 | "code": "huobipro",
9 | "IsSelect": true,
10 | "title": "HuobiPro"
11 | },
12 | {
13 | "code": "binance",
14 | "IsSelect": true,
15 | "title": "币安"
16 | },
17 | {
18 | "code": "okex",
19 | "IsSelect": true,
20 | "title": "OKex"
21 | },
22 | {
23 | "code": "upbit",
24 | "IsSelect": true,
25 | "title": "Upbit"
26 | },
27 | {
28 | "code": "bitfinex",
29 | "IsSelect": true,
30 | "title": "Bitfinex"
31 | },
32 | {
33 | "code": "bittrex",
34 | "IsSelect": true,
35 | "title": "Bittrex"
36 | },
37 | {
38 | "code": "poloniex",
39 | "IsSelect": true,
40 | "title": "Poloniex"
41 | },
42 | {
43 | "code": "bithumb",
44 | "IsSelect": true,
45 | "title": "Bithumb"
46 | },
47 | {
48 | "code": "zb",
49 | "IsSelect": false,
50 | "title": "ZB中币"
51 | },
52 | {
53 | "code": "kucoin",
54 | "IsSelect": true,
55 | "title": "KuCoin"
56 | },
57 | {
58 | "code": "hitbtc",
59 | "IsSelect": true,
60 | "title": "Hitbtc"
61 | },
62 | {
63 | "code": "gdax",
64 | "IsSelect": false,
65 | "title": "Gdax"
66 | },
67 | {
68 | "code": "gate-io",
69 | "IsSelect": false,
70 | "title": "Gate-IO"
71 | },
72 | {
73 | "code": "coinw",
74 | "IsSelect": false,
75 | "title": "Coinw"
76 | },
77 | {
78 | "code": "bibox",
79 | "IsSelect": true,
80 | "title": "Bibox"
81 | },
82 | {
83 | "code": "kraken",
84 | "IsSelect": true,
85 | "title": "Kraken"
86 | },
87 | {
88 | "code": "yobit",
89 | "IsSelect": false,
90 | "title": "Yobit"
91 | },
92 | {
93 | "code": "bit-z",
94 | "IsSelect": false,
95 | "title": "Bit-z"
96 | },
97 | {
98 | "code": "liqui",
99 | "IsSelect": false,
100 | "title": "Liqui"
101 | },
102 | {
103 | "code": "etherdelta",
104 | "IsSelect": false,
105 | "title": "Etherdelta"
106 | }
107 | ],
108 | "coin": [
109 | {
110 | "code": "bitcoin",
111 | "IsSelect": true,
112 | "title": "BTC"
113 | },
114 | {
115 | "code": "ethereum",
116 | "IsSelect": true,
117 | "title": "ETH"
118 | },
119 | {
120 | "code": "eos",
121 | "IsSelect": true,
122 | "title": "EOS"
123 | },
124 | {
125 | "code": "bqt",
126 | "IsSelect": true,
127 | "title": "BQT"
128 | },
129 | {
130 | "code": "ripple",
131 | "IsSelect": true,
132 | "title": "XRP"
133 | },
134 | {
135 | "code": "bitcoin-cash",
136 | "IsSelect": true,
137 | "title": "BCH"
138 | }
139 | ]
140 | }
--------------------------------------------------------------------------------
/app/src/main/res/raw/default_coin_en.json:
--------------------------------------------------------------------------------
1 | {
2 | "status": "true",
3 | "code": "0",
4 | "msg": "",
5 | "time":1517537524,
6 | "plat": [
7 | {
8 | "code": "huobipro",
9 | "IsSelect": true,
10 | "title": "HuobiPro"
11 | },
12 | {
13 | "code": "binance",
14 | "IsSelect": true,
15 | "title": "Binance"
16 | },
17 | {
18 | "code": "okex",
19 | "IsSelect": true,
20 | "title": "OKex"
21 | },
22 | {
23 | "code": "upbit",
24 | "IsSelect": true,
25 | "title": "Upbit"
26 | },
27 | {
28 | "code": "bitfinex",
29 | "IsSelect": true,
30 | "title": "Bitfinex"
31 | },
32 | {
33 | "code": "bittrex",
34 | "IsSelect": true,
35 | "title": "Bittrex"
36 | },
37 | {
38 | "code": "poloniex",
39 | "IsSelect": true,
40 | "title": "Poloniex"
41 | },
42 | {
43 | "code": "bithumb",
44 | "IsSelect": true,
45 | "title": "Bithumb"
46 | },
47 | {
48 | "code": "zb",
49 | "IsSelect": false,
50 | "title": "ZB-Com"
51 | },
52 | {
53 | "code": "kucoin",
54 | "IsSelect": true,
55 | "title": "KuCoin"
56 | },
57 | {
58 | "code": "hitbtc",
59 | "IsSelect": true,
60 | "title": "Hitbtc"
61 | },
62 | {
63 | "code": "gdax",
64 | "IsSelect": false,
65 | "title": "Gdax"
66 | },
67 | {
68 | "code": "gate-io",
69 | "IsSelect": false,
70 | "title": "Gate-IO"
71 | },
72 | {
73 | "code": "coinw",
74 | "IsSelect": false,
75 | "title": "Coinw"
76 | },
77 | {
78 | "code": "bibox",
79 | "IsSelect": true,
80 | "title": "Bibox"
81 | },
82 | {
83 | "code": "kraken",
84 | "IsSelect": true,
85 | "title": "Kraken"
86 | },
87 | {
88 | "code": "yobit",
89 | "IsSelect": false,
90 | "title": "Yobit"
91 | },
92 | {
93 | "code": "bit-z",
94 | "IsSelect": false,
95 | "title": "Bit-z"
96 | },
97 | {
98 | "code": "liqui",
99 | "IsSelect": false,
100 | "title": "Liqui"
101 | },
102 | {
103 | "code": "etherdelta",
104 | "IsSelect": false,
105 | "title": "Etherdelta"
106 | }
107 | ],
108 | "coin": [
109 | {
110 | "code": "bitcoin",
111 | "IsSelect": true,
112 | "title": "BTC"
113 | },
114 | {
115 | "code": "ethereum",
116 | "IsSelect": true,
117 | "title": "ETH"
118 | },
119 | {
120 | "code": "ripple",
121 | "IsSelect": true,
122 | "title": "XRP"
123 | },
124 | {
125 | "code": "bitcoin-cash",
126 | "IsSelect": true,
127 | "title": "BCH"
128 | },
129 | {
130 | "code": "litecoin",
131 | "IsSelect": false,
132 | "title": "LTC"
133 | }
134 | ]
135 | }
--------------------------------------------------------------------------------
/app/src/main/res/raw/default_coin_tw.json:
--------------------------------------------------------------------------------
1 | {
2 | "status": "true",
3 | "code": "0",
4 | "msg": "",
5 | "time":1517537524,
6 | "plat": [
7 | {
8 | "code": "huobipro",
9 | "IsSelect": true,
10 | "title": "HuobiPro"
11 | },
12 | {
13 | "code": "binance",
14 | "IsSelect": true,
15 | "title": "幣安"
16 | },
17 | {
18 | "code": "okex",
19 | "IsSelect": true,
20 | "title": "OKex"
21 | },
22 | {
23 | "code": "upbit",
24 | "IsSelect": true,
25 | "title": "Upbit"
26 | },
27 | {
28 | "code": "bitfinex",
29 | "IsSelect": true,
30 | "title": "Bitfinex"
31 | },
32 | {
33 | "code": "bittrex",
34 | "IsSelect": true,
35 | "title": "Bittrex"
36 | },
37 | {
38 | "code": "poloniex",
39 | "IsSelect": true,
40 | "title": "Poloniex"
41 | },
42 | {
43 | "code": "bithumb",
44 | "IsSelect": true,
45 | "title": "Bithumb"
46 | },
47 | {
48 | "code": "zb",
49 | "IsSelect": false,
50 | "title": "ZB中幣"
51 | },
52 | {
53 | "code": "kucoin",
54 | "IsSelect": true,
55 | "title": "KuCoin"
56 | },
57 | {
58 | "code": "hitbtc",
59 | "IsSelect": true,
60 | "title": "Hitbtc"
61 | },
62 | {
63 | "code": "gdax",
64 | "IsSelect": false,
65 | "title": "Gdax"
66 | },
67 | {
68 | "code": "gate-io",
69 | "IsSelect": false,
70 | "title": "Gate-IO"
71 | },
72 | {
73 | "code": "coinw",
74 | "IsSelect": false,
75 | "title": "Coinw"
76 | },
77 | {
78 | "code": "bibox",
79 | "IsSelect": true,
80 | "title": "Bibox"
81 | },
82 | {
83 | "code": "kraken",
84 | "IsSelect": true,
85 | "title": "Kraken"
86 | },
87 | {
88 | "code": "yobit",
89 | "IsSelect": false,
90 | "title": "Yobit"
91 | },
92 | {
93 | "code": "bit-z",
94 | "IsSelect": false,
95 | "title": "Bit-z"
96 | },
97 | {
98 | "code": "liqui",
99 | "IsSelect": false,
100 | "title": "Liqui"
101 | },
102 | {
103 | "code": "etherdelta",
104 | "IsSelect": false,
105 | "title": "Etherdelta"
106 | }
107 | ],
108 | "coin": [
109 | {
110 | "code": "bitcoin",
111 | "IsSelect": true,
112 | "title": "BTC"
113 | },
114 | {
115 | "code": "ethereum",
116 | "IsSelect": true,
117 | "title": "ETH"
118 | },
119 | {
120 | "code": "ripple",
121 | "IsSelect": true,
122 | "title": "XRP"
123 | },
124 | {
125 | "code": "bitcoin-cash",
126 | "IsSelect": true,
127 | "title": "BCH"
128 | },
129 | {
130 | "code": "litecoin",
131 | "IsSelect": false,
132 | "title": "LTC"
133 | }
134 | ]
135 | }
--------------------------------------------------------------------------------
/app/src/main/res/raw/hexie:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bayshier/MVPKotlin_Commonlib/d9aa24e8f99520d99ec5d3cacd5936fc1ecdba53/app/src/main/res/raw/hexie
--------------------------------------------------------------------------------
/app/src/main/res/raw/huilv.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "symbol": "aed",
4 | "rate": 3.679717397703856
5 | },
6 | {
7 | "symbol": "ars",
8 | "rate": 38.37298541826554
9 | },
10 | {
11 | "symbol": "aud",
12 | "rate": 1.3880984439416444
13 | },
14 | {
15 | "symbol": "bch",
16 | "rate": 0.009135712468608025
17 | },
18 | {
19 | "symbol": "bgn",
20 | "rate": 1.7133556069562237
21 | },
22 | {
23 | "symbol": "bob",
24 | "rate": 6.8601221101735606
25 | },
26 | {
27 | "symbol": "brl",
28 | "rate": 3.9261876717707107
29 | },
30 | {
31 | "symbol": "btc",
32 | "rate": 0.00029046145967623593
33 | },
34 | {
35 | "symbol": "cad",
36 | "rate": 1.341021858656296
37 | },
38 | {
39 | "symbol": "chf",
40 | "rate": 0.989021857383048
41 | },
42 | {
43 | "symbol": "clp",
44 | "rate": 680.2721088435375
45 | },
46 | {
47 | "symbol": "cny",
48 | "rate": 6.925207756232687
49 | },
50 | {
51 | "symbol": "cop",
52 | "rate": 3192.3383878691143
53 | },
54 | {
55 | "symbol": "czk",
56 | "rate": 22.779562176814963
57 | },
58 | {
59 | "symbol": "dkk",
60 | "rate": 6.565988181221274
61 | },
62 | {
63 | "symbol": "eth",
64 | "rate": 0.011018868626010269
65 | },
66 | {
67 | "symbol": "eur",
68 | "rate": 0.8796312585764047
69 | },
70 | {
71 | "symbol": "gbp",
72 | "rate": 0.7948936034911728
73 | },
74 | {
75 | "symbol": "hkd",
76 | "rate": 7.817385866166354
77 | },
78 | {
79 | "symbol": "hrk",
80 | "rate": 6.491820306413918
81 | },
82 | {
83 | "symbol": "huf",
84 | "rate": 286.77946659019216
85 | },
86 | {
87 | "symbol": "idr",
88 | "rate": 14692.918013517485
89 | },
90 | {
91 | "symbol": "ils",
92 | "rate": 3.751078435050077
93 | },
94 | {
95 | "symbol": "inr",
96 | "rate": 72.25433526011561
97 | },
98 | {
99 | "symbol": "isk",
100 | "rate": 122.39902080783354
101 | },
102 | {
103 | "symbol": "jpy",
104 | "rate": 113.08379509216329
105 | },
106 | {
107 | "symbol": "krw",
108 | "rate": 1135.0737797956867
109 | },
110 | {
111 | "symbol": "ltc",
112 | "rate": 0.04077666031030164
113 | },
114 | {
115 | "symbol": "mur",
116 | "rate": 34.91132523390588
117 | },
118 | {
119 | "symbol": "mxn",
120 | "rate": 20.32520325203252
121 | },
122 | {
123 | "symbol": "myr",
124 | "rate": 4.189359028068705
125 | },
126 | {
127 | "symbol": "ngn",
128 | "rate": 370.7823507601038
129 | },
130 | {
131 | "symbol": "nok",
132 | "rate": 8.55431993156544
133 | },
134 | {
135 | "symbol": "nzd",
136 | "rate": 1.4534883720930234
137 | },
138 | {
139 | "symbol": "pen",
140 | "rate": 3.363492650768558
141 | },
142 | {
143 | "symbol": "php",
144 | "rate": 53.022269353128316
145 | },
146 | {
147 | "symbol": "pkr",
148 | "rate": 138.58093126385808
149 | },
150 | {
151 | "symbol": "pln",
152 | "rate": 3.7852979029449614
153 | },
154 | {
155 | "symbol": "ron",
156 | "rate": 4.101218061764344
157 | },
158 | {
159 | "symbol": "rub",
160 | "rate": 66.8985817500669
161 | },
162 | {
163 | "symbol": "sek",
164 | "rate": 9.124087591240876
165 | },
166 | {
167 | "symbol": "sgd",
168 | "rate": 1.3738150844896277
169 | },
170 | {
171 | "symbol": "thb",
172 | "rate": 32.89473684210526
173 | },
174 | {
175 | "symbol": "try",
176 | "rate": 5.3415950002670805
177 | },
178 | {
179 | "symbol": "twd",
180 | "rate": 30.8641975308642
181 | },
182 | {
183 | "symbol": "uah",
184 | "rate": 27.693159789531983
185 | },
186 | {
187 | "symbol": "vnd",
188 | "rate": 23332.32226603514
189 | },
190 | {
191 | "symbol": "zar",
192 | "rate": 14.471780028943561
193 | }
194 | ]
--------------------------------------------------------------------------------
/app/src/main/res/raw/huilv_count.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "img": "ic_zhongguo.webp",
4 | "code": "CNY"
5 | },
6 | {
7 | "img": "ic_meiguo.webp",
8 | "code": "USD"
9 | },
10 | {
11 | "img": "bitcoin.webp",
12 | "code": "BTC"
13 | },
14 | {
15 | "img": "ethereum.webp",
16 | "code": "ETH"
17 | },
18 | {
19 | "img": "ic_riben.webp",
20 | "code": "JPY"
21 | },
22 | {
23 | "img": "ic_hanguo.webp",
24 | "code": "KRW"
25 | },
26 | {
27 | "img": "ic_xianggang.webp",
28 | "code": "HKD"
29 | },
30 | {
31 | "img": "ic_taiwan.webp",
32 | "code": "TWD"
33 | },
34 | {
35 | "img": "ic_eluosi.webp",
36 | "code": "RUB"
37 | },
38 |
39 | {
40 | "img": "ic_yindu.webp",
41 | "code": "INR"
42 | },
43 |
44 |
45 | {
46 | "img": "ic_aodaliya.webp",
47 | "code": "AUD"
48 | },
49 | {
50 | "img": "eu.webp",
51 | "code": "EUR"
52 | },
53 | {
54 | "img": "fr.webp",
55 | "code": "CHF"
56 | },
57 | {
58 | "img": "ic_yingguo.webp",
59 | "code": "GBP"
60 | },
61 | {
62 | "img": "ic_jianada.webp",
63 | "code": "CAD"
64 | },
65 | {
66 | "img": "id.webp",
67 | "code": "IDR"
68 | },
69 | {
70 | "img": "ic_baxi.webp",
71 | "code": "BRL"
72 | },
73 | {
74 | "img": "ic_moxige.webp",
75 | "code": "MXN"
76 | }
77 | ]
--------------------------------------------------------------------------------
/app/src/main/res/raw/plat.json:
--------------------------------------------------------------------------------
1 | {"coinsbank": "coinsbank.webp",
2 | "poloniex": "poloniex.webp",
3 | "bitmex": "bitmex.webp",
4 | "liqui": "liqui.webp",
5 | "bithumb": "bithumb.webp",
6 | "bitflyer": "bitflyer.webp",
7 | "chaoex": "chaoex.webp",
8 | "okcoin": "okcoin.webp",
9 | "btcbox": "btcbox.webp",
10 | "acx": "acx.webp",
11 | "allcoin": "allcoin.webp",
12 | "bcex": "bcex.webp",
13 | "btcc": "btcc.webp",
14 | "coinx": "coinx.webp",
15 | "exx": "exx.webp",
16 | "coolcoin": "coolcoin.webp",
17 | "zb": "zb.webp",
18 | "hitbtc": "hitbtc.webp",
19 | "gemini": "gemini.webp",
20 | "kraken": "kraken.webp",
21 | "hashtoken": "hashtoken.webp",
22 | "bitstar": "bitstar.webp",
23 | "lbank": "lbank.webp",
24 | "upbit": "upbit.webp",
25 | "huobi": "huobi.webp",
26 | "coinegg": "coinegg.webp",
27 | "okex": "okex.webp",
28 | "zaif": "zaif.webp",
29 | "coinnest": "coinnest.webp",
30 | "bitstamp": "bitstamp.webp",
31 | "coinexchange": "coinexchange.webp",
32 | "uncoinex": "uncoinex.webp",
33 | "ceo": "ceo.webp",
34 | "quoine": "quoine.webp",
35 | "kucoin": "kucoin.webp",
36 | "bter": "bter.webp",
37 | "chinawkb": "chinawkb.webp",
38 | "aex": "aex.webp",
39 | "bigone": "bigone.webp",
40 | "bittrex": "bittrex.webp",
41 | "cex": "cex.webp",
42 | "korbit": "korbit.webp",
43 | "lakebtc": "lakebtc.webp",
44 | "gatecoin": "gatecoin.webp",
45 | "coinone": "coinone.webp",
46 | "binance": "binance.webp",
47 | "bit-z": "bit_z.webp",
48 | "kex": "kex.webp",
49 | "gdax": "gdax.webp",
50 | "c-cex": "c_cex.webp",
51 | "dragonex": "dragonex.webp",
52 | "yobit": "yobit.webp",
53 | "wex": "wex.webp",
54 | "worldcryptocap": "worldcryptocap.webp",
55 | "btc38": "btc38.webp",
56 | "lykke": "lykke.webp",
57 | "bitfinex": "bitfinex.webp",
58 | "qbtc": "qbtc.webp",
59 | "coincheck": "coincheck.webp",
60 | "shuzibi": "shuzibi.webp",
61 | "livecoin": "livecoin.webp",
62 | "gate": "gate.webp",
63 | "mercatox": "mercatox.webp",
64 | "itbit": "itbit.webp"
65 | }
--------------------------------------------------------------------------------
/app/src/main/res/raw/price.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "人民币",
4 | "code": "CNY"
5 | },
6 | {
7 | "name": "美元",
8 | "code": "USD"
9 | },
10 | {
11 | "name": "比特币",
12 | "code": "BTC"
13 | },
14 | {
15 | "name": "以太坊",
16 | "code": "ETH"
17 | },
18 | {
19 | "name": "日元",
20 | "code": "JPY"
21 | },
22 | {
23 | "name": "韩币",
24 | "code": "KRW"
25 | },
26 | {
27 | "name": "港币",
28 | "code": "HKD"
29 | },
30 | {
31 | "name": "新台币",
32 | "code": "TWD"
33 | },
34 | {
35 | "name": "卢布",
36 | "code": "RUB"
37 | },
38 |
39 | {
40 | "name": "卢比",
41 | "code": "INR"
42 | },
43 |
44 |
45 | {
46 | "name": "澳元",
47 | "code": "AUD"
48 | },
49 | {
50 | "name": "欧元",
51 | "code": "EUR"
52 | },
53 | {
54 | "name": "法郎",
55 | "code": "CHF"
56 | },
57 | {
58 | "name": "英镑",
59 | "code": "GBP"
60 | },
61 | {
62 | "name": "加元",
63 | "code": "CAD"
64 | },
65 | {
66 | "name": "印尼盾",
67 | "code": "IDR"
68 | },
69 | {
70 | "name": "巴西雷亚尔",
71 | "code": "BRL"
72 | },
73 | {
74 | "name": "墨西哥比索",
75 | "code": "MXN"
76 | }
77 | ]
--------------------------------------------------------------------------------
/app/src/main/res/raw/price_en.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "",
4 | "code": "CNY"
5 | },
6 | {
7 | "name": "",
8 | "code": "USD"
9 | },
10 | {
11 | "name": "",
12 | "code": "BTC"
13 | },
14 | {
15 | "name": "",
16 | "code": "ETH"
17 | },
18 | {
19 | "name": "",
20 | "code": "JPY"
21 | },
22 | {
23 | "name": "",
24 | "code": "KRW"
25 | },
26 | {
27 | "name": "",
28 | "code": "HKD"
29 | },
30 | {
31 | "name": "",
32 | "code": "TWD"
33 | },
34 | {
35 | "name": "",
36 | "code": "RUB"
37 | },
38 | {
39 | "name": "",
40 | "code": "INR"
41 | },
42 | {
43 | "name": "",
44 | "code": "AUD"
45 | },
46 | {
47 | "name": "",
48 | "code": "EUR"
49 | },
50 | {
51 | "name": "",
52 | "code": "CHF"
53 | },
54 | {
55 | "name": "",
56 | "code": "GBP"
57 | },
58 | {
59 | "name": "",
60 | "code": "CAD"
61 | },
62 | {
63 | "name": "",
64 | "code": "IDR"
65 | },
66 | {
67 | "name": "",
68 | "code": "BRL"
69 | },
70 | {
71 | "name": "",
72 | "code": "MXN"
73 | }
74 | ]
--------------------------------------------------------------------------------
/app/src/main/res/raw/price_hk.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "人民幣",
4 | "code": "CNY"
5 | },
6 | {
7 | "name": "美元",
8 | "code": "USD"
9 | },
10 | {
11 | "name": "比特幣",
12 | "code": "BTC"
13 | },
14 | {
15 | "name": "以太坊",
16 | "code": "ETH"
17 | },
18 | {
19 | "name": "日元",
20 | "code": "JPY"
21 | },
22 | {
23 | "name": "韓元",
24 | "code": "KRW"
25 | },
26 | {
27 | "name": "港元",
28 | "code": "HKD"
29 | },
30 | {
31 | "name": "新台幣",
32 | "code": "TWD"
33 | },
34 | {
35 | "name": "盧布",
36 | "code": "RUB"
37 | },
38 | {
39 | "name": "盧比",
40 | "code": "INR"
41 | },
42 | {
43 | "name": "澳元",
44 | "code": "AUD"
45 | },
46 | {
47 | "name": "歐元",
48 | "code": "EUR"
49 | },
50 | {
51 | "name": "法郎",
52 | "code": "CHF"
53 | },
54 | {
55 | "name": "英鎊",
56 | "code": "GBP"
57 | },
58 | {
59 | "name": "加元",
60 | "code": "CAD"
61 | },
62 | {
63 | "name": "印尼盾",
64 | "code": "IDR"
65 | },
66 | {
67 | "name": "巴西雷亞爾",
68 | "code": "BRL"
69 | },
70 | {
71 | "name": "墨西哥披索",
72 | "code": "MXN"
73 | }
74 | ]
--------------------------------------------------------------------------------
/app/src/main/res/raw/price_tw.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "人民幣",
4 | "code": "CNY"
5 | },
6 | {
7 | "name": "美元",
8 | "code": "USD"
9 | },
10 | {
11 | "name": "比特幣",
12 | "code": "BTC"
13 | },
14 | {
15 | "name": "以太坊",
16 | "code": "ETH"
17 | },
18 | {
19 | "name": "日元",
20 | "code": "JPY"
21 | },
22 | {
23 | "name": "韓元",
24 | "code": "KRW"
25 | },
26 | {
27 | "name": "港元",
28 | "code": "HKD"
29 | },
30 | {
31 | "name": "新台幣",
32 | "code": "TWD"
33 | },
34 | {
35 | "name": "盧布",
36 | "code": "RUB"
37 | },
38 | {
39 | "name": "盧比",
40 | "code": "INR"
41 | },
42 | {
43 | "name": "澳元",
44 | "code": "AUD"
45 | },
46 | {
47 | "name": "歐元",
48 | "code": "EUR"
49 | },
50 | {
51 | "name": "法郎",
52 | "code": "CHF"
53 | },
54 | {
55 | "name": "英鎊",
56 | "code": "GBP"
57 | },
58 | {
59 | "name": "加元",
60 | "code": "CAD"
61 | },
62 | {
63 | "name": "印尼盾",
64 | "code": "IDR"
65 | },
66 | {
67 | "name": "巴西雷亞爾",
68 | "code": "BRL"
69 | },
70 | {
71 | "name": "墨西哥披索",
72 | "code": "MXN"
73 | }
74 | ]
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | MVPKotlin_Commonlib
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/lanyixin/myapplication/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.lanyixin.myapplication
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | apply from: "config.gradle"
3 | buildscript {
4 | ext.kotlin_version = '1.3.11'
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.2.1'
11 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
12 |
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | jcenter()
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/common_lib/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/common_lib/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | apply plugin: 'kotlin-android'
4 |
5 | apply plugin: 'kotlin-android-extensions' //扩展插件
6 |
7 | apply plugin: 'kotlin-kapt' //kapt3插件
8 |
9 | android {
10 |
11 | compileSdkVersion rootProject.ext.android.compileSdkVersion
12 | buildToolsVersion '28.0.3'
13 |
14 | defaultConfig {
15 | minSdkVersion rootProject.ext.android.minSdkVersion
16 | targetSdkVersion rootProject.ext.android.targetSdkVersion
17 | versionCode rootProject.ext.android.versionCode
18 | versionName rootProject.ext.android.versionName
19 | }
20 |
21 | buildTypes {
22 | release {
23 | minifyEnabled false
24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
25 | }
26 | }
27 | }
28 |
29 | dependencies {
30 | implementation fileTree(include: ['*.jar'], dir: 'libs')
31 | testImplementation 'junit:junit:4.12'
32 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
33 | androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.1') {
34 | exclude group: 'com.android.support', module: 'support-annotations'
35 | }
36 | // Support库
37 | api rootProject.ext.supportLibs
38 | // 网络请求库
39 | api rootProject.ext.networkLibs
40 | // RxJava2
41 | api rootProject.ext.rxJavaLibs
42 | api rootProject.ext.otherLibs
43 | // APT dependencies(Kotlin内置的注解处理器)
44 | kapt rootProject.ext.annotationProcessorLibs
45 |
46 | //kotlin 支持库
47 | api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
48 | //GlideOkHttp
49 | api(rootProject.ext.glideOkhttp) {
50 | exclude group: 'glide-parent'
51 | }
52 | //Logger
53 | api 'com.orhanobut:logger:2.1.1'
54 | //运行时权限
55 | api'pub.devrel:easypermissions:1.2.0'
56 |
57 | api project(':multiple-status-view')
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/common_lib/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 |
23 | #Glide的混淆规则
24 | -keep public class * implements com.bumptech.glide.module.GlideModule
25 | -keep public class * extends com.bumptech.glide.AppGlideModule
26 | -keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
27 | **[] $VALUES;
28 | public *;
29 | }
30 |
31 | # Platform calls Class.forName on types which do not exist on Android to determine platform.
32 | -dontnote retrofit2.Platform
33 | # Platform used when running on Java 8 VMs. Will not be used at runtime.
34 | -dontwarn retrofit2.Platform$Java8
35 | # Retain generic type information for use by reflection by converters and adapters.
36 | -keepattributes Signature
37 | # Retain declared checked exceptions for use by a Proxy instance.
38 | -keepattributes Exceptions
39 | -dontwarn org.xmlpull.v1.**
40 | -dontwarn okhttp3.**
41 | -keep class okhttp3.** { *; }
42 | -dontwarn okio.**
43 | -dontwarn javax.annotation.Nullable
44 | -dontwarn javax.annotation.ParametersAreNonnullByDefault
45 |
--------------------------------------------------------------------------------
/common_lib/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp
2 |
3 | import android.content.Context
4 | import android.support.v4.app.Fragment
5 | import android.view.View
6 | import android.widget.ImageView
7 | import android.widget.Toast
8 |
9 | /**
10 | */
11 |
12 | fun Fragment.showToast(content: String): Toast {
13 | val toast = Toast.makeText(this.activity?.applicationContext, content, Toast.LENGTH_SHORT)
14 | toast.show()
15 | return toast
16 | }
17 |
18 | fun Context.showToast(content: String,context: Context): Toast {
19 | val toast = Toast.makeText(context, content, Toast.LENGTH_SHORT)
20 | toast.show()
21 | return toast
22 | }
23 |
24 |
25 | fun View.dip2px(dipValue: Float): Int {
26 | val scale = this.resources.displayMetrics.density
27 | return (dipValue * scale + 0.5f).toInt()
28 | }
29 |
30 | fun View.px2dip(pxValue: Float): Int {
31 | val scale = this.resources.displayMetrics.density
32 | return (pxValue / scale + 0.5f).toInt()
33 | }
34 |
35 | fun durationFormat(duration: Long?): String {
36 | val minute = duration!! / 60
37 | val second = duration % 60
38 | return if (minute <= 9) {
39 | if (second <= 9) {
40 | "0$minute' 0$second''"
41 | } else {
42 | "0$minute' $second''"
43 | }
44 | } else {
45 | if (second <= 9) {
46 | "$minute' 0$second''"
47 | } else {
48 | "$minute' $second''"
49 | }
50 | }
51 | }
52 |
53 | /**
54 | * 数据流量格式化
55 | */
56 | fun Context.dataFormat(total: Long): String {
57 | var result: String
58 | var speedReal: Int = (total / (1024)).toInt()
59 | result = if (speedReal < 512) {
60 | speedReal.toString() + " KB"
61 | } else {
62 | val mSpeed = speedReal / 1024.0
63 | (Math.round(mSpeed * 100) / 100.0).toString() + " MB"
64 | }
65 | return result
66 | }
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/base/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.base
2 |
3 | import android.content.Context
4 | import android.os.Bundle
5 | import android.support.v7.app.AppCompatActivity
6 | import android.util.Log
7 | import android.view.View
8 | import android.view.inputmethod.InputMethodManager
9 | import android.widget.EditText
10 | import android.widget.Toast
11 | import com.classic.common.MultipleStatusView
12 | import io.reactivex.annotations.NonNull
13 | import pub.devrel.easypermissions.AppSettingsDialog
14 | import pub.devrel.easypermissions.EasyPermissions
15 |
16 |
17 |
18 | abstract class BaseActivity : AppCompatActivity(),EasyPermissions.PermissionCallbacks {
19 | /**
20 | * 多种状态的 View 的切换
21 | */
22 | protected var mLayoutStatusView: MultipleStatusView? = null
23 |
24 | override fun onCreate(savedInstanceState: Bundle?) {
25 | super.onCreate(savedInstanceState)
26 | setContentView(layoutId())
27 | initData()
28 | initView()
29 | start()
30 | initListener()
31 | }
32 |
33 | private fun initListener() {
34 | mLayoutStatusView?.setOnClickListener(mRetryClickListener)
35 | }
36 |
37 | open val mRetryClickListener: View.OnClickListener = View.OnClickListener {
38 | start()
39 | }
40 |
41 |
42 | /**
43 | * 加载布局
44 | */
45 | abstract fun layoutId(): Int
46 |
47 | /**
48 | * 初始化数据
49 | */
50 | abstract fun initData()
51 |
52 | /**
53 | * 初始化 View
54 | */
55 | abstract fun initView()
56 |
57 | /**
58 | * 开始请求
59 | */
60 | abstract fun start()
61 |
62 |
63 | /**
64 | * 打卡软键盘
65 | */
66 | fun openKeyBord(mEditText: EditText, mContext: Context) {
67 | val imm = mContext.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
68 | imm.showSoftInput(mEditText, InputMethodManager.RESULT_SHOWN)
69 | imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY)
70 | }
71 |
72 | /**
73 | * 关闭软键盘
74 | */
75 | fun closeKeyBord(mEditText: EditText, mContext: Context) {
76 | val imm = mContext.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
77 | imm.hideSoftInputFromWindow(mEditText.windowToken, 0)
78 | }
79 |
80 |
81 | override fun onDestroy() {
82 | super.onDestroy()
83 | }
84 |
85 |
86 | /**
87 | * 重写要申请权限的Activity或者Fragment的onRequestPermissionsResult()方法,
88 | * 在里面调用EasyPermissions.onRequestPermissionsResult(),实现回调。
89 | *
90 | * @param requestCode 权限请求的识别码
91 | * @param permissions 申请的权限
92 | * @param grantResults 授权结果
93 | */
94 | override fun onRequestPermissionsResult(requestCode: Int, @NonNull permissions: Array, @NonNull grantResults: IntArray) {
95 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
96 | EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this)
97 | }
98 |
99 | /**
100 | * 当权限被成功申请的时候执行回调
101 | *
102 | * @param requestCode 权限请求的识别码
103 | * @param perms 申请的权限的名字
104 | */
105 | override fun onPermissionsGranted(requestCode: Int, perms: List) {
106 | Log.i("EasyPermissions", "获取成功的权限$perms")
107 | }
108 |
109 | /**
110 | * 当权限申请失败的时候执行的回调
111 | *
112 | * @param requestCode 权限请求的识别码
113 | * @param perms 申请的权限的名字
114 | */
115 | override fun onPermissionsDenied(requestCode: Int, perms: List) {
116 | //处理权限名字字符串
117 | val sb = StringBuffer()
118 | for (str in perms) {
119 | sb.append(str)
120 | sb.append("\n")
121 | }
122 | sb.replace(sb.length - 2, sb.length, "")
123 | //用户点击拒绝并不在询问时候调用
124 | if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
125 | Toast.makeText(this, "已拒绝权限" + sb + "并不再询问", Toast.LENGTH_SHORT).show()
126 | AppSettingsDialog.Builder(this)
127 | .setRationale("此功能需要" + sb + "权限,否则无法正常使用,是否打开设置")
128 | .setPositiveButton("好")
129 | .setNegativeButton("不行")
130 | .build()
131 | .show()
132 | }
133 | }
134 |
135 | }
136 |
137 |
138 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/base/BaseFragment.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.base
2 |
3 | import android.os.Bundle
4 | import android.support.annotation.LayoutRes
5 | import android.support.v4.app.Fragment
6 | import android.util.Log
7 | import android.view.LayoutInflater
8 | import android.view.View
9 | import android.view.ViewGroup
10 | import android.widget.Toast
11 | import com.classic.common.MultipleStatusView
12 | import io.reactivex.annotations.NonNull
13 | import pub.devrel.easypermissions.AppSettingsDialog
14 | import pub.devrel.easypermissions.EasyPermissions
15 |
16 |
17 |
18 | abstract class BaseFragment: Fragment(),EasyPermissions.PermissionCallbacks{
19 |
20 | /**
21 | * 视图是否加载完毕
22 | */
23 | private var isViewPrepare = false
24 | /**
25 | * 数据是否加载过了
26 | */
27 | private var hasLoadData = false
28 | /**
29 | * 多种状态的 View 的切换
30 | */
31 | protected var mLayoutStatusView: MultipleStatusView? = null
32 |
33 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
34 | return inflater.inflate(getLayoutId(),null)
35 | }
36 |
37 |
38 |
39 | override fun setUserVisibleHint(isVisibleToUser: Boolean) {
40 | super.setUserVisibleHint(isVisibleToUser)
41 | if (isVisibleToUser) {
42 | lazyLoadDataIfPrepared()
43 | }
44 | }
45 |
46 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
47 | super.onViewCreated(view, savedInstanceState)
48 | isViewPrepare = true
49 | initView()
50 | lazyLoadDataIfPrepared()
51 | //多种状态切换的view 重试点击事件
52 | mLayoutStatusView?.setOnClickListener(mRetryClickListener)
53 | }
54 |
55 | private fun lazyLoadDataIfPrepared() {
56 | if (userVisibleHint && isViewPrepare && !hasLoadData) {
57 | lazyLoad()
58 | hasLoadData = true
59 | }
60 | }
61 |
62 | open val mRetryClickListener: View.OnClickListener = View.OnClickListener {
63 | lazyLoad()
64 | }
65 |
66 |
67 | /**
68 | * 加载布局
69 | */
70 | @LayoutRes
71 | abstract fun getLayoutId():Int
72 |
73 | /**
74 | * 初始化 ViewI
75 | */
76 | abstract fun initView()
77 |
78 | /**
79 | * 懒加载
80 | */
81 | abstract fun lazyLoad()
82 |
83 | override fun onDestroy() {
84 | super.onDestroy()
85 | }
86 |
87 |
88 | /**
89 | * 重写要申请权限的Activity或者Fragment的onRequestPermissionsResult()方法,
90 | * 在里面调用EasyPermissions.onRequestPermissionsResult(),实现回调。
91 | *
92 | * @param requestCode 权限请求的识别码
93 | * @param permissions 申请的权限
94 | * @param grantResults 授权结果
95 | */
96 | override fun onRequestPermissionsResult(requestCode: Int, @NonNull permissions: Array, @NonNull grantResults: IntArray) {
97 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
98 | EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this)
99 | }
100 |
101 | /**
102 | * 当权限被成功申请的时候执行回调
103 | *
104 | * @param requestCode 权限请求的识别码
105 | * @param perms 申请的权限的名字
106 | */
107 | override fun onPermissionsGranted(requestCode: Int, perms: List) {
108 | Log.i("EasyPermissions", "获取成功的权限$perms")
109 | }
110 |
111 | /**
112 | * 当权限申请失败的时候执行的回调
113 | *
114 | * @param requestCode 权限请求的识别码
115 | * @param perms 申请的权限的名字
116 | */
117 | override fun onPermissionsDenied(requestCode: Int, perms: List) {
118 | //处理权限名字字符串
119 | val sb = StringBuffer()
120 | for (str in perms) {
121 | sb.append(str)
122 | sb.append("\n")
123 | }
124 | sb.replace(sb.length - 2, sb.length, "")
125 | //用户点击拒绝并不在询问时候调用
126 | if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
127 | Toast.makeText(activity, "已拒绝权限" + sb + "并不再询问", Toast.LENGTH_SHORT).show()
128 | AppSettingsDialog.Builder(this)
129 | .setRationale("此功能需要" + sb + "权限,否则无法正常使用,是否打开设置")
130 | .setPositiveButton("好")
131 | .setNegativeButton("不行")
132 | .build()
133 | .show()
134 | }
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/base/BaseFragmentAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.base
2 |
3 | import android.annotation.SuppressLint
4 | import android.support.v4.app.Fragment
5 | import android.support.v4.app.FragmentManager
6 | import android.support.v4.app.FragmentPagerAdapter
7 |
8 |
9 | /**
10 | * 该类内的每一个生成的 Fragment 都将保存在内存之中,
11 | * 因此适用于那些相对静态的页,数量也比较少的那种;
12 | * 如果需要处理有很多页,并且数据动态性较大、占用内存较多的情况,
13 | * 应该使用FragmentStatePagerAdapter。
14 | */
15 | class BaseFragmentAdapter : FragmentPagerAdapter {
16 |
17 | private var fragmentList: List? = ArrayList()
18 | private var mTitles: List? = null
19 |
20 | constructor(fm: FragmentManager, fragmentList: List) : super(fm) {
21 | this.fragmentList = fragmentList
22 | }
23 |
24 | constructor(fm: FragmentManager, fragmentList: List, mTitles: List) : super(fm) {
25 | this.mTitles = mTitles
26 | setFragments(fm, fragmentList, mTitles)
27 | }
28 |
29 | //刷新fragment
30 | @SuppressLint("CommitTransaction")
31 | private fun setFragments(fm: FragmentManager, fragments: List, mTitles: List) {
32 | this.mTitles = mTitles
33 | if (this.fragmentList != null) {
34 | val ft = fm.beginTransaction()
35 | fragmentList?.forEach {
36 | ft.remove(it)
37 | }
38 | ft?.commitAllowingStateLoss()
39 | fm.executePendingTransactions()
40 | }
41 | this.fragmentList = fragments
42 | notifyDataSetChanged()
43 | }
44 |
45 | override fun getPageTitle(position: Int): CharSequence {
46 | return if (null != mTitles) mTitles!![position] else ""
47 | }
48 |
49 | override fun getItem(position: Int): Fragment {
50 | return fragmentList!![position]
51 | }
52 |
53 | override fun getCount(): Int {
54 | return fragmentList!!.size
55 | }
56 |
57 | }
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/base/BasePresenter.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.base
2 |
3 | import io.reactivex.disposables.CompositeDisposable
4 | import io.reactivex.disposables.Disposable
5 |
6 |
7 |
8 | /**
9 | * 11/16.
10 | *
11 | */
12 | open class BasePresenter : IPresenter {
13 |
14 | var mRootView: T? = null
15 | private set
16 |
17 | private var compositeDisposable = CompositeDisposable()
18 |
19 |
20 | override fun attachView(mRootView: T) {
21 | this.mRootView = mRootView
22 | }
23 |
24 | override fun detachView() {
25 | mRootView = null
26 |
27 | //保证activity结束时取消所有正在执行的订阅
28 | if (!compositeDisposable.isDisposed) {
29 | compositeDisposable.clear()
30 | }
31 |
32 | }
33 |
34 | private val isViewAttached: Boolean
35 | get() = mRootView != null
36 |
37 | fun checkViewAttached() {
38 | if (!isViewAttached) throw MvpViewNotAttachedException()
39 | }
40 |
41 | fun addSubscription(disposable: Disposable) {
42 | compositeDisposable.add(disposable)
43 | }
44 |
45 | private class MvpViewNotAttachedException internal constructor() : RuntimeException("Please call IPresenter.attachView(IBaseView) before" + " requesting data to the IPresenter")
46 |
47 |
48 | }
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/base/IBaseView.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.base
2 |
3 | interface IBaseView {
4 |
5 | fun showLoading()
6 |
7 | fun dismissLoading()
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/base/IPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.base
2 |
3 |
4 |
5 |
6 |
7 | interface IPresenter {
8 |
9 | fun attachView(mRootView: V)
10 |
11 | fun detachView()
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/glide/CustomAppGlideModule.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.glide
2 |
3 | import android.content.Context
4 |
5 | import com.bumptech.glide.Glide
6 | import com.bumptech.glide.GlideBuilder
7 | import com.bumptech.glide.Registry
8 | import com.bumptech.glide.annotation.GlideModule
9 | import com.bumptech.glide.load.engine.cache.LruResourceCache
10 | import com.bumptech.glide.module.AppGlideModule
11 |
12 | import java.io.InputStream
13 |
14 | /**
15 | * desc:
16 | */
17 | @GlideModule
18 | class CustomAppGlideModule : AppGlideModule() {
19 |
20 | /**
21 | * 通过GlideBuilder设置默认的结构(Engine,BitmapPool ,ArrayPool,MemoryCache等等).
22 | *
23 | * @param context
24 | * @param builder
25 | */
26 | override fun applyOptions(context: Context, builder: GlideBuilder) {
27 |
28 | //重新设置内存限制
29 | builder.setMemoryCache(LruResourceCache(10 * 1024 * 1024))
30 |
31 | }
32 |
33 |
34 | /**
35 | * 清单解析的开启
36 | *
37 | *
38 | * 这里不开启,避免添加相同的modules两次
39 | *
40 | * @return
41 | */
42 | override fun isManifestParsingEnabled(): Boolean {
43 | return false
44 | }
45 |
46 | /**
47 | *
48 | * 为App注册一个自定义的String类型的BaseGlideUrlLoader
49 | * @param context
50 | * @param glide
51 | * @param registry
52 | */
53 | override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
54 | registry.append(String::class.java, InputStream::class.java, CustomBaseGlideUrlLoader.Factory())
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/glide/CustomBaseGlideUrlLoader.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.glide
2 |
3 | import com.bumptech.glide.load.Options
4 | import com.bumptech.glide.load.model.*
5 | import com.bumptech.glide.load.model.stream.BaseGlideUrlLoader
6 | import java.io.InputStream
7 | import java.util.regex.Pattern
8 |
9 | /**
10 | * desc:
11 | */
12 |
13 | class CustomBaseGlideUrlLoader(concreteLoader: ModelLoader, modelCache: ModelCache) : BaseGlideUrlLoader(concreteLoader, modelCache) {
14 |
15 | /**
16 | * If the URL contains a special variable width indicator (eg "__w-200-400-800__")
17 | * we get the buckets from the URL (200, 400 and 800 in the example) and replace
18 | * the URL with the best bucket for the requested width (the bucket immediately
19 | * larger than the requested width).
20 | *
21 | * 控制加载的图片的大小
22 | */
23 | override fun getUrl(model: String, width: Int, height: Int, options: Options): String {
24 | val m = PATTERN.matcher(model)
25 | var bestBucket: Int
26 | if (m.find()) {
27 | val found = m.group(1).split("-".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
28 | for (bucketStr in found) {
29 | bestBucket = Integer.parseInt(bucketStr)
30 | if (bestBucket >= width) {
31 | // the best bucket is the first immediately bigger than the requested width
32 | break
33 | }
34 | }
35 |
36 | }
37 | return model
38 | }
39 |
40 | override fun handles(s: String): Boolean {
41 | return true
42 | }
43 |
44 | /**
45 | * 工厂来构建CustomBaseGlideUrlLoader对象
46 | */
47 | class Factory : ModelLoaderFactory {
48 | override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader {
49 | return CustomBaseGlideUrlLoader(multiFactory.build(GlideUrl::class.java, InputStream::class.java), urlCache)
50 | }
51 |
52 | override fun teardown() {
53 |
54 | }
55 | }
56 |
57 | companion object {
58 |
59 | private val urlCache = ModelCache(150)
60 | /**
61 | * Url的匹配规则
62 | */
63 | private val PATTERN = Pattern.compile("__w-((?:-?\\d+)+)__")
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/glide/GlideRoundTransform.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("DEPRECATION")
2 |
3 | package com.kotlinmvp.glide
4 |
5 | import android.content.res.Resources
6 | import android.graphics.Bitmap
7 | import android.graphics.BitmapShader
8 | import android.graphics.Canvas
9 | import android.graphics.Paint
10 | import android.graphics.RectF
11 | import android.graphics.Shader
12 |
13 | import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
14 | import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
15 |
16 | import java.security.MessageDigest
17 |
18 | /**
19 | * desc:
20 | * 1.永远不要把transform()传给你的原始resource或原始Bitmap给recycle()了,更不要放回BitmapPool,因为这些都自动完成了。值得注意的是,任何从BitmapPool取出的用于自定义图片变换的辅助Bitmap,如果不经过transform()方法返回,就必须主动放回BitmapPool或者调用recycle()回收。
21 | * 2.如果你从BitmapPool拿出多个Bitmap或不使用你从BitmapPool拿出的一个Bitmap,一定要返回extras给BitmapPool。
22 | * 3.如果你的图片处理没有替换原始resource(例如由于一张图片已经匹配了你想要的尺寸,你需要提前返回), transform()`方法就返回原始resource或原始Bitmap。
23 | */
24 |
25 | class GlideRoundTransform @JvmOverloads constructor(dp: Int = 4) : BitmapTransformation() {
26 |
27 | private var radius = 0f
28 |
29 | init {
30 | this.radius = Resources.getSystem().displayMetrics.density * dp
31 | }
32 |
33 | override fun transform(pool: BitmapPool, toTransform: Bitmap, outWidth: Int, outHeight: Int): Bitmap? {
34 | return roundCrop(pool, toTransform)
35 | }
36 |
37 |
38 | override fun updateDiskCacheKey(messageDigest: MessageDigest) {
39 |
40 | }
41 |
42 |
43 | private fun roundCrop(pool: BitmapPool, source: Bitmap?): Bitmap? {
44 | if (source == null) return null
45 |
46 | var result: Bitmap? = pool.get(source.width, source.height, Bitmap.Config.ARGB_8888)
47 | if (result == null) {
48 | result = Bitmap.createBitmap(source.width, source.height, Bitmap.Config.ARGB_8888)
49 | }
50 |
51 | val canvas = Canvas(result!!)
52 | val paint = Paint()
53 | paint.shader = BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
54 | paint.isAntiAlias = true
55 | val rectF = RectF(0f, 0f, source.width.toFloat(), source.height.toFloat())
56 | canvas.drawRoundRect(rectF, radius, radius, paint)
57 | return result
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/net/BaseResponse.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.net
2 |
3 | /**
4 | * 11/16.
5 | * 封装返回的数据
6 | */
7 | class BaseResponse(val code :Int,
8 | val msg:String,
9 | val data:T)
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/net/exception/ApiException.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.net.exception
2 |
3 | /**
4 | * desc:
5 | */
6 | class ApiException : RuntimeException {
7 |
8 | private var code: Int? = null
9 |
10 |
11 | constructor(throwable: Throwable, code: Int) : super(throwable) {
12 | this.code = code
13 | }
14 |
15 | constructor(message: String) : super(Throwable(message))
16 | }
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/net/exception/ErrorStatus.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.net.exception
2 |
3 | /**
4 | * desc:
5 | */
6 | object ErrorStatus {
7 | /**
8 | * 响应成功
9 | */
10 | @JvmField
11 | val SUCCESS = 0
12 |
13 | /**
14 | * 未知错误
15 | */
16 | @JvmField
17 | val UNKNOWN_ERROR = 1002
18 |
19 | /**
20 | * 服务器内部错误
21 | */
22 | @JvmField
23 | val SERVER_ERROR = 1003
24 |
25 | /**
26 | * 网络连接超时
27 | */
28 | @JvmField
29 | val NETWORK_ERROR = 1004
30 |
31 | /**
32 | * API解析异常(或者第三方数据结构更改)等其他异常
33 | */
34 | @JvmField
35 | val API_ERROR = 1005
36 |
37 | }
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/net/exception/ExceptionHandle.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.net.exception
2 |
3 | import com.google.gson.JsonParseException
4 | import com.orhanobut.logger.Logger
5 |
6 | import org.json.JSONException
7 |
8 | import java.net.ConnectException
9 |
10 | import java.net.SocketTimeoutException
11 | import java.net.UnknownHostException
12 | import java.text.ParseException
13 |
14 | /**
15 | * desc: 异常处理类
16 | */
17 |
18 | class ExceptionHandle {
19 |
20 |
21 | companion object {
22 | var errorCode = ErrorStatus.UNKNOWN_ERROR
23 | var errorMsg = "请求失败,请稍后重试"
24 |
25 | fun handleException(e: Throwable): String {
26 | e.printStackTrace()
27 | if (e is SocketTimeoutException) {//网络超时
28 | Logger.e("TAG", "网络连接异常: " + e.message)
29 | errorMsg = "网络连接异常"
30 | errorCode = ErrorStatus.NETWORK_ERROR
31 | } else if (e is ConnectException) { //均视为网络错误
32 | Logger.e("TAG", "网络连接异常: " + e.message)
33 | errorMsg = "网络连接异常"
34 | errorCode = ErrorStatus.NETWORK_ERROR
35 | } else if (e is JsonParseException
36 | || e is JSONException
37 | || e is ParseException) { //均视为解析错误
38 | Logger.e("TAG", "数据解析异常: " + e.message)
39 | errorMsg = "数据解析异常"
40 | errorCode = ErrorStatus.SERVER_ERROR
41 | } else if (e is ApiException) {//服务器返回的错误信息
42 | errorMsg = e.message.toString()
43 | errorCode = ErrorStatus.SERVER_ERROR
44 | } else if (e is UnknownHostException) {
45 | Logger.e("TAG", "网络连接异常: " + e.message)
46 | errorMsg = "网络连接异常"
47 | errorCode = ErrorStatus.NETWORK_ERROR
48 | } else if (e is IllegalArgumentException) {
49 | errorMsg = "参数错误"
50 | errorCode = ErrorStatus.SERVER_ERROR
51 | } else {//未知错误
52 | try {
53 | Logger.e("TAG", "错误: " + e.message)
54 | } catch (e1: Exception) {
55 | Logger.e("TAG", "未知错误Debug调试 ")
56 | }
57 |
58 | errorMsg = "未知错误,可能抛锚了吧~"
59 | errorCode = ErrorStatus.UNKNOWN_ERROR
60 | }
61 | return errorMsg
62 | }
63 |
64 | }
65 |
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/rx/scheduler/BaseScheduler.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.rx.scheduler
2 |
3 | import io.reactivex.*
4 | import org.reactivestreams.Publisher
5 |
6 | /**
7 | * .
8 | * desc:RxJava2.x 5中基础相应类型
9 | */
10 |
11 |
12 |
13 | abstract class BaseScheduler protected constructor(private val subscribeOnScheduler: Scheduler,
14 | private val observeOnScheduler: Scheduler) : ObservableTransformer,
15 | SingleTransformer,
16 | MaybeTransformer,
17 | CompletableTransformer,
18 | FlowableTransformer {
19 |
20 | override fun apply(upstream: Completable): CompletableSource {
21 | return upstream.subscribeOn(subscribeOnScheduler)
22 | .observeOn(observeOnScheduler)
23 | }
24 |
25 | override fun apply(upstream: Flowable): Publisher {
26 | return upstream.subscribeOn(subscribeOnScheduler)
27 | .observeOn(observeOnScheduler)
28 | }
29 |
30 | override fun apply(upstream: Maybe): MaybeSource {
31 | return upstream.subscribeOn(subscribeOnScheduler)
32 | .observeOn(observeOnScheduler)
33 | }
34 |
35 | override fun apply(upstream: Observable): ObservableSource {
36 | return upstream.subscribeOn(subscribeOnScheduler)
37 | .observeOn(observeOnScheduler)
38 | }
39 |
40 | override fun apply(upstream: Single): SingleSource {
41 | return upstream.subscribeOn(subscribeOnScheduler)
42 | .observeOn(observeOnScheduler)
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/rx/scheduler/ComputationMainScheduler.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.rx.scheduler
2 |
3 | import io.reactivex.android.schedulers.AndroidSchedulers
4 | import io.reactivex.schedulers.Schedulers
5 | /**
6 | * .
7 | * desc:
8 | */
9 |
10 |
11 | class ComputationMainScheduler private constructor() : BaseScheduler(Schedulers.computation(), AndroidSchedulers.mainThread())
12 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/rx/scheduler/IoMainScheduler.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.rx.scheduler
2 |
3 | import io.reactivex.android.schedulers.AndroidSchedulers
4 | import io.reactivex.schedulers.Schedulers
5 |
6 | /**
7 | * .
8 | * desc:
9 | */
10 | class IoMainScheduler : BaseScheduler(Schedulers.io(), AndroidSchedulers.mainThread())
11 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/rx/scheduler/NewThreadMainScheduler.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.rx.scheduler
2 |
3 | import io.reactivex.android.schedulers.AndroidSchedulers
4 | import io.reactivex.schedulers.Schedulers
5 |
6 | /**
7 | * .
8 | * desc:
9 | */
10 |
11 |
12 | class NewThreadMainScheduler private constructor() : BaseScheduler(Schedulers.newThread(), AndroidSchedulers.mainThread())
13 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/rx/scheduler/SchedulerUtils.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.rx.scheduler
2 |
3 | /**
4 | * .
5 | * desc:
6 | */
7 |
8 | object SchedulerUtils {
9 |
10 | fun ioToMain(): IoMainScheduler {
11 | return IoMainScheduler()
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/rx/scheduler/SingleMainScheduler.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.rx.scheduler
2 |
3 | import io.reactivex.android.schedulers.AndroidSchedulers
4 | import io.reactivex.schedulers.Schedulers
5 |
6 | /**
7 | * .
8 | * desc:
9 | */
10 |
11 |
12 | class SingleMainScheduler private constructor() : BaseScheduler(Schedulers.single(), AndroidSchedulers.mainThread())
13 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/rx/scheduler/TrampolineMainScheduler.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.rx.scheduler
2 |
3 | import io.reactivex.android.schedulers.AndroidSchedulers
4 | import io.reactivex.schedulers.Schedulers
5 |
6 | /**
7 | * .
8 | * desc:
9 | */
10 |
11 |
12 | class TrampolineMainScheduler private constructor() : BaseScheduler(Schedulers.trampoline(), AndroidSchedulers.mainThread())
13 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/utils/AppUtils.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.utils
2 |
3 | import android.annotation.SuppressLint
4 | import android.app.ActivityManager
5 | import android.content.Context
6 | import android.content.pm.PackageManager
7 | import android.os.Build
8 | import java.security.MessageDigest
9 |
10 | /**
11 | * 12/6.
12 | * desc: APP 相关的工具类
13 | */
14 |
15 | class AppUtils private constructor() {
16 |
17 |
18 | init {
19 | throw Error("Do not need instantiate!")
20 | }
21 |
22 | companion object {
23 |
24 | private val DEBUG = true
25 | private val TAG = "AppUtils"
26 |
27 |
28 | /**
29 | * 得到软件版本号
30 | *
31 | * @param context 上下文
32 | * @return 当前版本Code
33 | */
34 | fun getVerCode(context: Context): Int {
35 | var verCode = -1
36 | try {
37 | val packageName = context.packageName
38 | verCode = context.packageManager
39 | .getPackageInfo(packageName, 0).versionCode
40 | } catch (e: PackageManager.NameNotFoundException) {
41 | e.printStackTrace()
42 | }
43 |
44 | return verCode
45 | }
46 |
47 |
48 | /**
49 | * 获取应用运行的最大内存
50 | *
51 | * @return 最大内存
52 | */
53 | val maxMemory: Long
54 | get() = Runtime.getRuntime().maxMemory() / 1024
55 |
56 |
57 | /**
58 | * 得到软件显示版本信息
59 | *
60 | * @param context 上下文
61 | * @return 当前版本信息
62 | */
63 | fun getVerName(context: Context): String {
64 | var verName = ""
65 | try {
66 | val packageName = context.packageName
67 | verName = context.packageManager
68 | .getPackageInfo(packageName, 0).versionName
69 | } catch (e: PackageManager.NameNotFoundException) {
70 | e.printStackTrace()
71 | }
72 |
73 | return verName
74 | }
75 |
76 |
77 | @SuppressLint("PackageManagerGetSignatures")
78 | /**
79 | * 获取应用签名
80 | *
81 | * @param context 上下文
82 | * @param pkgName 包名
83 | * @return 返回应用的签名
84 | */
85 | fun getSign(context: Context, pkgName: String): String? {
86 | return try {
87 | @SuppressLint("PackageManagerGetSignatures") val pis = context.packageManager
88 | .getPackageInfo(pkgName,
89 | PackageManager.GET_SIGNATURES)
90 | hexDigest(pis.signatures[0].toByteArray())
91 | } catch (e: PackageManager.NameNotFoundException) {
92 | e.printStackTrace()
93 | null
94 | }
95 |
96 | }
97 |
98 | /**
99 | * 将签名字符串转换成需要的32位签名
100 | *
101 | * @param paramArrayOfByte 签名byte数组
102 | * @return 32位签名字符串
103 | */
104 | private fun hexDigest(paramArrayOfByte: ByteArray): String {
105 | val hexDigits = charArrayOf(48.toChar(), 49.toChar(), 50.toChar(), 51.toChar(), 52.toChar(), 53.toChar(), 54.toChar(), 55.toChar(), 56.toChar(), 57.toChar(), 97.toChar(), 98.toChar(), 99.toChar(), 100.toChar(), 101.toChar(), 102.toChar())
106 | try {
107 | val localMessageDigest = MessageDigest.getInstance("MD5")
108 | localMessageDigest.update(paramArrayOfByte)
109 | val arrayOfByte = localMessageDigest.digest()
110 | val arrayOfChar = CharArray(32)
111 | var i = 0
112 | var j = 0
113 | while (true) {
114 | if (i >= 16) {
115 | return String(arrayOfChar)
116 | }
117 | val k = arrayOfByte[i].toInt()
118 | arrayOfChar[j] = hexDigits[0xF and k.ushr(4)]
119 | arrayOfChar[++j] = hexDigits[k and 0xF]
120 | i++
121 | j++
122 | }
123 | } catch (e: Exception) {
124 | e.printStackTrace()
125 | }
126 |
127 | return ""
128 | }
129 |
130 |
131 | /**
132 | * 获取设备的可用内存大小
133 | *
134 | * @param context 应用上下文对象context
135 | * @return 当前内存大小
136 | */
137 | fun getDeviceUsableMemory(context: Context): Int {
138 | val am = context.getSystemService(
139 | Context.ACTIVITY_SERVICE) as ActivityManager
140 | val mi = ActivityManager.MemoryInfo()
141 | am.getMemoryInfo(mi)
142 | // 返回当前系统的可用内存
143 | return (mi.availMem / (1024 * 1024)).toInt()
144 | }
145 |
146 |
147 | fun getMobileModel(): String {
148 | var model: String? = Build.MODEL
149 | model = model?.trim { it <= ' ' } ?: ""
150 | return model
151 | }
152 |
153 | /**
154 | * 获取手机系统SDK版本
155 | *
156 | * @return 如API 17 则返回 17
157 | */
158 | val sdkVersion: Int
159 | get() = android.os.Build.VERSION.SDK_INT
160 | }
161 |
162 |
163 | }
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/utils/CleanLeakUtils.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.utils
2 |
3 | import android.content.Context
4 | import android.view.View
5 | import android.view.inputmethod.InputMethodManager
6 | import com.orhanobut.logger.Logger
7 |
8 | import java.lang.reflect.Field
9 |
10 | /**
11 | * desc:
12 | */
13 |
14 | object CleanLeakUtils {
15 |
16 | fun fixInputMethodManagerLeak(destContext: Context?) {
17 | if (destContext == null) {
18 | return
19 | }
20 | val inputMethodManager = destContext.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
21 |
22 | val viewArray = arrayOf("mCurRootView", "mServedView", "mNextServedView")
23 | var filed: Field
24 | var filedObject: Any?
25 |
26 | for (view in viewArray) {
27 | try {
28 | filed = inputMethodManager.javaClass.getDeclaredField(view)
29 | if (!filed.isAccessible) {
30 | filed.isAccessible = true
31 | }
32 | filedObject = filed.get(inputMethodManager)
33 | if (filedObject != null && filedObject is View) {
34 | val fileView = filedObject as View?
35 | if (fileView!!.context === destContext) { // 被InputMethodManager持有引用的context是想要目标销毁的
36 | filed.set(inputMethodManager, null) // 置空,破坏掉path to gc节点
37 | } else {
38 | break// 不是想要目标销毁的,即为又进了另一层界面了,不要处理,避免影响原逻辑,也就不用继续for循环了
39 | }
40 | }
41 | } catch (t: Throwable) {
42 | t.printStackTrace()
43 | }
44 |
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/utils/DisplayManager.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.utils
2 |
3 | import android.content.Context
4 | import android.util.DisplayMetrics
5 |
6 | /**
7 | * desc:
8 | */
9 |
10 | object DisplayManager {
11 | init {
12 |
13 | }
14 |
15 | private var displayMetrics: DisplayMetrics? = null
16 |
17 | private var screenWidth: Int? = null
18 |
19 | private var screenHeight: Int? = null
20 |
21 | private var screenDpi: Int? = null
22 |
23 | fun init(context: Context) {
24 | displayMetrics = context.resources.displayMetrics
25 | screenWidth = displayMetrics?.widthPixels
26 | screenHeight = displayMetrics?.heightPixels
27 | screenDpi = displayMetrics?.densityDpi
28 | }
29 |
30 |
31 | //UI图的大小
32 | private const val STANDARD_WIDTH = 1080
33 | private const val STANDARD_HEIGHT = 1920
34 |
35 |
36 | fun getScreenWidth(): Int? {
37 | return screenWidth
38 | }
39 |
40 | fun getScreenHeight(): Int? {
41 | return screenHeight
42 | }
43 |
44 |
45 | /**
46 | * 传入UI图中问题的高度,单位像素
47 | * @param size
48 | * @return
49 | */
50 | fun getPaintSize(size: Int): Int? {
51 | return getRealHeight(size)
52 | }
53 |
54 | /**
55 | * 输入UI图的尺寸,输出实际的px
56 | *
57 | * @param px ui图中的大小
58 | * @return
59 | */
60 | fun getRealWidth(px: Int): Int? {
61 | //ui图的宽度
62 | return getRealWidth(px, STANDARD_WIDTH.toFloat())
63 | }
64 |
65 | /**
66 | * 输入UI图的尺寸,输出实际的px,第二个参数是父布局
67 | *
68 | * @param px ui图中的大小
69 | * @param parentWidth 父view在ui图中的高度
70 | * @return
71 | */
72 | fun getRealWidth(px: Int, parentWidth: Float): Int? {
73 | return (px / parentWidth * getScreenWidth()!!).toInt()
74 | }
75 |
76 | /**
77 | * 输入UI图的尺寸,输出实际的px
78 | *
79 | * @param px ui图中的大小
80 | * @return
81 | */
82 | fun getRealHeight(px: Int): Int? {
83 | //ui图的宽度
84 | return getRealHeight(px, STANDARD_HEIGHT.toFloat())
85 | }
86 |
87 | /**
88 | * 输入UI图的尺寸,输出实际的px,第二个参数是父布局
89 | *
90 | * @param px ui图中的大小
91 | * @param parentHeight 父view在ui图中的高度
92 | * @return
93 | */
94 | fun getRealHeight(px: Int, parentHeight: Float): Int? {
95 | return (px / parentHeight * getScreenHeight()!!).toInt()
96 | }
97 |
98 | /**
99 | * dip转px
100 | * @param dipValue
101 | * @return int
102 | */
103 | fun dip2px(dipValue: Float): Int? {
104 | val scale = displayMetrics?.density
105 | return (dipValue * scale!! + 0.5f).toInt()
106 | }
107 | }
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/utils/GsonUtils.java:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.utils;
2 |
3 | import com.google.gson.Gson;
4 |
5 | import java.lang.reflect.Type;
6 | import java.util.List;
7 |
8 | /**
9 | */
10 | public class GsonUtils {
11 |
12 | public static String createGsonString(Object object) {
13 | Gson gson = new Gson();
14 | String gsonString = gson.toJson(object);
15 | return gsonString;
16 | }
17 |
18 | public static T changeGsonToBean(String gsonString, Class cls) {
19 | Gson gson = new Gson();
20 | T t = gson.fromJson(gsonString, cls);
21 | return t;
22 | }
23 |
24 | public static List changeGsonToList(String gsonString, Type type) {
25 | Gson gson = new Gson();
26 | List list = gson.fromJson(gsonString, type);
27 | return list;
28 | }
29 |
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/utils/NetworkUtil.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.utils
2 |
3 | import android.content.Context
4 | import android.net.ConnectivityManager
5 | import android.net.NetworkInfo
6 | import android.telephony.TelephonyManager
7 | import java.io.IOException
8 | import java.net.HttpURLConnection
9 | import java.net.NetworkInterface
10 | import java.net.SocketException
11 | import java.net.URL
12 |
13 |
14 | /**
15 | */
16 | class NetworkUtil{
17 |
18 | companion object {
19 |
20 | var NET_CNNT_BAIDU_OK = 1 // NetworkAvailable
21 | var NET_CNNT_BAIDU_TIMEOUT = 2 // no NetworkAvailable
22 | var NET_NOT_PREPARE = 3 // Net no ready
23 | var NET_ERROR = 4 //net error
24 | private val TIMEOUT = 3000 // TIMEOUT
25 | /**
26 | * check NetworkAvailable
27 | *
28 | * @param context
29 | * @return
30 | */
31 | @JvmStatic
32 | fun isNetworkAvailable(context: Context): Boolean {
33 | val manager = context.applicationContext.getSystemService(
34 | Context.CONNECTIVITY_SERVICE) as ConnectivityManager
35 | val info = manager.activeNetworkInfo
36 | return !(null == info || !info.isAvailable)
37 | }
38 |
39 | /**
40 | * 得到ip地址
41 | *
42 | * @return
43 | */
44 | @JvmStatic
45 | fun getLocalIpAddress(): String {
46 | var ret = ""
47 | try {
48 | val en = NetworkInterface.getNetworkInterfaces()
49 | while (en.hasMoreElements()) {
50 | val enumIpAddress = en.nextElement().inetAddresses
51 | while (enumIpAddress.hasMoreElements()) {
52 | val netAddress = enumIpAddress.nextElement()
53 | if (!netAddress.isLoopbackAddress) {
54 | ret = netAddress.hostAddress.toString()
55 | }
56 | }
57 | }
58 | } catch (ex: SocketException) {
59 | ex.printStackTrace()
60 | }
61 |
62 | return ret
63 | }
64 |
65 |
66 | /**
67 | * ping "http://www.baidu.com"
68 | *
69 | * @return
70 | */
71 | @JvmStatic
72 | private fun pingNetWork(): Boolean {
73 | var result = false
74 | var httpUrl: HttpURLConnection? = null
75 | try {
76 | httpUrl = URL("http://www.baidu.com")
77 | .openConnection() as HttpURLConnection
78 | httpUrl.connectTimeout = TIMEOUT
79 | httpUrl.connect()
80 | result = true
81 | } catch (e: IOException) {
82 | } finally {
83 | if (null != httpUrl) {
84 | httpUrl.disconnect()
85 | }
86 | }
87 | return result
88 | }
89 |
90 | /**
91 | * check is3G
92 | *
93 | * @param context
94 | * @return boolean
95 | */
96 | @JvmStatic
97 | fun is3G(context: Context): Boolean {
98 | val connectivityManager = context
99 | .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
100 | val activeNetInfo = connectivityManager.activeNetworkInfo
101 | return activeNetInfo != null && activeNetInfo.type == ConnectivityManager.TYPE_MOBILE
102 | }
103 |
104 | /**
105 | * isWifi
106 | *
107 | * @param context
108 | * @return boolean
109 | */
110 | @JvmStatic
111 | fun isWifi(context: Context): Boolean {
112 | val connectivityManager = context
113 | .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
114 | val activeNetInfo = connectivityManager.activeNetworkInfo
115 | return activeNetInfo != null && activeNetInfo.type == ConnectivityManager.TYPE_WIFI
116 | }
117 |
118 | /**
119 | * is2G
120 | *
121 | * @param context
122 | * @return boolean
123 | */
124 | @JvmStatic
125 | fun is2G(context: Context): Boolean {
126 | val connectivityManager = context
127 | .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
128 | val activeNetInfo = connectivityManager.activeNetworkInfo
129 | return activeNetInfo != null && (activeNetInfo.subtype == TelephonyManager.NETWORK_TYPE_EDGE
130 | || activeNetInfo.subtype == TelephonyManager.NETWORK_TYPE_GPRS || activeNetInfo
131 | .subtype == TelephonyManager.NETWORK_TYPE_CDMA)
132 | }
133 |
134 | /**
135 | * is wifi on
136 | */
137 | @JvmStatic
138 | fun isWifiEnabled(context: Context): Boolean {
139 | val mgrConn = context
140 | .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
141 | val mgrTel = context
142 | .getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
143 | return mgrConn.activeNetworkInfo != null && mgrConn
144 | .activeNetworkInfo.state == NetworkInfo.State.CONNECTED || mgrTel
145 | .networkType == TelephonyManager.NETWORK_TYPE_UMTS
146 | }
147 |
148 | }
149 |
150 |
151 |
152 | }
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/utils/Preference.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.utils
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import android.content.SharedPreferences
6 | import java.io.*
7 | import kotlin.reflect.KProperty
8 |
9 | /**
10 | * 12/11.
11 | * desc:kotlin委托属性+SharedPreference实例
12 | */
13 | class Preference constructor(val context: Context, val name: String, private val default: T) {
14 |
15 | private val mcontext: Context
16 |
17 | private val file_name = "kotlin_mvp_file"
18 |
19 | private val prefs: SharedPreferences
20 |
21 | init {
22 | this.mcontext = context
23 | prefs = mcontext.getSharedPreferences(file_name, Context.MODE_PRIVATE)
24 | }
25 |
26 |
27 | /**
28 | * 删除全部数据
29 | */
30 | fun clearPreference() {
31 | prefs.edit().clear().apply()
32 | }
33 |
34 | /**
35 | * 根据key删除存储数据
36 | */
37 | fun clearPreference(key: String) {
38 | prefs.edit().remove(key).apply()
39 | }
40 |
41 |
42 | operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
43 | return getSharedPreferences(name, default)
44 | }
45 |
46 | operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
47 | putSharedPreferences(name, value)
48 | }
49 |
50 | @SuppressLint("CommitPrefEdits")
51 | private fun putSharedPreferences(name: String, value: T) = with(prefs.edit()) {
52 | when (value) {
53 | is Long -> putLong(name, value)
54 | is String -> putString(name, value)
55 | is Int -> putInt(name, value)
56 | is Boolean -> putBoolean(name, value)
57 | is Float -> putFloat(name, value)
58 | else -> putString(name, serialize(value))
59 | }.apply()
60 | }
61 |
62 | @Suppress("UNCHECKED_CAST")
63 | private fun getSharedPreferences(name: String, default: T): T = with(prefs) {
64 | val res: Any = when (default) {
65 | is Long -> getLong(name, default)
66 | is String -> getString(name, default)
67 | is Int -> getInt(name, default)
68 | is Boolean -> getBoolean(name, default)
69 | is Float -> getFloat(name, default)
70 | else -> deSerialization(getString(name, serialize(default)))
71 | }
72 | return res as T
73 | }
74 |
75 |
76 | /**
77 | * 序列化对象
78 |
79 | * @param person
80 | * *
81 | * @return
82 | * *
83 | * @throws IOException
84 | */
85 | @Throws(IOException::class)
86 | private fun serialize(obj: A): String {
87 | val byteArrayOutputStream = ByteArrayOutputStream()
88 | val objectOutputStream = ObjectOutputStream(
89 | byteArrayOutputStream
90 | )
91 | objectOutputStream.writeObject(obj)
92 | var serStr = byteArrayOutputStream.toString("ISO-8859-1")
93 | serStr = java.net.URLEncoder.encode(serStr, "UTF-8")
94 | objectOutputStream.close()
95 | byteArrayOutputStream.close()
96 | return serStr
97 | }
98 |
99 | /**
100 | * 反序列化对象
101 |
102 | * @param str
103 | * *
104 | * @return
105 | * *
106 | * @throws IOException
107 | * *
108 | * @throws ClassNotFoundException
109 | */
110 | @Suppress("UNCHECKED_CAST")
111 | @Throws(IOException::class, ClassNotFoundException::class)
112 | private fun deSerialization(str: String): A {
113 | val redStr = java.net.URLDecoder.decode(str, "UTF-8")
114 | val byteArrayInputStream = ByteArrayInputStream(
115 | redStr.toByteArray(charset("ISO-8859-1"))
116 | )
117 | val objectInputStream = ObjectInputStream(
118 | byteArrayInputStream
119 | )
120 | val obj = objectInputStream.readObject() as A
121 | objectInputStream.close()
122 | byteArrayInputStream.close()
123 | return obj
124 | }
125 |
126 |
127 | /**
128 | * 查询某个key是否已经存在
129 | *
130 | * @param key
131 | * @return
132 | */
133 | fun contains(key: String): Boolean {
134 | return prefs.contains(key)
135 | }
136 |
137 | /**
138 | * 返回所有的键值对
139 | *
140 | * @param context
141 | * @return
142 | */
143 | fun getAll(): Map {
144 | return prefs.all
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/view/LoadingView.java:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.view;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Canvas;
6 | import android.graphics.Paint;
7 | import android.graphics.Path;
8 | import android.graphics.PointF;
9 | import android.graphics.RectF;
10 | import android.os.Handler;
11 | import android.os.Message;
12 | import android.util.AttributeSet;
13 | import android.view.View;
14 | import com.kotlinmvp.R;
15 |
16 |
17 | import java.lang.ref.WeakReference;
18 |
19 | /**
20 | * 12/4.
21 | * desc:LoadingView 加载动画
22 | */
23 |
24 | public class LoadingView extends View {
25 |
26 | private int mOuterCircleRadius;
27 | private int mOuterCircleColor;
28 | private int mInnerTriangleRadius;
29 | private int mInnerTriangleColor;
30 | private int mBackgroundColor;
31 | private int mStrokeWidth;
32 | private boolean mIsNeedBackground;
33 |
34 | private Paint mPaint;
35 | private Paint mTrianglePaint;
36 | private Paint mBackGroundPaint;
37 | private boolean isReverse = false;
38 | private int mProgress = 0;
39 | private int mStartAngle = -90;
40 | private int mRotateAngle = 0;
41 | private int mDel = 30;
42 | private RectF mRectF;
43 | private RectF mRoundRectF;
44 | private Path mPath;
45 | private PointF mRotateCenter;
46 | private MyHandler mHandler;
47 |
48 |
49 | public LoadingView(Context context) {
50 | this(context, null);
51 |
52 | }
53 |
54 | public LoadingView(Context context, AttributeSet attrs) {
55 | this(context, attrs, 0);
56 | }
57 |
58 | public LoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
59 | super(context, attrs, defStyleAttr);
60 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LoadingView);
61 | mOuterCircleRadius = typedArray.getDimensionPixelSize(R.styleable.LoadingView_outerCircleRadius, 50);
62 | mOuterCircleColor = typedArray.getColor(R.styleable.LoadingView_outerCircleColor, 0xFF228B22);
63 | mInnerTriangleRadius = typedArray.getDimensionPixelSize(R.styleable.LoadingView_innerTriangleRadius, 25);
64 | mInnerTriangleColor = typedArray.getColor(R.styleable.LoadingView_innerTriangleColor, 0xFF228B22);
65 | mIsNeedBackground = typedArray.getBoolean(R.styleable.LoadingView_isNeedBackground, true);
66 | mBackgroundColor = typedArray.getColor(R.styleable.LoadingView_backgroundColor, 0xBB222222);
67 | mStrokeWidth = typedArray.getDimensionPixelSize(R.styleable.LoadingView_strokeWidth, 5);
68 |
69 | typedArray.recycle();
70 |
71 | init();
72 | }
73 |
74 | private void init() {
75 | //设置画进度圈的画笔
76 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
77 | mPaint.setColor(mOuterCircleColor);
78 | mPaint.setStrokeWidth(mStrokeWidth);
79 | mPaint.setStyle(Paint.Style.STROKE);
80 |
81 | //设置画三角形的画笔
82 | mTrianglePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
83 | mTrianglePaint.setColor(mInnerTriangleColor);
84 |
85 | //设置画背景的画笔
86 | mBackGroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
87 | mBackGroundPaint.setColor(mBackgroundColor);
88 |
89 | mPath = new Path();
90 | mRotateCenter = new PointF();
91 | mRoundRectF = new RectF();
92 | mHandler = new MyHandler(this);
93 | }
94 |
95 |
96 | @Override
97 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
98 | super.onSizeChanged(w, h, oldw, oldh);
99 | //判断外圆的半径
100 | mOuterCircleRadius = (int) (Math.min(mOuterCircleRadius,
101 | (Math.min(w - getPaddingRight() - getPaddingLeft(), h - getPaddingTop() - getPaddingBottom()) - 4 * mPaint.getStrokeWidth()) / 2));
102 | if (mOuterCircleRadius < 0) {
103 | mStrokeWidth = Math.min(w - getPaddingRight() - getPaddingLeft(), h - getPaddingTop() - getPaddingBottom()) / 2;
104 | mOuterCircleRadius = Math.min(w - getPaddingRight() - getPaddingLeft(), h - getPaddingTop() - getPaddingBottom()) / 4;
105 | }
106 | float left = (w - 2 * mOuterCircleRadius) / 2;
107 | float top = (h - 2 * mOuterCircleRadius) / 2;
108 | float diameter = 2 * mOuterCircleRadius;
109 | mRectF = new RectF(left, top, left + diameter, top + diameter);
110 |
111 | //判断内圆的半径大小
112 | mInnerTriangleRadius = (mInnerTriangleRadius < mOuterCircleRadius) ? mInnerTriangleRadius : 3 * mOuterCircleRadius / 5;
113 | if (mInnerTriangleRadius < 0) {
114 | mInnerTriangleRadius = 0;
115 | }
116 | //计算内圆的圆心,圆心应该和外圆圆心相同
117 | float centerX = left + mOuterCircleRadius;
118 | float centerY = top + mOuterCircleRadius;
119 | //计算内圆的内接三角形的三个定点组成的path
120 | mPath.moveTo(centerX - mInnerTriangleRadius / 2, (float) (centerY - Math.sqrt(3) * mInnerTriangleRadius / 2));
121 | mPath.lineTo(centerX + mInnerTriangleRadius, centerY);
122 | mPath.lineTo(centerX - mInnerTriangleRadius / 2, (float) (centerY + Math.sqrt(3) * mInnerTriangleRadius / 2));
123 | mPath.close();
124 |
125 | mRotateCenter.set(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
126 | mRoundRectF.left = 0;
127 | mRoundRectF.top = 0;
128 | mRoundRectF.right = w;
129 | mRoundRectF.bottom = h;
130 | }
131 |
132 | @Override
133 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
134 | setMeasuredDimension(measureSize(widthMeasureSpec, 140), measureSize(heightMeasureSpec, 140));
135 | }
136 |
137 | private int measureSize(int measureSpec, int defaultSize) {
138 | int specMode = MeasureSpec.getMode(measureSpec);
139 | int specSize = MeasureSpec.getSize(measureSpec);
140 |
141 | int resultSize = defaultSize;
142 | switch (specMode) {
143 | case MeasureSpec.EXACTLY:
144 | resultSize = specSize;
145 | break;
146 | case MeasureSpec.AT_MOST:
147 | case MeasureSpec.UNSPECIFIED:
148 | resultSize = Math.min(specSize, resultSize);
149 | break;
150 | }
151 | return resultSize;
152 | }
153 |
154 |
155 | @Override
156 | protected void onDraw(Canvas canvas) {
157 | if (mIsNeedBackground) {
158 | canvas.drawRoundRect(mRoundRectF, 8, 8, mBackGroundPaint);
159 | }
160 |
161 | if (isReverse) {
162 | mProgress -= mDel;
163 | mStartAngle += mDel;
164 | if (mStartAngle >= 270) {
165 | mStartAngle = -90;
166 | isReverse = false;
167 | }
168 | mRotateAngle += mDel;
169 | if (mRotateAngle >= 360) {
170 | mRotateAngle = 0;
171 | }
172 | canvas.save();
173 | canvas.rotate(mRotateAngle, mRotateCenter.x, mRotateCenter.y);
174 | canvas.drawPath(mPath, mTrianglePaint);
175 | canvas.restore();
176 | } else {
177 | mProgress += mDel;
178 | if (mProgress >= 360) {
179 | isReverse = true;
180 | }
181 | canvas.drawPath(mPath, mTrianglePaint);
182 | }
183 |
184 | canvas.drawArc(mRectF, mStartAngle, mProgress, false, mPaint);
185 | mHandler.sendEmptyMessageDelayed(MyHandler.REFRESH_VIEW, 80);
186 | }
187 |
188 |
189 | private static class MyHandler extends Handler {
190 | static final int REFRESH_VIEW = 0;
191 | private final WeakReference mLoadingViewWeakReference;
192 |
193 | private MyHandler(LoadingView loadingView) {
194 | mLoadingViewWeakReference = new WeakReference<>(loadingView);
195 | }
196 |
197 | @Override
198 | public void handleMessage(Message msg) {
199 | if (mLoadingViewWeakReference.get() != null) {
200 | switch (msg.what) {
201 | case REFRESH_VIEW:
202 | mLoadingViewWeakReference.get().postInvalidate();
203 | break;
204 | }
205 | }
206 | }
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/view/ViewAnimUtils.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.view
2 |
3 | import android.animation.Animator
4 | import android.animation.AnimatorListenerAdapter
5 | import android.annotation.TargetApi
6 | import android.content.Context
7 | import android.os.Build
8 | import android.support.annotation.ColorRes
9 | import android.support.annotation.RequiresApi
10 | import android.support.v4.content.ContextCompat
11 | import android.view.View
12 | import android.view.ViewAnimationUtils
13 | import android.view.animation.AccelerateDecelerateInterpolator
14 |
15 | /**
16 | * 12/1.
17 | * desc: View 动画工具类
18 | */
19 |
20 | object ViewAnimUtils {
21 |
22 | interface OnRevealAnimationListener {
23 | fun onRevealHide()
24 |
25 | fun onRevealShow()
26 | }
27 |
28 |
29 |
30 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
31 | fun animateRevealShow(
32 | context: Context, view: View,
33 | startRadius: Int, @ColorRes color: Int,
34 | listener: OnRevealAnimationListener) {
35 | val cx = (view.left + view.right) / 2
36 | val cy = (view.top + view.bottom) / 2
37 |
38 | val finalRadius = Math.hypot(view.width.toDouble(), view.height.toDouble()).toFloat()
39 |
40 | // 设置圆形显示动画
41 | val anim = ViewAnimationUtils.createCircularReveal(view, cx, cy, startRadius.toFloat(), finalRadius)
42 | anim.duration = 300
43 | anim.interpolator = AccelerateDecelerateInterpolator()
44 | anim.addListener(object : AnimatorListenerAdapter() {
45 | override fun onAnimationEnd(animation: Animator) {
46 | super.onAnimationEnd(animation)
47 | view.visibility = View.VISIBLE
48 | listener.onRevealShow()
49 | }
50 |
51 | override fun onAnimationStart(animation: Animator) {
52 | super.onAnimationStart(animation)
53 | view.setBackgroundColor(ContextCompat.getColor(context, color))
54 | }
55 | })
56 |
57 | anim.start()
58 | }
59 |
60 | // 圆圈凝聚效果
61 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
62 | fun animateRevealHide(
63 | context: Context, view: View,
64 | finalRadius: Int, @ColorRes color: Int,
65 | listener: OnRevealAnimationListener
66 | ) {
67 | val cx = (view.left + view.right) / 2
68 | val cy = (view.top + view.bottom) / 2
69 | val initialRadius = view.width
70 | // 与入场动画的区别就是圆圈起始和终止的半径相反
71 | val anim = ViewAnimationUtils.createCircularReveal(view, cx, cy, initialRadius.toFloat(), finalRadius.toFloat())
72 | anim.duration = 300
73 | anim.interpolator = AccelerateDecelerateInterpolator()
74 | anim.addListener(object : AnimatorListenerAdapter() {
75 | override fun onAnimationStart(animation: Animator) {
76 | super.onAnimationStart(animation)
77 | view.setBackgroundColor(ContextCompat.getColor(context, color))
78 | }
79 |
80 | override fun onAnimationEnd(animation: Animator) {
81 | super.onAnimationEnd(animation)
82 | listener.onRevealHide()
83 | view.visibility = View.INVISIBLE
84 | }
85 | })
86 | anim.start()
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/view/recyclerview/MultipleType.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.view.recyclerview
2 |
3 | /**
4 | * 11/22.
5 | * desc: 多布局条目类型
6 | */
7 |
8 | interface MultipleType {
9 | fun getLayoutId(item: T, position: Int): Int
10 | }
11 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/view/recyclerview/ViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.view.recyclerview
2 |
3 | import android.annotation.SuppressLint
4 | import android.support.v7.widget.RecyclerView
5 | import android.util.SparseArray
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import android.widget.ImageView
9 | import android.widget.TextView
10 |
11 | @Suppress("UNCHECKED_CAST")
12 | /**
13 | * 11/22.
14 | * desc:
15 | */
16 |
17 | class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
18 |
19 | //用于缓存已找的界面
20 | private var mView: SparseArray?=null
21 |
22 | init {
23 | mView = SparseArray()
24 | }
25 |
26 | fun getView(viewId: Int): T {
27 | //对已有的view做缓存
28 | var view: View? = mView?.get(viewId)
29 | //使用缓存的方式减少findViewById的次数
30 | if (view == null) {
31 | view = itemView.findViewById(viewId)
32 | mView?.put(viewId, view)
33 | }
34 | return view as T
35 | }
36 |
37 |
38 | fun getViewGroup(viewId: Int): T {
39 | //对已有的view做缓存
40 | var view: View? = mView?.get(viewId)
41 | //使用缓存的方式减少findViewById的次数
42 | if (view == null) {
43 | view = itemView.findViewById(viewId)
44 | mView?.put(viewId, view)
45 | }
46 | return view as T
47 | }
48 |
49 | @SuppressLint("SetTextI18n")
50 | //通用的功能进行封装 设置文本 设置条目点击事件 设置图片
51 | fun setText(viewId: Int, text: CharSequence): ViewHolder {
52 | val view = getView(viewId)
53 | view.text = "" + text
54 | //希望可以链式调用
55 | return this
56 | }
57 |
58 | fun setHintText(viewId: Int, text: CharSequence): ViewHolder {
59 | val view = getView(viewId)
60 | view.hint = "" + text
61 | return this
62 | }
63 |
64 | /**
65 | * 设置本地图片
66 | *
67 | * @param viewId
68 | * @param resId
69 | * @return
70 | */
71 | fun setImageResource(viewId: Int, resId: Int): ViewHolder {
72 | val iv = getView(viewId)
73 | iv.setImageResource(resId)
74 | return this
75 | }
76 |
77 | /**
78 | * 加载图片资源路径
79 | *
80 | * @param viewId
81 | * @param imageLoader
82 | * @return
83 | */
84 | fun setImagePath(viewId: Int, imageLoader: HolderImageLoader): ViewHolder {
85 | val iv = getView(viewId)
86 | imageLoader.loadImage(iv, imageLoader.path)
87 | return this
88 | }
89 |
90 | abstract class HolderImageLoader(val path: String) {
91 |
92 | /**
93 | * 需要去复写这个方法加载图片
94 | *
95 | * @param iv
96 | * @param path
97 | */
98 | abstract fun loadImage(iv: ImageView, path: String)
99 | }
100 |
101 | /**
102 | * 设置View的Visibility
103 | */
104 | fun setViewVisibility(viewId: Int, visibility: Int): ViewHolder {
105 | getView(viewId).visibility = visibility
106 | return this
107 | }
108 |
109 | /**
110 | * 设置条目点击事件
111 | */
112 | fun setOnItemClickListener(listener: View.OnClickListener) {
113 | itemView.setOnClickListener(listener)
114 | }
115 |
116 | /**
117 | * 设置条目长按事件
118 | */
119 | fun setOnItemLongClickListener(listener: View.OnLongClickListener) {
120 | itemView.setOnLongClickListener(listener)
121 | }
122 |
123 | }
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/view/recyclerview/adapter/CommonAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.view.recyclerview.adapter
2 |
3 | import android.content.Context
4 | import android.support.v7.widget.RecyclerView
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 |
9 | import com.kotlinmvp.view.recyclerview.MultipleType
10 | import com.kotlinmvp.view.recyclerview.ViewHolder
11 |
12 | /**
13 | * 11/22.
14 | * desc: 通用的 Adapter
15 | */
16 |
17 | abstract class CommonAdapter(var mContext: Context, var mData: ArrayList, //条目布局
18 | private var mLayoutId: Int) : RecyclerView.Adapter() {
19 | protected var mInflater: LayoutInflater? = null
20 | private var mTypeSupport: MultipleType? = null
21 |
22 | //使用接口回调点击事件
23 | private var mItemClickListener: OnItemClickListener? = null
24 |
25 | //使用接口回调点击事件
26 | private var mItemLongClickListener: OnItemLongClickListener? = null
27 |
28 | init {
29 | mInflater = LayoutInflater.from(mContext)
30 | }
31 |
32 | //需要多布局
33 | constructor(context: Context, data: ArrayList, typeSupport: MultipleType) : this(context, data, -1) {
34 | this.mTypeSupport = typeSupport
35 | }
36 |
37 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
38 | if (mTypeSupport != null) {
39 | //需要多布局
40 | mLayoutId = viewType
41 | }
42 | //创建view
43 | val view = mInflater?.inflate(mLayoutId, parent, false)
44 | return ViewHolder(view!!)
45 | }
46 |
47 | override fun getItemViewType(position: Int): Int {
48 | //多布局问题
49 | return mTypeSupport?.getLayoutId(mData[position], position) ?: super.getItemViewType(position)
50 | }
51 |
52 | override fun onBindViewHolder(holder: ViewHolder, position: Int) {
53 | //绑定数据
54 | bindData(holder, mData[position], position)
55 |
56 | // if (mItemClickListener != null) {
57 | // holder.itemView.setOnClickListener { mItemClickListener!!.onItemClick(mData[position], position) }
58 | // }
59 | // //长按点击事件
60 | // if (mItemLongClickListener != null) {
61 | // holder.itemView.setOnLongClickListener { mItemLongClickListener!!.onItemLongClick(mData[position], position) }
62 | // }
63 | //条目点击事件
64 | mItemClickListener?.let {
65 | holder.itemView.setOnClickListener { mItemClickListener!!.onItemClick(mData[position], position) }
66 | }
67 |
68 | //长按点击事件
69 | mItemLongClickListener?.let {
70 | holder.itemView.setOnLongClickListener { mItemLongClickListener!!.onItemLongClick(mData[position], position) }
71 | }
72 | }
73 |
74 | /**
75 | * 将必要参数传递出去
76 | *
77 | * @param holder
78 | * @param data
79 | * @param position
80 | */
81 | protected abstract fun bindData(holder: ViewHolder, data: T, position: Int)
82 |
83 | override fun getItemCount(): Int {
84 | return mData.size
85 | }
86 |
87 | fun setOnItemClickListener(itemClickListener: OnItemClickListener) {
88 | this.mItemClickListener = itemClickListener
89 | }
90 |
91 | fun setOnItemLongClickListener(itemLongClickListener: OnItemLongClickListener) {
92 | this.mItemLongClickListener = itemLongClickListener
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/view/recyclerview/adapter/OnItemClickListener.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.view.recyclerview.adapter
2 |
3 |
4 | interface OnItemClickListener {
5 |
6 | fun onItemClick(obj: Any?, position: Int)
7 | }
8 |
--------------------------------------------------------------------------------
/common_lib/src/main/java/com/kotlinmvp/view/recyclerview/adapter/OnItemLongClickListener.kt:
--------------------------------------------------------------------------------
1 | package com.kotlinmvp.view.recyclerview.adapter
2 |
3 | /**
4 | *
5 | * Description: Adapter条目的长按事件
6 | */
7 | interface OnItemLongClickListener {
8 |
9 | fun onItemLongClick(obj: Any?, position: Int): Boolean
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/common_lib/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/common_lib/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFFFFF
4 | #549cf8
5 | #EF5362
6 |
7 | #9a9a9a
8 |
9 | #5000
10 |
11 |
12 | #EF5362
13 | #FE6D4B
14 | #FFCF47
15 | #9FD661
16 | #3FD0AD
17 | #2BBDF3
18 | #00b0ff
19 | #AC8FEF
20 | #EE85C1
21 |
22 | #ff4081
23 | #dd2c00
24 | #ee447AA4
25 |
26 |
27 |
28 |
29 |
30 | #CCFFFFFF
31 |
32 | #EEEEEE
33 |
34 |
35 | #FFFFFF
36 | #000000
37 |
38 | #87CEEB
39 |
40 |
41 | #000000
42 |
43 | #ddd
44 | #aaa
45 |
46 | @color/color_lighter_gray
47 |
48 | #00000000
49 |
50 | #7FFFD4
51 |
52 | #5000
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/common_lib/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 16dp
3 | 51dp
4 |
5 |
6 | 10dp
7 | 15dp
8 | 10dp
9 | 5dp
10 |
11 | 10sp
12 | 12sp
13 | 14sp
14 | 16sp
15 | 18sp
16 |
17 | 0.5dp
18 |
19 |
--------------------------------------------------------------------------------
/common_lib/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/common_lib/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Kotlin Mvp
3 |
4 |
5 | 万
6 | 亿
7 | 万亿
8 | 京
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/common_lib/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
34 |
35 |
36 |
40 |
41 |
48 |
49 |
55 |
56 |
57 |
64 |
65 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/config.gradle:
--------------------------------------------------------------------------------
1 | ext{
2 |
3 | android = [
4 | compileSdkVersion: 27,
5 | buildToolsVersion: "27.0.3",
6 | minSdkVersion : 19,
7 | targetSdkVersion : 27,
8 | versionCode : 6,
9 | versionName : "1.3.0"
10 | ]
11 |
12 | dependVersion = [
13 | androidSupportSdkVersion: "27.1.1",
14 | retrofitSdkVersion : "2.3.0",
15 | glideSdkVersion : "4.7.1",
16 | rxJava : "2.1.5",
17 | rxAndroid : "2.0.1"
18 | ]
19 |
20 | supportDeps = [
21 | //-------- support -------
22 | supportv4 : "com.android.support:support-v4:$dependVersion.androidSupportSdkVersion",
23 | appcompatv7 : "com.android.support:appcompat-v7:$dependVersion.androidSupportSdkVersion",
24 | cardview : "com.android.support:cardview-v7:$dependVersion.androidSupportSdkVersion",
25 | design : "com.android.support:design:$dependVersion.androidSupportSdkVersion",
26 | annotations : "com.android.support:support-annotations:$dependVersion.androidSupportSdkVersion"
27 | ]
28 |
29 |
30 | retrofit = [
31 | //------ retrofit和RxJava ---------
32 | retrofit : "com.squareup.retrofit2:retrofit:$dependVersion.retrofitSdkVersion",
33 | retrofitConverterGson : "com.squareup.retrofit2:converter-gson:$dependVersion.retrofitSdkVersion",
34 | retrofitAdapterRxjava2 : "com.squareup.retrofit2:adapter-rxjava2:$dependVersion.retrofitSdkVersion",
35 | okhttp3LoggerInterceptor: 'com.squareup.okhttp3:logging-interceptor:3.4.1',
36 |
37 | //stetho
38 | stetho : 'com.facebook.stetho:stetho:1.3.1',
39 | stetho_okhttp3 : 'com.facebook.stetho:stetho-okhttp3:1.3.1'
40 | ]
41 |
42 | rxJava = [
43 | rxJava : "io.reactivex.rxjava2:rxjava:$dependVersion.rxJava",
44 | rxAndroid : "io.reactivex.rxjava2:rxandroid:$dependVersion.rxAndroid"
45 | ]
46 |
47 |
48 | glide = "com.github.bumptech.glide:glide:$dependVersion.glideSdkVersion"
49 | glideCompiler = "com.github.bumptech.glide:compiler:$dependVersion.glideSdkVersion"
50 | glideOkhttp = "com.github.bumptech.glide:okhttp3-integration:$dependVersion.glideSdkVersion"
51 |
52 |
53 | supportLibs = supportDeps.values()
54 | networkLibs = retrofit.values()
55 | rxJavaLibs = rxJava.values()
56 | otherLibs = [glide]
57 |
58 | // APT
59 | annotationProcessorLibs =[glideCompiler]
60 |
61 | }
--------------------------------------------------------------------------------
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bayshier/MVPKotlin_Commonlib/d9aa24e8f99520d99ec5d3cacd5936fc1ecdba53/demo.gif
--------------------------------------------------------------------------------
/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 | # Kotlin code style for this project: "official" or "obsolete":
15 | kotlin.code.style=official
16 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bayshier/MVPKotlin_Commonlib/d9aa24e8f99520d99ec5d3cacd5936fc1ecdba53/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/multiple-status-view/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | *.iml
--------------------------------------------------------------------------------
/multiple-status-view/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 |
5 | compileSdkVersion rootProject.ext.android.compileSdkVersion
6 | buildToolsVersion '28.0.3'
7 |
8 | defaultConfig {
9 | minSdkVersion rootProject.ext.android.minSdkVersion
10 | targetSdkVersion rootProject.ext.android.targetSdkVersion
11 | versionCode rootProject.ext.android.versionCode
12 | versionName rootProject.ext.android.versionName
13 | }
14 |
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 | }
22 |
23 | dependencies { }
--------------------------------------------------------------------------------
/multiple-status-view/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\AndroidStudio\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/multiple-status-view/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/multiple-status-view/src/main/java/com/classic/common/MultipleStatusView.java:
--------------------------------------------------------------------------------
1 | package com.classic.common;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.util.AttributeSet;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.RelativeLayout;
10 |
11 | import java.util.ArrayList;
12 |
13 | /**
14 | * 类描述: 一个方便在多种状态切换的view
15 | *
16 | * 创建时间: 2016/1/15 10:20.
17 | */
18 | @SuppressWarnings("unused")
19 | public class MultipleStatusView extends RelativeLayout {
20 | private static final String TAG = "MultipleStatusView";
21 |
22 | private static final RelativeLayout.LayoutParams DEFAULT_LAYOUT_PARAMS =
23 | new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
24 | RelativeLayout.LayoutParams.MATCH_PARENT);
25 |
26 | public static final int STATUS_CONTENT = 0x00;
27 | public static final int STATUS_LOADING = 0x01;
28 | public static final int STATUS_EMPTY = 0x02;
29 | public static final int STATUS_ERROR = 0x03;
30 | public static final int STATUS_NO_NETWORK = 0x04;
31 |
32 | private static final int NULL_RESOURCE_ID = -1;
33 |
34 | private View mEmptyView;
35 | private View mErrorView;
36 | private View mLoadingView;
37 | private View mNoNetworkView;
38 | private View mContentView;
39 | private int mEmptyViewResId;
40 | private int mErrorViewResId;
41 | private int mLoadingViewResId;
42 | private int mNoNetworkViewResId;
43 | private int mContentViewResId;
44 |
45 | private int mViewStatus;
46 | private LayoutInflater mInflater;
47 | private OnClickListener mOnRetryClickListener;
48 |
49 | private final ArrayList mOtherIds = new ArrayList<>();
50 |
51 | public MultipleStatusView(Context context) {
52 | this(context, null);
53 | }
54 |
55 | public MultipleStatusView(Context context, AttributeSet attrs) {
56 | this(context, attrs, 0);
57 | }
58 |
59 | public MultipleStatusView(Context context, AttributeSet attrs, int defStyleAttr) {
60 | super(context, attrs, defStyleAttr);
61 | final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MultipleStatusView, defStyleAttr, 0);
62 | mEmptyViewResId = a.getResourceId(R.styleable.MultipleStatusView_emptyView, R.layout.empty_view);
63 | mErrorViewResId = a.getResourceId(R.styleable.MultipleStatusView_errorView, R.layout.error_view);
64 | mLoadingViewResId = a.getResourceId(R.styleable.MultipleStatusView_loadingView, R.layout.loading_view);
65 | mNoNetworkViewResId = a.getResourceId(R.styleable.MultipleStatusView_noNetworkView, R.layout.no_network_view);
66 | mContentViewResId = a.getResourceId(R.styleable.MultipleStatusView_contentView, NULL_RESOURCE_ID);
67 | a.recycle();
68 | mInflater = LayoutInflater.from(getContext());
69 | }
70 |
71 | @Override protected void onFinishInflate() {
72 | super.onFinishInflate();
73 | showContent();
74 | }
75 |
76 | @Override protected void onDetachedFromWindow() {
77 | super.onDetachedFromWindow();
78 | clear(mEmptyView, mLoadingView, mErrorView, mNoNetworkView);
79 | if (null != mOtherIds) {
80 | mOtherIds.clear();
81 | }
82 | if (null != mOnRetryClickListener) {
83 | mOnRetryClickListener = null;
84 | }
85 | mInflater = null;
86 | }
87 |
88 | /**
89 | * 获取当前状态
90 | */
91 | public int getViewStatus() {
92 | return mViewStatus;
93 | }
94 |
95 | /**
96 | * 设置重试点击事件
97 | *
98 | * @param onRetryClickListener 重试点击事件
99 | */
100 | public void setOnRetryClickListener(OnClickListener onRetryClickListener) {
101 | this.mOnRetryClickListener = onRetryClickListener;
102 | }
103 |
104 | /**
105 | * 显示空视图
106 | */
107 | public final void showEmpty() {
108 | showEmpty(mEmptyViewResId, DEFAULT_LAYOUT_PARAMS);
109 | }
110 |
111 | /**
112 | * 显示空视图
113 | * @param layoutId 自定义布局文件
114 | * @param layoutParams 布局参数
115 | */
116 | public final void showEmpty(int layoutId, ViewGroup.LayoutParams layoutParams) {
117 | showEmpty(inflateView(layoutId), layoutParams);
118 | }
119 |
120 | /**
121 | * 显示空视图
122 | * @param view 自定义视图
123 | * @param layoutParams 布局参数
124 | */
125 | public final void showEmpty(View view, ViewGroup.LayoutParams layoutParams) {
126 | checkNull(view, "Empty view is null!");
127 | mViewStatus = STATUS_EMPTY;
128 | if (null == mEmptyView) {
129 | mEmptyView = view;
130 | View emptyRetryView = mEmptyView.findViewById(R.id.empty_retry_view);
131 | if (null != mOnRetryClickListener && null != emptyRetryView) {
132 | emptyRetryView.setOnClickListener(mOnRetryClickListener);
133 | }
134 | mOtherIds.add(mEmptyView.getId());
135 | addView(mEmptyView, 0, layoutParams);
136 | }
137 | showViewById(mEmptyView.getId());
138 | }
139 |
140 | /**
141 | * 显示错误视图
142 | */
143 | public final void showError() {
144 | showError(mErrorViewResId, DEFAULT_LAYOUT_PARAMS);
145 | }
146 |
147 | /**
148 | * 显示错误视图
149 | * @param layoutId 自定义布局文件
150 | * @param layoutParams 布局参数
151 | */
152 | public final void showError(int layoutId, ViewGroup.LayoutParams layoutParams) {
153 | showError(inflateView(layoutId), layoutParams);
154 | }
155 |
156 | /**
157 | * 显示错误视图
158 | * @param view 自定义视图
159 | * @param layoutParams 布局参数
160 | */
161 | public final void showError(View view, ViewGroup.LayoutParams layoutParams) {
162 | checkNull(view, "Error view is null!");
163 | mViewStatus = STATUS_ERROR;
164 | if (null == mErrorView) {
165 | mErrorView = view;
166 | View errorRetryView = mErrorView.findViewById(R.id.error_retry_view);
167 | if (null != mOnRetryClickListener && null != errorRetryView) {
168 | errorRetryView.setOnClickListener(mOnRetryClickListener);
169 | }
170 | mOtherIds.add(mErrorView.getId());
171 | addView(mErrorView, 0, layoutParams);
172 | }
173 | showViewById(mErrorView.getId());
174 | }
175 |
176 | /**
177 | * 显示加载中视图
178 | */
179 | public final void showLoading() {
180 | showLoading(mLoadingViewResId, DEFAULT_LAYOUT_PARAMS);
181 | }
182 |
183 | /**
184 | * 显示加载中视图
185 | * @param layoutId 自定义布局文件
186 | * @param layoutParams 布局参数
187 | */
188 | public final void showLoading(int layoutId, ViewGroup.LayoutParams layoutParams) {
189 | showLoading(inflateView(layoutId), layoutParams);
190 | }
191 |
192 | /**
193 | * 显示加载中视图
194 | * @param view 自定义视图
195 | * @param layoutParams 布局参数
196 | */
197 | public final void showLoading(View view, ViewGroup.LayoutParams layoutParams) {
198 | checkNull(view, "Loading view is null!");
199 | mViewStatus = STATUS_LOADING;
200 | if (null == mLoadingView) {
201 | mLoadingView = view;
202 | mOtherIds.add(mLoadingView.getId());
203 | addView(mLoadingView, 0, layoutParams);
204 | }
205 | showViewById(mLoadingView.getId());
206 | }
207 |
208 | /**
209 | * 显示无网络视图
210 | */
211 | public final void showNoNetwork() {
212 | showNoNetwork(mNoNetworkViewResId, DEFAULT_LAYOUT_PARAMS);
213 | }
214 |
215 | /**
216 | * 显示无网络视图
217 | * @param layoutId 自定义布局文件
218 | * @param layoutParams 布局参数
219 | */
220 | public final void showNoNetwork(int layoutId, ViewGroup.LayoutParams layoutParams) {
221 | showNoNetwork(inflateView(layoutId), layoutParams);
222 | }
223 |
224 | /**
225 | * 显示无网络视图
226 | * @param view 自定义视图
227 | * @param layoutParams 布局参数
228 | */
229 | public final void showNoNetwork(View view, ViewGroup.LayoutParams layoutParams) {
230 | checkNull(view, "No network view is null!");
231 | mViewStatus = STATUS_NO_NETWORK;
232 | if (null == mNoNetworkView) {
233 | mNoNetworkView = view;
234 | View noNetworkRetryView = mNoNetworkView.findViewById(R.id.no_network_retry_view);
235 | if (null != mOnRetryClickListener && null != noNetworkRetryView) {
236 | noNetworkRetryView.setOnClickListener(mOnRetryClickListener);
237 | }
238 | mOtherIds.add(mNoNetworkView.getId());
239 | addView(mNoNetworkView, 0, layoutParams);
240 | }
241 | showViewById(mNoNetworkView.getId());
242 | }
243 |
244 | /**
245 | * 显示内容视图
246 | */
247 | public final void showContent() {
248 | mViewStatus = STATUS_CONTENT;
249 | if (null == mContentView && mContentViewResId != NULL_RESOURCE_ID) {
250 | mContentView = mInflater.inflate(mContentViewResId, null);
251 | addView(mContentView, 0, DEFAULT_LAYOUT_PARAMS);
252 | }
253 | showContentView();
254 | }
255 |
256 | private void showContentView() {
257 | final int childCount = getChildCount();
258 | for (int i = 0; i < childCount; i++) {
259 | View view = getChildAt(i);
260 | view.setVisibility(mOtherIds.contains(view.getId()) ? View.GONE : View.VISIBLE);
261 | }
262 | }
263 |
264 | private View inflateView(int layoutId) {
265 | return mInflater.inflate(layoutId, null);
266 | }
267 |
268 | private void showViewById(int viewId) {
269 | final int childCount = getChildCount();
270 | for (int i = 0; i < childCount; i++) {
271 | View view = getChildAt(i);
272 | view.setVisibility(view.getId() == viewId ? View.VISIBLE : View.GONE);
273 | }
274 | }
275 |
276 | private void checkNull(Object object, String hint) {
277 | if (null == object) {
278 | throw new NullPointerException(hint);
279 | }
280 | }
281 |
282 | private void clear(View... views) {
283 | if (null == views) {
284 | return;
285 | }
286 | try {
287 | for (View view : views) {
288 | if (null != view) {
289 | removeView(view);
290 | }
291 | }
292 | } catch (Exception e) {
293 | e.printStackTrace();
294 | }
295 | }
296 | }
297 |
--------------------------------------------------------------------------------
/multiple-status-view/src/main/res/layout/empty_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
--------------------------------------------------------------------------------
/multiple-status-view/src/main/res/layout/error_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
--------------------------------------------------------------------------------
/multiple-status-view/src/main/res/layout/loading_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
13 |
--------------------------------------------------------------------------------
/multiple-status-view/src/main/res/layout/no_network_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
--------------------------------------------------------------------------------
/multiple-status-view/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/multiple-status-view/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/multiple-status-view/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | multiple-status-view
3 |
4 | 暂无数据
5 | 加载失败
6 | 网络异常
7 |
8 |
--------------------------------------------------------------------------------
/multiple-status-view/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app','common_lib', ':multiple-status-view'
2 |
--------------------------------------------------------------------------------