├── .gitignore
├── .idea
├── caches
│ └── build_file_checksums.ser
├── codeStyles
│ └── Project.xml
├── gradle.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── kotlingank.jks
├── proguard-rules.pro
├── release
│ ├── kotlinmvp_v1.1.0_release.apk
│ └── output.json
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── jaygengi
│ │ └── gank
│ │ ├── Constants.kt
│ │ ├── Extensions.kt
│ │ ├── MyApplication.kt
│ │ ├── api
│ │ ├── ApiService.kt
│ │ └── UriConstant.kt
│ │ ├── base
│ │ ├── BaseActivity.kt
│ │ ├── BaseFragment.kt
│ │ ├── BasePresenter.kt
│ │ ├── IBaseView.kt
│ │ └── IPresenter.kt
│ │ ├── mvp
│ │ ├── contract
│ │ │ ├── CategoryContract.kt
│ │ │ ├── GirlsContract.kt
│ │ │ └── HomeContract.kt
│ │ ├── model
│ │ │ ├── CategoryModel.kt
│ │ │ ├── GirlsModel.kt
│ │ │ ├── Main.java
│ │ │ ├── ToDayModel.kt
│ │ │ └── bean
│ │ │ │ ├── CategoryEntity.kt
│ │ │ │ ├── GirlsEntity.kt
│ │ │ │ ├── MySectionEntity.kt
│ │ │ │ ├── TabEntity.kt
│ │ │ │ └── ToDayEntity.kt
│ │ └── presenter
│ │ │ ├── CategoryPresenter.kt
│ │ │ ├── GirlsPresenter.kt
│ │ │ └── HomePresenter.kt
│ │ ├── net
│ │ ├── BaseResponse.kt
│ │ ├── RetrofitManager.kt
│ │ └── exception
│ │ │ ├── ApiException.kt
│ │ │ ├── ErrorStatus.kt
│ │ │ └── ExceptionHandle.kt
│ │ ├── scheduler
│ │ ├── BaseScheduler.kt
│ │ ├── ComputationMainScheduler.kt
│ │ ├── IoMainScheduler.kt
│ │ ├── NewThreadMainScheduler.kt
│ │ ├── SchedulerUtils.kt
│ │ ├── SingleMainScheduler.kt
│ │ └── TrampolineMainScheduler.kt
│ │ ├── ui
│ │ ├── activity
│ │ │ ├── MainActivity.kt
│ │ │ ├── SearchActivity.kt
│ │ │ ├── SplashActivity.kt
│ │ │ └── WebViewActivity.kt
│ │ ├── adapter
│ │ │ ├── GirlsAdapter.kt
│ │ │ ├── HomePageAdapter.kt
│ │ │ ├── ToDayAndroidAdapter.kt
│ │ │ └── ToDaySectionAdapter.kt
│ │ └── fragment
│ │ │ ├── GankTypeFragment.kt
│ │ │ ├── HomeFragment.kt
│ │ │ ├── MineFragment.kt
│ │ │ └── home
│ │ │ ├── CategoryFragment.kt
│ │ │ └── WelfareFragment.kt
│ │ ├── utils
│ │ ├── AppUtils.kt
│ │ ├── CleanLeakUtils.kt
│ │ ├── DensityUtil.java
│ │ ├── DisplayManager.kt
│ │ ├── NetworkUtil.kt
│ │ ├── Preference.kt
│ │ ├── RxUtil.java
│ │ ├── StatusBarUtil.kt
│ │ ├── WatchHistoryUtils.kt
│ │ └── img
│ │ │ └── GlideImageLoader.java
│ │ └── widget
│ │ ├── SlidingTabLayout.java
│ │ └── SlidingTabStrip.java
│ └── res
│ ├── anim
│ ├── anim_in.xml
│ ├── anim_out.xml
│ ├── push_bottom_in.xml
│ └── push_bottom_out.xml
│ ├── drawable
│ ├── ic_left.xml
│ ├── line_h.xml
│ └── shape_item.xml
│ ├── layout
│ ├── activity_main.xml
│ ├── activity_search.xml
│ ├── activity_splash.xml
│ ├── activity_webview.xml
│ ├── fragment_app.xml
│ ├── fragment_gank_type.xml
│ ├── fragment_home.xml
│ ├── fragment_home_common_type.xml
│ ├── fragment_mine.xml
│ ├── fragment_welfare.xml
│ ├── item_category.xml
│ ├── item_category_info.xml
│ ├── item_grils_info.xml
│ ├── item_home_today.xml
│ ├── item_home_today_img.xml
│ ├── item_today_header.xml
│ ├── layout_empty_view.xml
│ ├── layout_error_view.xml
│ ├── layout_loading_view.xml
│ └── layout_network_view.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ ├── ic_action_clear.png
│ ├── ic_action_search_black.png
│ ├── ic_action_search_small.png
│ ├── ic_action_search_white.png
│ ├── ic_classification_normal.png
│ ├── ic_classification_selected.png
│ ├── ic_error.png
│ ├── ic_home_normal.png
│ ├── ic_home_selected.png
│ ├── ic_mine_normal.png
│ ├── ic_mine_selected.png
│ ├── ic_no_data.png
│ ├── ic_no_network.png
│ └── img_jaygengi_avatar.jpg
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── transition-v21
│ └── arc_motion.xml
│ ├── values-v19
│ └── styles.xml
│ └── values
│ ├── attrs.xml
│ ├── colors.xml
│ ├── dimens.xml
│ ├── ids.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── 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
└── show
├── gank_type.png
├── gankapp.gif
├── home.png
├── img.png
├── mine.png
└── type.png
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/.idea/caches/build_file_checksums.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/.idea/caches/build_file_checksums.ser
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
18 |
19 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.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 | # KotlinGankApp客户端
2 |
3 | 基于 **MVP** 结构,使用 Kotlin 语言开发和借助 [Gank.io](https://gank.io/) 提供的API开发的一个客户端
4 |
5 | Kotlin+MVP的框架思路来源于 [git-xuhao](https://github.com/git-xuhao/KotlinMvp)。
6 |
7 | [APK下载地址](https://github.com/JayGengi/KotlinGankApp/blob/master/app/release/kotlinmvp_v1.1.0_release.apk)
8 |
9 | ## 前言
10 |
11 | 作为Google主要力推的Kotlin开发语言,早已心痒,跃跃欲试。对于使用过程中,方法和函数的便捷使用和扩展插件的方便。值的大家尝试一波
12 |
13 |
14 | ### 界面截图
15 |
16 |
17 |

18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions' //扩展插件
4 | apply plugin: 'kotlin-kapt' //kapt3插件
5 |
6 | android {
7 | signingConfigs {
8 | // config {
9 | // keyAlias 'kotlingank'
10 | // keyPassword '123456'
11 | // storeFile file('../kotlingank.jks')
12 | // storePassword '123456'
13 | // }
14 | }
15 | compileSdkVersion 27
16 | buildToolsVersion "28.0.3"
17 | defaultConfig {
18 | applicationId "com.jaygengi.gank"
19 | minSdkVersion 18
20 | targetSdkVersion 27
21 | versionCode 1
22 | versionName "1.1.0"
23 | javaCompileOptions {
24 | annotationProcessorOptions {
25 | includeCompileClasspath true
26 | }
27 | }
28 | // signingConfig signingConfigs.config
29 | // 实现毛玻璃那种透明的效果需要添加的库
30 | renderscriptTargetApi 19
31 | renderscriptSupportModeEnabled true // Enable RS support
32 |
33 | ndk {
34 | //APP的build.gradle设置支持的SO库架构
35 | abiFilters 'armeabi', 'armeabi-v7a', 'x86'
36 | }
37 | multiDexEnabled true
38 | }
39 | buildTypes {
40 | debug {
41 | minifyEnabled false
42 | debuggable true
43 | // signingConfig signingConfigs.config
44 | }
45 | release {
46 | minifyEnabled false
47 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
48 | debuggable false
49 | // signingConfig signingConfigs.config
50 | zipAlignEnabled true
51 | }
52 | }
53 | sourceSets {
54 | main {
55 | jni.srcDirs = []
56 | jniLibs.srcDirs = ['libs']
57 | }
58 | }
59 | // 自定义输出配置
60 | android.applicationVariants.all { variant ->
61 | variant.outputs.all {
62 | outputFileName = "kotlinmvp_v${variant.versionName}_${variant.name}.apk"
63 | }
64 | }
65 | compileOptions {
66 | targetCompatibility JavaVersion.VERSION_1_8
67 | sourceCompatibility JavaVersion.VERSION_1_8
68 | }
69 | productFlavors {
70 |
71 | }
72 |
73 | dexOptions {
74 | jumboMode true
75 | }
76 | lintOptions {
77 | abortOnError false
78 | }
79 | }
80 |
81 | dependencies {
82 | implementation fileTree(include: ['*.jar'], dir: 'libs')
83 | //kotlin 支持库
84 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
85 |
86 | // Support库
87 | implementation 'com.android.support:support-v4:27.1.1'
88 | implementation 'com.android.support:appcompat-v7:27.1.1'
89 | implementation 'com.android.support:cardview-v7:27.1.1'
90 | implementation 'com.android.support:recyclerview-v7:27.1.1'
91 | implementation 'com.android.support:design:27.1.1'
92 |
93 | implementation 'com.android.support.constraint:constraint-layout:2.0.0-alpha2'
94 |
95 | implementation 'com.android.support:multidex:1.0.3'
96 |
97 | //qmui
98 | implementation 'com.qmuiteam:qmui:1.1.6'
99 | // 底部菜单
100 | implementation('com.flyco.tablayout:FlycoTabLayout_Lib:2.1.0@aar') {
101 | exclude group: 'com.android.support', module: 'support-v4'
102 | }
103 |
104 | // Retrofit
105 | implementation 'com.squareup.retrofit2:retrofit:2.4.0'
106 | implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
107 | implementation 'com.squareup.retrofit2:converter-scalars:2.4.0'
108 | implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
109 | //rxjava2
110 | implementation 'io.reactivex.rxjava2:rxjava:2.2.2'
111 | implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
112 |
113 | //Retrofit依赖OkHttp来进行网络请求 日志拦截器
114 | implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
115 |
116 | ////BaseQuickAdapter[很赞的适配器封装]
117 | implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.40'
118 | //smartRefreshLayout 下拉刷新
119 | implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.0.5.1'
120 | implementation 'com.scwang.smartrefresh:SmartRefreshHeader:1.0.3'
121 |
122 | implementation project(':multiple-status-view')
123 | //模糊透明 View
124 | implementation 'com.github.mmin18:realtimeblurview:1.1.0'
125 |
126 | //Logger
127 | implementation 'com.orhanobut:logger:2.1.1'
128 | //leakCanary 内存泄漏自检
129 | debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.1'
130 | releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1'
131 |
132 | //banner
133 | implementation 'com.youth.banner:banner:1.4.10'
134 | implementation 'com.github.SherlockGougou:BigImageViewPager:v4_2.1.4'
135 |
136 | //glide
137 | implementation 'com.github.bumptech.glide:glide:4.8.0'
138 | annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
139 | implementation 'com.github.bumptech.glide:okhttp3-integration:4.8.0'
140 | //WebView
141 | implementation 'com.github.delight-im:Android-AdvancedWebView:v3.0.0'
142 | //今日头条屏幕适配
143 | // implementation 'me.jessyan:autosize:1.0.6'
144 | }
145 |
--------------------------------------------------------------------------------
/app/kotlingank.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/app/kotlingank.jks
--------------------------------------------------------------------------------
/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 |
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 |
--------------------------------------------------------------------------------
/app/release/kotlinmvp_v1.1.0_release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/app/release/kotlinmvp_v1.1.0_release.apk
--------------------------------------------------------------------------------
/app/release/output.json:
--------------------------------------------------------------------------------
1 | [{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.1.0","enabled":true,"outputFile":"kotlinmvp_v1.1.0_release.apk","fullName":"release","baseName":"release"},"path":"kotlinmvp_v1.1.0_release.apk","properties":{}}]
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
18 |
19 |
20 |
21 |
22 |
32 |
33 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
50 |
51 |
54 |
55 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/Constants.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank
2 |
3 | // ┏┓ ┏┓
4 | //┏┛┻━━━┛┻┓
5 | //┃ ┃
6 | //┃ ━ ┃
7 | //┃ ┳┛ ┗┳ ┃
8 | //┃ ┃
9 | //┃ ┻ ┃
10 | //┃ ┃
11 | //┗━┓ ┏━┛
12 | // ┃ ┃ 神兽保佑
13 | // ┃ ┃ 代码无BUG!
14 | // ┃ ┗━━━┓
15 | // ┃ ┣┓
16 | // ┃ ┏┛
17 | // ┗┓┓┏━┳┓┏┛
18 | // ┃┫┫ ┃┫┫
19 | // ┗┻┛ ┗┻┛
20 | /**
21 | * Created by xuhao on 2017/11/27.
22 | * desc: 常量
23 | */
24 | class Constants private constructor() {
25 |
26 | companion object {
27 |
28 | }
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank
2 |
3 | import android.content.Context
4 | import android.support.v4.app.Fragment
5 | import android.view.View
6 | import android.widget.Toast
7 | import com.jaygengi.gank.MyApplication
8 |
9 | /**
10 | * Created by xuhao on 2017/11/14.
11 | */
12 |
13 | fun Fragment.showToast(content: String): Toast {
14 | val toast = Toast.makeText(this.activity?.applicationContext, content, Toast.LENGTH_SHORT)
15 | toast.show()
16 | return toast
17 | }
18 |
19 | fun Context.showToast(content: String): Toast {
20 | val toast = Toast.makeText(MyApplication.context, content, Toast.LENGTH_SHORT)
21 | toast.show()
22 | return toast
23 | }
24 |
25 |
26 | fun View.dip2px(dipValue: Float): Int {
27 | val scale = this.resources.displayMetrics.density
28 | return (dipValue * scale + 0.5f).toInt()
29 | }
30 |
31 | fun View.px2dip(pxValue: Float): Int {
32 | val scale = this.resources.displayMetrics.density
33 | return (pxValue / scale + 0.5f).toInt()
34 | }
35 |
36 | fun durationFormat(duration: Long?): String {
37 | val minute = duration!! / 60
38 | val second = duration % 60
39 | return if (minute <= 9) {
40 | if (second <= 9) {
41 | "0$minute' 0$second''"
42 | } else {
43 | "0$minute' $second''"
44 | }
45 | } else {
46 | if (second <= 9) {
47 | "$minute' 0$second''"
48 | } else {
49 | "$minute' $second''"
50 | }
51 | }
52 | }
53 |
54 | /**
55 | * 数据流量格式化
56 | */
57 | fun Context.dataFormat(total: Long): String {
58 | var result: String
59 | var speedReal: Int = (total / (1024)).toInt()
60 | result = if (speedReal < 512) {
61 | speedReal.toString() + " KB"
62 | } else {
63 | val mSpeed = speedReal / 1024.0
64 | (Math.round(mSpeed * 100) / 100.0).toString() + " MB"
65 | }
66 | return result
67 | }
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/MyApplication.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank
2 |
3 | import android.app.Activity
4 | import android.app.Application
5 | import android.content.Context
6 | import android.os.Bundle
7 | import android.support.multidex.MultiDexApplication
8 | import android.util.Log
9 | import com.jaygengi.gank.utils.DisplayManager
10 | import com.orhanobut.logger.AndroidLogAdapter
11 | import com.orhanobut.logger.Logger
12 | import com.orhanobut.logger.PrettyFormatStrategy
13 | import com.scwang.smartrefresh.layout.SmartRefreshLayout
14 | import com.scwang.smartrefresh.layout.constant.SpinnerStyle
15 | import com.scwang.smartrefresh.layout.footer.ClassicsFooter
16 | import com.scwang.smartrefresh.layout.header.ClassicsHeader
17 | import com.squareup.leakcanary.LeakCanary
18 | import com.squareup.leakcanary.RefWatcher
19 | import kotlin.properties.Delegates
20 |
21 |
22 | public class MyApplication : MultiDexApplication(){
23 | private var refWatcher: RefWatcher? = null
24 |
25 | companion object {
26 |
27 | private val TAG = "MyApplication"
28 |
29 | var context: Context by Delegates.notNull()
30 | private set
31 |
32 | fun getRefWatcher(context: Context): RefWatcher? {
33 | val myApplication = context.applicationContext as MyApplication
34 | return myApplication.refWatcher
35 | }
36 |
37 | }
38 |
39 | override fun onCreate() {
40 | super.onCreate()
41 | context = applicationContext
42 | // refWatcher = setupLeakCanary()
43 | initConfig()
44 | DisplayManager.init(this)
45 | registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks)
46 |
47 |
48 | }
49 |
50 | private fun setupLeakCanary(): RefWatcher {
51 | return if (LeakCanary.isInAnalyzerProcess(this)) {
52 | RefWatcher.DISABLED
53 | } else LeakCanary.install(this)
54 | }
55 |
56 | /**
57 | * 初始化配置
58 | */
59 | private fun initConfig() {
60 |
61 | val formatStrategy = PrettyFormatStrategy.newBuilder()
62 | .showThreadInfo(false) // 隐藏线程信息 默认:显示
63 | .methodCount(0) // 决定打印多少行(每一行代表一个方法)默认:2
64 | .methodOffset(7) // (Optional) Hides internal method calls up to offset. Default 5
65 | .tag("jaygengi") // (Optional) Global tag for every log. Default PRETTY_LOGGER
66 | .build()
67 | Logger.addLogAdapter(object : AndroidLogAdapter(formatStrategy) {
68 | override fun isLoggable(priority: Int, tag: String?): Boolean {
69 | return BuildConfig.DEBUG
70 | }
71 | })
72 | }
73 |
74 |
75 | private val mActivityLifecycleCallbacks = object : Application.ActivityLifecycleCallbacks {
76 | override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
77 | Log.d(TAG, "onCreated: " + activity.componentName.className)
78 | }
79 |
80 | override fun onActivityStarted(activity: Activity) {
81 | Log.d(TAG, "onStart: " + activity.componentName.className)
82 | }
83 |
84 | override fun onActivityResumed(activity: Activity) {
85 |
86 | }
87 |
88 | override fun onActivityPaused(activity: Activity) {
89 |
90 | }
91 |
92 | override fun onActivityStopped(activity: Activity) {
93 |
94 | }
95 |
96 | override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
97 |
98 | }
99 |
100 | override fun onActivityDestroyed(activity: Activity) {
101 | Log.d(TAG, "onDestroy: " + activity.componentName.className)
102 | }
103 | }
104 | /**
105 | * 上拉加载,下拉刷新
106 | * static 代码段可以防止内存泄露
107 | */
108 | init {
109 | //设置全局的Header构建器
110 | SmartRefreshLayout.setDefaultRefreshHeaderCreator { context, layout ->
111 | //全局设置主题颜色
112 | // layout.setPrimaryColorsId(R.color.common_color_line, R.color.qmui_config_color_black)
113 | //指定为经典Header,默认是 贝塞尔雷达Header
114 | ClassicsHeader(context).setSpinnerStyle(SpinnerStyle.Translate)
115 | }
116 | //设置全局的Footer构建器
117 | SmartRefreshLayout.setDefaultRefreshFooterCreator { context, layout ->
118 | // layout.setPrimaryColorsId(R.color.common_color_line, R.color.qmui_config_color_black)
119 | //指定为经典Footer,默认是 BallPulseFooter
120 | ClassicsFooter(context).setSpinnerStyle(SpinnerStyle.Translate)
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/api/ApiService.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.api
2 |
3 | import com.jaygengi.gank.mvp.model.bean.CategoryEntity
4 | import com.jaygengi.gank.mvp.model.bean.GirlsEntity
5 | import com.jaygengi.gank.mvp.model.bean.ToDayEntity
6 | import com.jaygengi.gank.net.BaseResponse
7 | import io.reactivex.Observable
8 | import retrofit2.http.GET
9 | import retrofit2.http.Path
10 |
11 | /**
12 | * @description: Api 接口
13 | * @author JayGengi
14 | * @date 2018/11/13 0013 下午 2:01
15 | * @email jaygengiii@gmail.com
16 | */
17 |
18 | interface ApiService{
19 |
20 | /**
21 | * 首页精选
22 | */
23 | @GET(UrlConstant.BASE_URL+"data/福利/{limit}/{page}")
24 | fun getGirlsInfo(@Path("limit")limit:Int,
25 | @Path("page")page:Int): Observable
26 |
27 | /**
28 | * 获取福利数据信息
29 | */
30 | @GET(UrlConstant.BASE_URL+"today")
31 | fun getToDayInfo(): Observable
32 |
33 | /**
34 | * 技术干货分类文章
35 | */
36 | @GET(UrlConstant.BASE_URL+"search/query/listview/category/{key}/count/{limit}/page/{page} ")
37 | fun getCategoryInfo(@Path("key")key:String,
38 | @Path("limit")limit:Int,
39 | @Path("page")page:Int): Observable
40 |
41 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/api/UriConstant.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.api
2 |
3 | /**
4 | * @description: UrlConstant
5 | * @author JayGengi
6 | * @date 2018/11/13 0013 上午 10:28
7 | * @email jaygengiii@gmail.com
8 | */
9 | object UrlConstant{
10 |
11 | const val BASE_URL = "http://gank.io/api/"
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/base/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.base
2 |
3 | import android.os.Bundle
4 | import android.support.v7.app.AppCompatActivity
5 | import android.util.Log
6 | import android.view.View
7 | import android.view.Window
8 | import android.view.WindowManager
9 | import android.widget.Toast
10 | import com.classic.common.MultipleStatusView
11 | import com.jaygengi.gank.MyApplication
12 | import com.qmuiteam.qmui.util.QMUIStatusBarHelper
13 | import io.reactivex.annotations.NonNull
14 |
15 |
16 | /**
17 | * @description: BaseActivity基类
18 | * @author JayGengi
19 | * @date 2018/10/29 0029 上午 11:57
20 | * @email jaygengiii@gmail.com
21 | */
22 | abstract class BaseActivity : AppCompatActivity() {
23 | /**
24 | * 多种状态的 View 的切换
25 | */
26 | protected var mLayoutStatusView: MultipleStatusView? = null
27 |
28 | override fun onCreate(savedInstanceState: Bundle?) {
29 | super.onCreate(savedInstanceState)
30 | QMUIStatusBarHelper.translucent(this) // 沉浸式状态栏
31 | QMUIStatusBarHelper.setStatusBarLightMode(this)
32 | setContentView(layoutId())
33 | initData()
34 | initView()
35 | start()
36 | initListener()
37 |
38 |
39 | }
40 |
41 | private fun initListener() {
42 | mLayoutStatusView?.setOnClickListener(mRetryClickListener)
43 | }
44 |
45 | open val mRetryClickListener: View.OnClickListener = View.OnClickListener {
46 | start()
47 | }
48 |
49 |
50 | /**
51 | * 加载布局
52 | */
53 | abstract fun layoutId(): Int
54 |
55 | /**
56 | * 初始化数据
57 | */
58 | abstract fun initData()
59 |
60 | /**
61 | * 初始化 View
62 | */
63 | abstract fun initView()
64 |
65 | /**
66 | * 开始请求
67 | */
68 | abstract fun start()
69 |
70 | override fun onDestroy() {
71 | super.onDestroy()
72 | MyApplication.getRefWatcher(this)?.watch(this)
73 | }
74 |
75 | }
76 |
77 |
78 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/base/BaseFragment.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.base
2 |
3 | import android.os.Bundle
4 | import android.support.annotation.LayoutRes
5 | import android.support.v4.app.Fragment
6 | import android.view.LayoutInflater
7 | import android.view.View
8 | import android.view.ViewGroup
9 | import com.classic.common.MultipleStatusView
10 | import com.jaygengi.gank.MyApplication
11 |
12 | /**
13 | * @description: Fragment基类
14 | * @author JayGengi
15 | * @date 2018/11/13 0013 上午 10:35
16 | * @email jaygengiii@gmail.com
17 | */
18 |
19 | abstract class BaseFragment: Fragment(){
20 | /**
21 | * 当前页数
22 | */
23 | var CURRENT_PAGE = 1
24 | /**
25 | * 每页容量- 每页有多少条记录
26 | */
27 | val PAGE_CAPACITY = 10
28 | /**
29 | * 视图是否加载完毕
30 | */
31 | private var isViewPrepare = false
32 | /**
33 | * 数据是否加载过了
34 | */
35 | private var hasLoadData = false
36 | /**
37 | * 多种状态的 View 的切换
38 | */
39 | protected var mLayoutStatusView: MultipleStatusView? = null
40 |
41 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
42 | return inflater.inflate(getLayoutId(),null)
43 | }
44 |
45 |
46 |
47 | override fun setUserVisibleHint(isVisibleToUser: Boolean) {
48 | super.setUserVisibleHint(isVisibleToUser)
49 | if (isVisibleToUser) {
50 | lazyLoadDataIfPrepared()
51 | }
52 | }
53 |
54 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
55 | super.onViewCreated(view, savedInstanceState)
56 | isViewPrepare = true
57 | initView()
58 | lazyLoadDataIfPrepared()
59 | //多种状态切换的view 重试点击事件
60 | mLayoutStatusView?.setOnClickListener(mRetryClickListener)
61 | }
62 |
63 | private fun lazyLoadDataIfPrepared() {
64 | if (userVisibleHint && isViewPrepare && !hasLoadData) {
65 | lazyLoad()
66 | hasLoadData = true
67 | }
68 | }
69 |
70 | open val mRetryClickListener: View.OnClickListener = View.OnClickListener {
71 | lazyLoad()
72 | }
73 |
74 |
75 | /**
76 | * 加载布局
77 | */
78 | @LayoutRes
79 | abstract fun getLayoutId():Int
80 |
81 | /**
82 | * 初始化 ViewI
83 | */
84 | abstract fun initView()
85 |
86 | /**
87 | * 懒加载
88 | */
89 | abstract fun lazyLoad()
90 |
91 | override fun onDestroy() {
92 | super.onDestroy()
93 | activity?.let { MyApplication.getRefWatcher(it)?.watch(activity) }
94 | }
95 |
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/base/BasePresenter.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.base
2 |
3 | import io.reactivex.disposables.CompositeDisposable
4 | import io.reactivex.disposables.Disposable
5 |
6 |
7 |
8 | /**
9 | * Created by xuhao on 2017/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 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/base/IBaseView.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.base
2 |
3 | /**
4 | * @description: View基类
5 | * @author JayGengi
6 | * @date 2018/11/13 0013 上午 10:35
7 | * @email jaygengiii@gmail.com
8 | */
9 | interface IBaseView {
10 |
11 | fun showLoading()
12 |
13 | fun dismissLoading()
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/base/IPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.base
2 |
3 |
4 |
5 | /**
6 | * @description: Presenterj基类
7 | * @author JayGengi
8 | * @date 2018/11/13 0013 上午 10:35
9 | * @email jaygengiii@gmail.com
10 | */
11 |
12 |
13 | interface IPresenter {
14 |
15 | fun attachView(mRootView: V)
16 |
17 | fun detachView()
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/mvp/contract/CategoryContract.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.mvp.contract
2 |
3 | import com.jaygengi.gank.base.IBaseView
4 | import com.jaygengi.gank.base.IPresenter
5 | import com.jaygengi.gank.mvp.model.bean.CategoryEntity
6 |
7 | /**
8 | * @description: 契约类
9 | * @author JayGengi
10 | * @date 2018/11/13 0013 上午 10:35
11 | * @email jaygengiii@gmail.com
12 | */
13 |
14 | interface CategoryContract {
15 |
16 | interface View : IBaseView {
17 |
18 | /**
19 | * 获取最新一天的干货
20 | */
21 | fun getCategoryInfo(todayInfo: CategoryEntity)
22 | /**
23 | * 显示错误信息
24 | */
25 | fun showError(msg: String,errorCode:Int)
26 |
27 |
28 | }
29 |
30 | interface Presenter : IPresenter {
31 |
32 | /**
33 | * 获取最新一天的干货
34 | */
35 | fun requestCategoryInfo(key:String,limit: Int,page: Int)
36 |
37 |
38 | }
39 |
40 |
41 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/mvp/contract/GirlsContract.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.mvp.contract
2 |
3 | import com.jaygengi.gank.base.IBaseView
4 | import com.jaygengi.gank.base.IPresenter
5 | import com.jaygengi.gank.mvp.model.bean.GirlsEntity
6 | import com.jaygengi.gank.mvp.model.bean.ToDayEntity
7 | import com.jaygengi.gank.net.BaseResponse
8 |
9 | /**
10 | * @description: 契约类
11 | * @author JayGengi
12 | * @date 2018/11/13 0013 上午 10:35
13 | * @email jaygengiii@gmail.com
14 | */
15 |
16 | interface GirlsContract {
17 |
18 | interface View : IBaseView {
19 |
20 | /**
21 | * 显示福利轮播图
22 | */
23 | fun showGirlInfo(dataInfo: GirlsEntity)
24 |
25 | /**
26 | * 显示错误信息
27 | */
28 | fun showError(msg: String,errorCode:Int)
29 |
30 | }
31 |
32 | interface Presenter : IPresenter {
33 |
34 | /**
35 | * 获取福利轮播图
36 | */
37 | fun requestGirlInfo(limit: Int,page: Int)
38 |
39 | }
40 |
41 |
42 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/mvp/contract/HomeContract.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.mvp.contract
2 |
3 | import com.jaygengi.gank.base.IBaseView
4 | import com.jaygengi.gank.base.IPresenter
5 | import com.jaygengi.gank.mvp.model.bean.GirlsEntity
6 | import com.jaygengi.gank.mvp.model.bean.ToDayEntity
7 | import com.jaygengi.gank.net.BaseResponse
8 |
9 | /**
10 | * @description: 契约类
11 | * @author JayGengi
12 | * @date 2018/11/13 0013 上午 10:35
13 | * @email jaygengiii@gmail.com
14 | */
15 |
16 | interface HomeContract {
17 |
18 | interface View : IBaseView {
19 | /**
20 | * 显示福利轮播图
21 | */
22 | fun showGirlInfo(dataInfo: GirlsEntity)
23 | /**
24 | * 获取最新一天的干货
25 | */
26 | fun showToDayInfo(todayInfo: ToDayEntity)
27 | /**
28 | * 显示错误信息
29 | */
30 | fun showError(msg: String,errorCode:Int)
31 |
32 | }
33 |
34 | interface Presenter : IPresenter {
35 |
36 | /**
37 | * 获取福利轮播图
38 | */
39 | fun requestGirlInfo()
40 | /**
41 | * 获取最新一天的干货
42 | */
43 | fun requestToDayInfo()
44 | }
45 |
46 |
47 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/mvp/model/CategoryModel.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.mvp.model
2 |
3 | import com.jaygengi.gank.mvp.model.bean.CategoryEntity
4 | import com.jaygengi.gank.net.RetrofitManager
5 | import com.jaygengi.gank.scheduler.SchedulerUtils
6 | import io.reactivex.Observable
7 |
8 | /**
9 | * @description: 获取最新一天的干货数据模型
10 | * @author JayGengi
11 | * @date 2018/11/14 0014 下午 4:33
12 | * @email jaygengiii@gmail.com
13 | */
14 | class CategoryModel {
15 |
16 |
17 | /**
18 | * 获取福利数据信息
19 | */
20 | fun getCategoryInfo(key:String,limit: Int,page: Int): Observable {
21 | return RetrofitManager.service.getCategoryInfo(key,limit,page)
22 | .compose(SchedulerUtils.ioToMain())
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/mvp/model/GirlsModel.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.mvp.model
2 |
3 | import com.jaygengi.gank.mvp.model.bean.GirlsEntity
4 | import com.jaygengi.gank.net.BaseResponse
5 | import com.jaygengi.gank.net.RetrofitManager
6 | import com.jaygengi.gank.scheduler.SchedulerUtils
7 | import io.reactivex.Observable
8 |
9 | /**
10 | * @description: 福利数据模型
11 | * @author JayGengi
12 | * @date 2018/11/13 0013 下午 3:35
13 | * @email jaygengiii@gmail.com
14 | */
15 | class GirlsModel {
16 |
17 |
18 | /**
19 | * 获取福利数据信息
20 | */
21 | fun getGirlsInfo(limit: Int,page: Int): Observable {
22 | return RetrofitManager.service.getGirlsInfo(limit,page)
23 | .compose(SchedulerUtils.ioToMain())
24 | }
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/mvp/model/Main.java:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.mvp.model;
2 |
3 | public class Main {
4 | }
5 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/mvp/model/ToDayModel.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.mvp.model
2 |
3 | import com.jaygengi.gank.mvp.model.bean.ToDayEntity
4 | import com.jaygengi.gank.net.RetrofitManager
5 | import com.jaygengi.gank.scheduler.SchedulerUtils
6 | import io.reactivex.Observable
7 |
8 | /**
9 | * @description: 获取最新一天的干货数据模型
10 | * @author JayGengi
11 | * @date 2018/11/14 0014 下午 4:33
12 | * @email jaygengiii@gmail.com
13 | */
14 | class ToDayModel {
15 |
16 |
17 | /**
18 | * 获取福利数据信息
19 | */
20 | fun getToDayInfo(): Observable {
21 | return RetrofitManager.service.getToDayInfo()
22 | .compose(SchedulerUtils.ioToMain())
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/mvp/model/bean/CategoryEntity.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.mvp.model.bean
2 |
3 | class CategoryEntity {
4 |
5 |
6 | /**
7 | * count : 10
8 | * error : false
9 | * results : [{"desc":"The Best DevOps Tools on OSX","ganhuo_id":"56cc6d22421aa95caa70793a","publishedAt":"2016-02-26T11:58:00.331000","readability":"","type":"iOS","url":"https://dzone.com/articles/the-best-devops-tools-on-osx","who":"Andrew Liu"},{"desc":"加载网络图片第三方库ssloadURLImage","ganhuo_id":"56cc6d23421aa95caa707951","publishedAt":"2015-10-21T02:57:40.909000","readability":"","type":"iOS","url":"https://github.com/shareJOBS/ssloadURLImage","who":"Andrew Liu"},{"desc":"强制修改 iOS 状态栏颜色","ganhuo_id":"56cc6d23421aa95caa70795b","publishedAt":"2015-10-22T02:06:07.739000","readability":"","type":"iOS","url":"http://www.jianshu.com/p/9d6b6f790493","who":"Andrew Liu"},{"desc":"Thoughts on Functional Programming in Swift","ganhuo_id":"56cc6d23421aa95caa7079a4","publishedAt":"2015-11-16T03:55:17.189000","readability":"","type":"iOS","url":"http://natashatherobot.com/functional-programming-in-swift/#","who":"CallMeWhy"},{"desc":"Swift Scripting By Example","ganhuo_id":"56cc6d23421aa95caa7079ba","publishedAt":"2015-11-20T03:54:49.808000","readability":"","type":"iOS","url":"http://swift.ayaka.me/posts/2015/11/5/swift-scripting-generating-acknowledgements-for-cocoapods-and-carthage-dependencies","who":"CallMeWhy"},{"desc":"通过 RAC 的 RACSignal 将 iOS 的任务转变为后台任务","ganhuo_id":"56cc6d23421aa95caa7079c7","publishedAt":"2015-05-18T03:52:44.016000","readability":"","type":"iOS","url":"http://spin.atomicobject.com/2015/05/14/ios-background-task-reactivecocoa/#.VVSask7KdL8.hackernews","who":"CallMeWhy"},{"desc":"iOS 富文本控件","ganhuo_id":"56cc6d23421aa95caa7079bf","publishedAt":"2015-11-18T05:17:57.241000","readability":"","type":"iOS","url":"https://github.com/ibireme/YYText","who":"mthli"},{"desc":"XCPlayground,是时候展现 Playground 的真正实力了。","ganhuo_id":"56cc6d23421aa95caa7079d3","publishedAt":"2016-03-03T12:12:56.684000","readability":"","type":"iOS","url":"http://nshipster.com/xcplayground/?utm_campaign=This%2BWeek%2Bin%2BSwift&utm_medium=web&utm_source=This_Week_in_Swift_39","who":"CallMeWhy"},{"desc":"AFNetworking 安全bug的回复","ganhuo_id":"56cc6d23421aa95caa7079d8","publishedAt":"2015-05-27T04:21:13.831000","readability":"","type":"iOS","url":"http://www.yming9.com/?p=579","who":"Andrew Liu"},{"desc":"PaintCode教程 译自Ranwenderlich:http://www.raywenderlich.com/100281/paintcode-for-designers-getting-started","ganhuo_id":"56cc6d23421aa95caa7079d9","publishedAt":"2016-03-31T11:44:55.091000","readability":"","type":"iOS","url":"http://www.jianshu.com/p/5e75408812df","who":"Andrew Liu"}]
10 | */
11 |
12 | var count: Int = 0
13 | var isError: Boolean = false
14 | var results: List? = null
15 |
16 | class ResultsBean {
17 | /**
18 | * desc : The Best DevOps Tools on OSX
19 | * ganhuo_id : 56cc6d22421aa95caa70793a
20 | * publishedAt : 2016-02-26T11:58:00.331000
21 | * readability :
22 | * type : iOS
23 | * url : https://dzone.com/articles/the-best-devops-tools-on-osx
24 | * who : Andrew Liu
25 | */
26 |
27 | var desc: String? = null
28 | var ganhuo_id: String? = null
29 | var publishedAt: String? = null
30 | var readability: String? = null
31 | var type: String? = null
32 | var url: String? = null
33 | var who: String? = null
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/mvp/model/bean/GirlsEntity.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.mvp.model.bean
2 |
3 | /**
4 | * @description: 福利 Entity
5 | * @author JayGengi
6 | * @date 2018/11/13 0013 下午 3:35
7 | * @email jaygengiii@gmail.com
8 | */
9 | class GirlsEntity {
10 | /**
11 | * error : false
12 | * results : [{"_id":"5be14edb9d21223dd50660f8","createdAt":"2018-11-06T08:20:43.656Z","desc":"2018-11-06","publishedAt":"2018-11-06T00:00:00.0Z","source":"web","type":"福利","url":"https://ws1.sinaimg.cn/large/0065oQSqgy1fwyf0wr8hhj30ie0nhq6p.jpg","used":true,"who":"lijinshanmx"},{"_id":"5bcd71979d21220315c663fc","createdAt":"2018-10-22T06:43:35.440Z","desc":"2018-10-22","publishedAt":"2018-10-22T00:00:00.0Z","source":"web","type":"福利","url":"https://ws1.sinaimg.cn/large/0065oQSqgy1fwgzx8n1syj30sg15h7ew.jpg","used":true,"who":"lijinshanmx"},{"_id":"5bc434ac9d212279160c4c9e","createdAt":"2018-10-15T06:33:16.497Z","desc":"2018-10-15","publishedAt":"2018-10-15T00:00:00.0Z","source":"web","type":"福利","url":"https://ws1.sinaimg.cn/large/0065oQSqly1fw8wzdua6rj30sg0yc7gp.jpg","used":true,"who":"lijinshanmx"},{"_id":"5bbb0de09d21226111b86f1c","createdAt":"2018-10-08T07:57:20.978Z","desc":"2018-10-08","publishedAt":"2018-10-08T00:00:00.0Z","source":"web","type":"福利","url":"https://ws1.sinaimg.cn/large/0065oQSqly1fw0vdlg6xcj30j60mzdk7.jpg","used":true,"who":"lijinshanmx"},{"_id":"5ba206ec9d2122610aba3440","createdAt":"2018-09-19T08:21:00.295Z","desc":"2018-09-19","publishedAt":"2018-09-19T00:00:00.0Z","source":"web","type":"福利","url":"https://ws1.sinaimg.cn/large/0065oQSqly1fvexaq313uj30qo0wldr4.jpg","used":true,"who":"lijinshanmx"}]
13 | */
14 |
15 | var isError: Boolean = false
16 | var results: List? = null
17 |
18 | class ResultsBean {
19 | /**
20 | * _id : 5be14edb9d21223dd50660f8
21 | * createdAt : 2018-11-06T08:20:43.656Z
22 | * desc : 2018-11-06
23 | * publishedAt : 2018-11-06T00:00:00.0Z
24 | * source : web
25 | * type : 福利
26 | * url : https://ws1.sinaimg.cn/large/0065oQSqgy1fwyf0wr8hhj30ie0nhq6p.jpg
27 | * used : true
28 | * who : lijinshanmx
29 | */
30 |
31 | var _id: String? = null
32 | var createdAt: String? = null
33 | var desc: String? = null
34 | var publishedAt: String? = null
35 | var source: String? = null
36 | var type: String? = null
37 | var url: String? = null
38 | var isUsed: Boolean = false
39 | var who: String? = null
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/mvp/model/bean/MySectionEntity.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.mvp.model.bean
2 |
3 | import com.chad.library.adapter.base.entity.SectionEntity
4 |
5 | /**
6 | * @description: 分组布局 实体类必须继承SectionEntity
7 | * @author JayGengi
8 | * @date 2018/11/14 0014 下午 4:53
9 | * @email jaygengiii@gmail.com
10 | */
11 | class MySectionEntity : SectionEntity {
12 | private val isMore: Boolean = false
13 |
14 | constructor(isHeader: Boolean, header: String) : super(isHeader, header) {}
15 |
16 | constructor(t: ToDayEntity) : super(t) {}
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/mvp/model/bean/TabEntity.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.mvp.model.bean
2 |
3 | import com.flyco.tablayout.listener.CustomTabEntity
4 |
5 |
6 |
7 | /**
8 | * @description: 首页tab entity
9 | * @author JayGengi
10 | * @date 2018/11/13 0013 上午 10:47
11 | * @email jaygengiii@gmail.com
12 | */
13 | class TabEntity(var title: String, private var selectedIcon: Int, private var unSelectedIcon: Int) : CustomTabEntity {
14 |
15 | override fun getTabTitle(): String {
16 | return title
17 | }
18 |
19 | override fun getTabSelectedIcon(): Int {
20 | return selectedIcon
21 | }
22 |
23 | override fun getTabUnselectedIcon(): Int {
24 | return unSelectedIcon
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/mvp/model/bean/ToDayEntity.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.mvp.model.bean
2 |
3 | /**
4 | * @description: 获取最新一天的干货
5 | * @author JayGengi
6 | * @date 2018/11/14 0014 下午 4:33
7 | * @email jaygengiii@gmail.com
8 | */
9 | class ToDayEntity {
10 |
11 | var isError: Boolean = false
12 | var results: ResultsBean? = null
13 | var category: List? = null
14 |
15 | class ResultsBean {
16 | var Android: List? = null
17 | var App: List? = null
18 | var iOS: List? = null
19 | var 休息视频: List? = null
20 | var 前端: List? = null
21 | var 拓展资源: List? = null
22 | var 瞎推荐: List? = null
23 | var 福利: List? = null
24 | class CategoryBean {
25 | /**
26 | * _id : 5bc49bb99d2122791c972ca9
27 | * createdAt : 2018-10-15T13:52:57.103Z
28 | * desc : 新版youtube视频效果
29 | * images : ["https://ww1.sinaimg.cn/large/0073sXn7gy1fwyf8dcpt0g308w0fse83","https://ww1.sinaimg.cn/large/0073sXn7gy1fwyf8gpdc4g308w0fsnpg"]
30 | * publishedAt : 2018-11-06T00:00:00.0Z
31 | * source : web
32 | * type : Android
33 | * url : https://github.com/moyokoo/YoutubeVideoSample
34 | * used : true
35 | * who : miaoyj
36 | */
37 | var _id: String? = null
38 | var createdAt: String? = null
39 | var desc: String? = null
40 | var publishedAt: String? = null
41 | var source: String? = null
42 | var type: String? = null
43 | var url: String? = null
44 | var isUsed: Boolean = false
45 | var who: String? = null
46 | var images: List? = null
47 | }
48 |
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/mvp/presenter/CategoryPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.mvp.presenter
2 |
3 | import com.jaygengi.gank.base.BasePresenter
4 | import com.jaygengi.gank.mvp.contract.CategoryContract
5 | import com.jaygengi.gank.mvp.model.CategoryModel
6 | import com.jaygengi.gank.mvp.model.ToDayModel
7 | import com.jaygengi.gank.net.exception.ExceptionHandle
8 |
9 |
10 | /**
11 | * Created by xuhao on 2017/11/8.
12 | * 首页精选的 Presenter
13 | * (数据是 Banner 数据和一页数据组合而成的 HomeBean,查看接口然后在分析就明白了)
14 | */
15 |
16 | class CategoryPresenter : BasePresenter(), CategoryContract.Presenter {
17 |
18 |
19 | private val categoryModel: CategoryModel by lazy {
20 |
21 | CategoryModel()
22 | }
23 |
24 | override fun requestCategoryInfo(key:String,limit: Int,page: Int) {
25 | checkViewAttached()
26 | mRootView?.showLoading()
27 | val disposable = categoryModel.getCategoryInfo(key,limit,page)
28 | .subscribe({ categoryList ->
29 | mRootView?.apply {
30 | dismissLoading()
31 | if(!categoryList.isError){
32 | getCategoryInfo(categoryList)
33 | }else{
34 | showError(ExceptionHandle.errorMsg, ExceptionHandle.errorCode)
35 | }
36 | }
37 | }, { t ->
38 | mRootView?.apply {
39 | //处理异常
40 | showError(ExceptionHandle.handleException(t), ExceptionHandle.errorCode)
41 | }
42 |
43 | })
44 |
45 | addSubscription(disposable)
46 | }
47 |
48 |
49 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/mvp/presenter/GirlsPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.mvp.presenter
2 |
3 | import com.jaygengi.gank.base.BasePresenter
4 | import com.jaygengi.gank.mvp.contract.GirlsContract
5 | import com.jaygengi.gank.mvp.model.GirlsModel
6 | import com.jaygengi.gank.net.exception.ExceptionHandle
7 |
8 |
9 | /**
10 | * Created by xuhao on 2017/11/8.
11 | * 首页精选的 Presenter
12 | * (数据是 Banner 数据和一页数据组合而成的 HomeBean,查看接口然后在分析就明白了)
13 | */
14 |
15 | class GirlsPresenter : BasePresenter(), GirlsContract.Presenter {
16 |
17 |
18 |
19 | private val girlsModel: GirlsModel by lazy {
20 |
21 | GirlsModel()
22 | }
23 |
24 | override fun requestGirlInfo(limit: Int,page: Int) {
25 | checkViewAttached()
26 | mRootView?.showLoading()
27 | val disposable = girlsModel.getGirlsInfo(limit,page)
28 | .subscribe({ girlsList ->
29 | mRootView?.apply {
30 | dismissLoading()
31 | if(!girlsList.isError){
32 | showGirlInfo(girlsList)
33 | }else{
34 | showError(ExceptionHandle.errorMsg,ExceptionHandle.errorCode)
35 | }
36 | }
37 | }, { t ->
38 | mRootView?.apply {
39 | //处理异常
40 | showError(ExceptionHandle.handleException(t),ExceptionHandle.errorCode)
41 | }
42 |
43 | })
44 |
45 | addSubscription(disposable)
46 | }
47 |
48 |
49 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/mvp/presenter/HomePresenter.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.mvp.presenter
2 |
3 | import com.jaygengi.gank.base.BasePresenter
4 | import com.jaygengi.gank.mvp.contract.HomeContract
5 | import com.jaygengi.gank.mvp.model.GirlsModel
6 | import com.jaygengi.gank.mvp.model.ToDayModel
7 | import com.jaygengi.gank.net.exception.ExceptionHandle
8 |
9 |
10 | /**
11 | * @description: 最新一天的干货
12 | * @author JayGengi
13 | * @date 2018/11/16 0016 上午 11:29
14 | * @email jaygengiii@gmail.com
15 | */
16 |
17 | class HomePresenter : BasePresenter(), HomeContract.Presenter {
18 |
19 | private val girlsModel: GirlsModel by lazy {
20 |
21 | GirlsModel()
22 | }
23 | private val todayModel: ToDayModel by lazy {
24 |
25 | ToDayModel()
26 | }
27 | override fun requestGirlInfo() {
28 | checkViewAttached()
29 | mRootView?.showLoading()
30 | val disposable = girlsModel.getGirlsInfo(1,1)
31 | .subscribe({ girlsList ->
32 | mRootView?.apply {
33 | dismissLoading()
34 | if(!girlsList.isError){
35 | showGirlInfo(girlsList)
36 | }else{
37 | showError(ExceptionHandle.errorMsg,ExceptionHandle.errorCode)
38 | }
39 | }
40 | }, { t ->
41 | mRootView?.apply {
42 | //处理异常
43 | showError(ExceptionHandle.handleException(t),ExceptionHandle.errorCode)
44 | }
45 |
46 | })
47 |
48 | addSubscription(disposable)
49 | }
50 | override fun requestToDayInfo() {
51 | checkViewAttached()
52 | mRootView?.showLoading()
53 | val disposable = todayModel.getToDayInfo()
54 | .subscribe({ todayList ->
55 | mRootView?.apply {
56 | dismissLoading()
57 | if(!todayList.isError){
58 | showToDayInfo(todayList)
59 | }else{
60 | showError(ExceptionHandle.errorMsg,ExceptionHandle.errorCode)
61 | }
62 | }
63 | }, { t ->
64 | mRootView?.apply {
65 | //处理异常
66 | showError(ExceptionHandle.handleException(t),ExceptionHandle.errorCode)
67 | }
68 |
69 | })
70 |
71 | addSubscription(disposable)
72 | }
73 |
74 |
75 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/net/BaseResponse.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.net
2 |
3 | /**
4 | * @description: 封装返回的数据
5 | * @author JayGengi
6 | * @date 2018/11/13 0013 下午 3:31
7 | * @email jaygengiii@gmail.com
8 | */
9 | class BaseResponse(val error:Boolean,
10 | val results:T)
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/net/RetrofitManager.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.net
2 |
3 | import com.jaygengi.gank.MyApplication
4 | import com.jaygengi.gank.api.ApiService
5 | import com.jaygengi.gank.api.UrlConstant
6 | import com.jaygengi.gank.utils.NetworkUtil
7 | import com.jaygengi.gank.utils.Preference
8 | import okhttp3.*
9 | import okhttp3.logging.HttpLoggingInterceptor
10 | import retrofit2.Retrofit
11 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
12 | import retrofit2.converter.gson.GsonConverterFactory
13 | import java.io.File
14 | import java.util.concurrent.TimeUnit
15 |
16 | /**
17 | * @description: RetrofitManager
18 | * @author JayGengi
19 | * @date 2018/11/13 0013 下午 2:03
20 | * @email jaygengiii@gmail.com
21 | */
22 |
23 | object RetrofitManager{
24 |
25 | val service: ApiService by lazy (LazyThreadSafetyMode.SYNCHRONIZED){
26 | getRetrofit().create(ApiService::class.java)
27 | }
28 |
29 | private var token:String by Preference("token","")
30 |
31 | /**
32 | * 设置公共参数
33 | */
34 | private fun addQueryParameterInterceptor(): Interceptor {
35 | return Interceptor { chain ->
36 | val originalRequest = chain.request()
37 | val request: Request
38 | val modifiedUrl = originalRequest.url().newBuilder()
39 | .build()
40 | request = originalRequest.newBuilder().url(modifiedUrl).build()
41 | chain.proceed(request)
42 | }
43 | }
44 |
45 | /**
46 | * 设置头
47 | */
48 | private fun addHeaderInterceptor(): Interceptor {
49 | return Interceptor { chain ->
50 | val originalRequest = chain.request()
51 | val requestBuilder = originalRequest.newBuilder()
52 | // Provide your custom header here
53 | .header("token", token)
54 | .method(originalRequest.method(), originalRequest.body())
55 | val request = requestBuilder.build()
56 | chain.proceed(request)
57 | }
58 | }
59 |
60 | /**
61 | * 设置缓存
62 | */
63 | private fun addCacheInterceptor(): Interceptor {
64 | return Interceptor { chain ->
65 | var request = chain.request()
66 | if (!NetworkUtil.isNetworkAvailable(MyApplication.context)) {
67 | request = request.newBuilder()
68 | .cacheControl(CacheControl.FORCE_CACHE)
69 | .build()
70 | }
71 | val response = chain.proceed(request)
72 | if (NetworkUtil.isNetworkAvailable(MyApplication.context)) {
73 | val maxAge = 0
74 | // 有网络时 设置缓存超时时间0个小时 ,意思就是不读取缓存数据,只对get有用,post没有缓冲
75 | response.newBuilder()
76 | .header("Cache-Control", "public, max-age=" + maxAge)
77 | .removeHeader("Retrofit")// 清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效
78 | .build()
79 | } else {
80 | // 无网络时,设置超时为4周 只对get有用,post没有缓冲
81 | val maxStale = 60 * 60 * 24 * 28
82 | response.newBuilder()
83 | .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
84 | .removeHeader("nyn")
85 | .build()
86 | }
87 | response
88 | }
89 | }
90 |
91 | private fun getRetrofit(): Retrofit {
92 | // 获取retrofit的实例
93 | return Retrofit.Builder()
94 | .baseUrl(UrlConstant.BASE_URL) //自己配置
95 | .client(getOkHttpClient())
96 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
97 | .addConverterFactory(GsonConverterFactory.create())
98 | .build()
99 |
100 | }
101 |
102 | private fun getOkHttpClient(): OkHttpClient {
103 | //添加一个log拦截器,打印所有的log
104 | val httpLoggingInterceptor = HttpLoggingInterceptor()
105 | //可以设置请求过滤的水平,body,basic,headers
106 | httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
107 |
108 | //设置 请求的缓存的大小跟位置
109 | val cacheFile = File(MyApplication.context.cacheDir, "cache")
110 | val cache = Cache(cacheFile, 1024 * 1024 * 50) //50Mb 缓存的大小
111 |
112 | return OkHttpClient.Builder()
113 | .addInterceptor(addQueryParameterInterceptor()) //参数添加
114 | .addInterceptor(addHeaderInterceptor()) // token过滤
115 | // .addInterceptor(addCacheInterceptor())
116 | .addInterceptor(httpLoggingInterceptor) //日志,所有的请求响应度看到
117 | .cache(cache) //添加缓存
118 | .connectTimeout(60L, TimeUnit.SECONDS)
119 | .readTimeout(60L, TimeUnit.SECONDS)
120 | .writeTimeout(60L, TimeUnit.SECONDS)
121 | .build()
122 | }
123 |
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/net/exception/ApiException.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.net.exception
2 |
3 | /**
4 | * Created by xuhao on 2017/12/5.
5 | * desc:
6 | */
7 | class ApiException : RuntimeException {
8 |
9 | private var code: Int? = null
10 |
11 |
12 | constructor(throwable: Throwable, code: Int) : super(throwable) {
13 | this.code = code
14 | }
15 |
16 | constructor(message: String) : super(Throwable(message))
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/net/exception/ErrorStatus.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.net.exception
2 |
3 | /**
4 | * Created by xuhao on 2017/12/5.
5 | * desc:
6 | */
7 | object ErrorStatus {
8 | /**
9 | * 响应成功
10 | */
11 | @JvmField
12 | val SUCCESS = 0
13 |
14 | /**
15 | * 未知错误
16 | */
17 | @JvmField
18 | val UNKNOWN_ERROR = 1002
19 |
20 | /**
21 | * 服务器内部错误
22 | */
23 | @JvmField
24 | val SERVER_ERROR = 1003
25 |
26 | /**
27 | * 网络连接超时
28 | */
29 | @JvmField
30 | val NETWORK_ERROR = 1004
31 |
32 | /**
33 | * API解析异常(或者第三方数据结构更改)等其他异常
34 | */
35 | @JvmField
36 | val API_ERROR = 1005
37 |
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/net/exception/ExceptionHandle.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.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 | * Created by xuhao on 2017/12/5.
16 | * desc: 异常处理类
17 | */
18 |
19 | class ExceptionHandle {
20 |
21 |
22 | companion object {
23 | var errorCode = ErrorStatus.UNKNOWN_ERROR
24 | var errorMsg = "请求失败,请稍后重试"
25 |
26 | fun handleException(e: Throwable): String {
27 | e.printStackTrace()
28 | if (e is SocketTimeoutException) {//网络超时
29 | Logger.e("TAG", "网络连接异常: " + e.message)
30 | errorMsg = "网络连接异常"
31 | errorCode = ErrorStatus.NETWORK_ERROR
32 | } else if (e is ConnectException) { //均视为网络错误
33 | Logger.e("TAG", "网络连接异常: " + e.message)
34 | errorMsg = "网络连接异常"
35 | errorCode = ErrorStatus.NETWORK_ERROR
36 | } else if (e is JsonParseException
37 | || e is JSONException
38 | || e is ParseException) { //均视为解析错误
39 | Logger.e("TAG", "数据解析异常: " + e.message)
40 | errorMsg = "数据解析异常"
41 | errorCode = ErrorStatus.SERVER_ERROR
42 | } else if (e is ApiException) {//服务器返回的错误信息
43 | errorMsg = e.message.toString()
44 | errorCode = ErrorStatus.SERVER_ERROR
45 | } else if (e is UnknownHostException) {
46 | Logger.e("TAG", "网络连接异常: " + e.message)
47 | errorMsg = "网络连接异常"
48 | errorCode = ErrorStatus.NETWORK_ERROR
49 | } else if (e is IllegalArgumentException) {
50 | errorMsg = "参数错误"
51 | errorCode = ErrorStatus.SERVER_ERROR
52 | } else {//未知错误
53 | try {
54 | Logger.e("TAG", "错误: " + e.message)
55 | } catch (e1: Exception) {
56 | Logger.e("TAG", "未知错误Debug调试 ")
57 | }
58 |
59 | errorMsg = "未知错误,可能抛锚了吧~"
60 | errorCode = ErrorStatus.UNKNOWN_ERROR
61 | }
62 | return errorMsg
63 | }
64 |
65 | }
66 |
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/scheduler/BaseScheduler.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.scheduler
2 |
3 | import io.reactivex.*
4 | import org.reactivestreams.Publisher
5 |
6 | /**
7 | * Created by xuhao on 2017/11/17.
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 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/scheduler/ComputationMainScheduler.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.scheduler
2 |
3 | import com.jaygengi.gank.scheduler.BaseScheduler
4 | import io.reactivex.android.schedulers.AndroidSchedulers
5 | import io.reactivex.schedulers.Schedulers
6 | /**
7 | * Created by xuhao on 2017/11/17.
8 | * desc:
9 | */
10 |
11 |
12 | class ComputationMainScheduler private constructor() : BaseScheduler(Schedulers.computation(), AndroidSchedulers.mainThread())
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/scheduler/IoMainScheduler.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.scheduler
2 |
3 | import com.jaygengi.gank.scheduler.BaseScheduler
4 | import io.reactivex.android.schedulers.AndroidSchedulers
5 | import io.reactivex.schedulers.Schedulers
6 |
7 | /**
8 | * Created by xuhao on 2017/11/17.
9 | * desc:
10 | */
11 | class IoMainScheduler : BaseScheduler(Schedulers.io(), AndroidSchedulers.mainThread())
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/scheduler/NewThreadMainScheduler.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.scheduler
2 |
3 | import com.jaygengi.gank.scheduler.BaseScheduler
4 | import io.reactivex.android.schedulers.AndroidSchedulers
5 | import io.reactivex.schedulers.Schedulers
6 |
7 | /**
8 | * Created by xuhao on 2017/11/17.
9 | * desc:
10 | */
11 |
12 |
13 | class NewThreadMainScheduler private constructor() : BaseScheduler(Schedulers.newThread(), AndroidSchedulers.mainThread())
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/scheduler/SchedulerUtils.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.scheduler
2 |
3 | /**
4 | * Created by xuhao on 2017/11/17.
5 | * desc:
6 | */
7 |
8 | object SchedulerUtils {
9 |
10 | fun ioToMain(): IoMainScheduler {
11 | return IoMainScheduler()
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/scheduler/SingleMainScheduler.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.scheduler
2 |
3 | import com.jaygengi.gank.scheduler.BaseScheduler
4 | import io.reactivex.android.schedulers.AndroidSchedulers
5 | import io.reactivex.schedulers.Schedulers
6 |
7 | /**
8 | * Created by xuhao on 2017/11/17.
9 | * desc:
10 | */
11 |
12 |
13 | class SingleMainScheduler private constructor() : BaseScheduler(Schedulers.single(), AndroidSchedulers.mainThread())
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/scheduler/TrampolineMainScheduler.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.scheduler
2 |
3 | import com.jaygengi.gank.scheduler.BaseScheduler
4 | import io.reactivex.android.schedulers.AndroidSchedulers
5 | import io.reactivex.schedulers.Schedulers
6 |
7 | /**
8 | * Created by xuhao on 2017/11/17.
9 | * desc:
10 | */
11 |
12 |
13 | class TrampolineMainScheduler private constructor() : BaseScheduler(Schedulers.trampoline(), AndroidSchedulers.mainThread())
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/ui/activity/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.ui.activity
2 |
3 | import android.annotation.SuppressLint
4 | import android.os.Bundle
5 | import android.support.v4.app.FragmentTransaction
6 | import android.view.KeyEvent
7 | import com.flyco.tablayout.listener.CustomTabEntity
8 | import com.flyco.tablayout.listener.OnTabSelectListener
9 | import com.jaygengi.gank.mvp.model.bean.TabEntity
10 | import com.jaygengi.gank.R
11 | import com.jaygengi.gank.base.BaseActivity
12 | import com.jaygengi.gank.showToast
13 | import com.jaygengi.gank.ui.fragment.*
14 | import kotlinx.android.synthetic.main.activity_main.*
15 | import java.util.*
16 |
17 |
18 | /**
19 | * @description: 首页
20 | * @author JayGengi
21 | * @date 2018/10/29 0029 上午 11:57
22 | * @email jaygengiii@gmail.com
23 | */
24 |
25 |
26 | class MainActivity : BaseActivity() {
27 |
28 |
29 | private val mTitles = arrayOf("首页", "分类", "个人中心")
30 |
31 | // 未被选中的图标
32 | private val mIconUnSelectIds = intArrayOf(R.mipmap.ic_home_normal, R.mipmap.ic_classification_normal, R.mipmap.ic_mine_normal)
33 | // 被选中的图标
34 | private val mIconSelectIds = intArrayOf(R.mipmap.ic_home_selected, R.mipmap.ic_classification_selected, R.mipmap.ic_mine_selected)
35 |
36 | private val mTabEntities = ArrayList()
37 |
38 | private var mHomeFragment: HomeFragment? = null
39 | private var mHotFragment: GankTypeFragment? = null
40 | private var mMineFragment: MineFragment? = null
41 |
42 | //默认为0
43 | private var mIndex = 0
44 |
45 |
46 | override fun onCreate(savedInstanceState: Bundle?) {
47 | if (savedInstanceState != null) {
48 | mIndex = savedInstanceState.getInt("currTabIndex")
49 | }
50 | super.onCreate(savedInstanceState)
51 | initTab()
52 | tab_layout.currentTab = mIndex
53 | switchFragment(mIndex)
54 |
55 | }
56 |
57 | override fun layoutId(): Int {
58 | return R.layout.activity_main
59 | }
60 |
61 |
62 | //初始化底部菜单
63 | private fun initTab() {
64 | (0 until mTitles.size)
65 | .mapTo(mTabEntities) { TabEntity(mTitles[it], mIconSelectIds[it], mIconUnSelectIds[it]) }
66 | //为Tab赋值
67 | tab_layout.setTabData(mTabEntities)
68 | tab_layout.setOnTabSelectListener(object : OnTabSelectListener {
69 | override fun onTabSelect(position: Int) {
70 | //切换Fragment
71 | switchFragment(position)
72 | }
73 |
74 | override fun onTabReselect(position: Int) {
75 |
76 | }
77 | })
78 | }
79 |
80 | /**
81 | * 切换Fragment
82 | * @param position 下标
83 | */
84 | private fun switchFragment(position: Int) {
85 | val transaction = supportFragmentManager.beginTransaction()
86 | hideFragments(transaction)
87 | when (position) {
88 | 0 // 首页
89 | -> mHomeFragment?.let {
90 | transaction.show(it)
91 | } ?: HomeFragment.getInstance(mTitles[position]).let {
92 | mHomeFragment = it
93 | transaction.add(R.id.fl_container, it, "home")
94 | }
95 | 1 //热门
96 | -> mHotFragment?.let {
97 | transaction.show(it)
98 | } ?: GankTypeFragment.getInstance(mTitles[position]).let {
99 | mHotFragment = it
100 | transaction.add(R.id.fl_container, it, "hot") }
101 | 2 //我的
102 | -> mMineFragment?.let {
103 | transaction.show(it)
104 | } ?: MineFragment.getInstance(mTitles[position]).let {
105 | mMineFragment = it
106 | transaction.add(R.id.fl_container, it, "mine") }
107 |
108 | else -> {
109 |
110 | }
111 | }
112 |
113 | mIndex = position
114 | tab_layout.currentTab = mIndex
115 | transaction.commitAllowingStateLoss()
116 | }
117 |
118 |
119 | /**
120 | * 隐藏所有的Fragment
121 | * @param transaction transaction
122 | */
123 | private fun hideFragments(transaction: FragmentTransaction) {
124 | mHomeFragment?.let { transaction.hide(it) }
125 | mHotFragment?.let { transaction.hide(it) }
126 | mMineFragment?.let { transaction.hide(it) }
127 | }
128 |
129 |
130 | @SuppressLint("MissingSuperCall")
131 | override fun onSaveInstanceState(outState: Bundle) {
132 | // showToast("onSaveInstanceState->"+mIndex)
133 | // super.onSaveInstanceState(outState)
134 | //记录fragment的位置,防止崩溃 activity被系统回收时,fragment错乱
135 | if (tab_layout != null) {
136 | outState.putInt("currTabIndex", mIndex)
137 | }
138 | }
139 |
140 | override fun initView() {
141 |
142 | }
143 |
144 | override fun initData() {
145 |
146 | }
147 |
148 | override fun start() {
149 |
150 | }
151 |
152 | private var mExitTime: Long = 0
153 |
154 | override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
155 | if (keyCode == KeyEvent.KEYCODE_BACK) {
156 | if (System.currentTimeMillis().minus(mExitTime) <= 2000) {
157 | finish()
158 | } else {
159 | mExitTime = System.currentTimeMillis()
160 | showToast("再按一次退出程序")
161 | }
162 | return true
163 | }
164 | return super.onKeyDown(keyCode, event)
165 | }
166 |
167 |
168 | }
169 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/ui/activity/SearchActivity.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.ui.activity
2 |
3 | import android.annotation.SuppressLint
4 | import com.jaygengi.gank.R
5 | import com.jaygengi.gank.base.BaseActivity
6 | import kotlinx.android.synthetic.main.activity_search.*
7 |
8 |
9 | /**
10 | * @description: 首頁搜索頁
11 | * @author JayGengi
12 | * @date 2018/10/29 0029 上午 11:57
13 | * @email jaygengiii@gmail.com
14 | */
15 |
16 |
17 | class SearchActivity : BaseActivity() {
18 |
19 | override fun layoutId(): Int {
20 | return R.layout.activity_search
21 | }
22 |
23 | @SuppressLint("ResourceAsColor")
24 | override fun initView() {
25 | topbar.setTitle("Gank Search").setTextColor(R.color.color_black)
26 | topbar.addLeftImageButton(R.drawable.ic_left,R.id.left).setOnClickListener {
27 | finish()
28 | }
29 | }
30 |
31 | override fun start() {
32 | }
33 |
34 | override fun initData() {
35 | }
36 |
37 |
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/ui/activity/SplashActivity.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.ui.activity
2 |
3 | import android.Manifest
4 | import android.annotation.SuppressLint
5 | import android.content.Intent
6 | import android.view.animation.AlphaAnimation
7 | import android.view.animation.Animation
8 | import android.view.animation.Animation.AnimationListener
9 | import com.jaygengi.gank.MyApplication
10 | import com.jaygengi.gank.R
11 | import com.jaygengi.gank.base.BaseActivity
12 | import com.jaygengi.gank.utils.AppUtils
13 | import kotlinx.android.synthetic.main.activity_splash.*
14 |
15 |
16 | /**
17 | * @description: 启动页
18 | * @author JayGengi
19 | * @date 2018/10/29 0029 上午 11:56
20 | * @email jaygengiii@gmail.com
21 | */
22 |
23 | class SplashActivity : BaseActivity() {
24 |
25 |
26 |
27 | private var alphaAnimation:AlphaAnimation?=null
28 |
29 |
30 | override fun layoutId(): Int = R.layout.activity_splash
31 |
32 | override fun initData() {
33 |
34 | }
35 |
36 | @SuppressLint("SetTextI18n")
37 | override fun initView() {
38 |
39 | //渐变展示启动屏
40 | alphaAnimation= AlphaAnimation(0.3f, 1.0f)
41 | alphaAnimation?.duration = 2000
42 | alphaAnimation?.setAnimationListener(object : AnimationListener {
43 | override fun onAnimationEnd(arg0: Animation) {
44 | redirectTo()
45 | }
46 | override fun onAnimationRepeat(animation: Animation) {}
47 |
48 | override fun onAnimationStart(animation: Animation) {}
49 |
50 | })
51 | tv_version_name.text = "v${AppUtils.getVerName(MyApplication.context)}"
52 | if (alphaAnimation != null) {
53 | iv_web_icon.startAnimation(alphaAnimation)
54 | }
55 | }
56 | override fun start() {
57 |
58 | }
59 |
60 | fun redirectTo() {
61 | val intent = Intent(this, MainActivity::class.java)
62 | startActivity(intent)
63 | finish()
64 | }
65 |
66 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/ui/activity/WebViewActivity.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.ui.activity
2 |
3 | import android.annotation.SuppressLint
4 | import android.graphics.Bitmap
5 | import android.view.View
6 | import com.jaygengi.gank.R
7 | import com.jaygengi.gank.base.BaseActivity
8 | import im.delight.android.webview.AdvancedWebView
9 | import kotlinx.android.synthetic.main.activity_webview.*
10 |
11 | /**
12 | * @description: WebViewActivity
13 | * @author JayGengi
14 | * @date 2018/11/16 0016 下午 1:02
15 | * @email jaygengiii@gmail.com
16 | */
17 | class WebViewActivity : BaseActivity(), AdvancedWebView.Listener{
18 | private var url:String? = ""
19 | private var title:String? = ""
20 |
21 | private var mWebView: AdvancedWebView? = null
22 | override fun layoutId(): Int {
23 | return R.layout.activity_webview
24 | }
25 |
26 | override fun initData() {
27 | }
28 |
29 | override fun start() {
30 | }
31 |
32 |
33 | @SuppressLint("ResourceAsColor")
34 | override fun initView() {
35 | url = intent.getStringExtra("url")
36 | title = intent.getStringExtra("title")
37 | topbar.setTitle(title).setTextColor(R.color.color_black)
38 | topbar.addLeftImageButton(R.drawable.ic_left,R.id.left).setOnClickListener {
39 | finish()
40 | }
41 | mWebView = findViewById(R.id.webview) as AdvancedWebView
42 | mWebView!!.setListener(this, this)
43 | setSettings()
44 | mWebView!!.loadUrl(url)
45 | }
46 | private fun setSettings(){
47 | mWebView!!.settings.setSupportZoom(true)
48 | }
49 | override fun onDestroy() {
50 | mWebView!!.onDestroy()
51 | super.onDestroy()
52 | }
53 |
54 | override fun onPageFinished(url: String?) {
55 | }
56 |
57 | override fun onPageError(errorCode: Int, description: String?, failingUrl: String?) {
58 | }
59 |
60 | override fun onDownloadRequested(url: String?, suggestedFilename: String?, mimeType: String?, contentLength: Long, contentDisposition: String?, userAgent: String?) {
61 | }
62 |
63 | override fun onExternalPageRequest(url: String?) {
64 | }
65 |
66 | override fun onPageStarted(url: String?, favicon: Bitmap?) {
67 | }
68 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/ui/adapter/GirlsAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.ui.adapter
2 |
3 | import com.bumptech.glide.Glide
4 | import com.chad.library.adapter.base.BaseQuickAdapter
5 | import com.chad.library.adapter.base.BaseViewHolder
6 | import com.jaygengi.gank.R
7 | import com.jaygengi.gank.mvp.model.bean.GirlsEntity
8 |
9 | /**
10 | * @description: 美女福利适配器
11 | * @author JayGengi
12 | * @date 2018/11/14 0014 下午 5:01
13 | * @email jaygengiii@gmail.com
14 | */
15 | class GirlsAdapter(data: List?)
16 | : BaseQuickAdapter
17 | (R.layout.item_grils_info, data) {
18 |
19 | override fun convert(helper: BaseViewHolder, item: GirlsEntity.ResultsBean) {
20 | //Glide 加载图片简单用法
21 | Glide.with(mContext).load(item.url).into(helper.getView(R.id.rental_image))
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/ui/adapter/HomePageAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.ui.adapter
2 |
3 | import android.support.v4.app.Fragment
4 | import android.support.v4.app.FragmentManager
5 | import android.support.v4.app.FragmentPagerAdapter
6 |
7 | /**
8 | * @description: PageAdapter
9 | * @author JayGengi
10 | * @date 2018/11/15 0015 下午 4:48
11 | * @email jaygengiii@gmail.com
12 | */
13 | class HomePageAdapter(fm: FragmentManager?) : FragmentPagerAdapter(fm) {
14 |
15 | private val fragments = ArrayList()
16 | private val titles = ArrayList()
17 |
18 | fun setData(fragments: List, titles: List) {
19 | this.fragments.clear()
20 | this.fragments.addAll(fragments)
21 | this.titles.clear()
22 | this.titles.addAll(titles)
23 | }
24 |
25 | override fun getItem(position: Int): Fragment {
26 | return fragments[position]
27 | }
28 |
29 | override fun getCount() = fragments.size
30 |
31 | override fun getPageTitle(position: Int) = titles[position]
32 |
33 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/ui/adapter/ToDayAndroidAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.ui.adapter
2 |
3 | import android.annotation.SuppressLint
4 | import com.chad.library.adapter.base.BaseQuickAdapter
5 | import com.chad.library.adapter.base.BaseViewHolder
6 | import com.jaygengi.gank.R
7 | import com.jaygengi.gank.mvp.model.bean.CategoryEntity
8 | import java.text.ParseException
9 | import java.text.SimpleDateFormat
10 | import java.util.*
11 |
12 | /**
13 | * @description: 获取最新一天的干货适配器
14 | * @author JayGengi
15 | * @date 2018/11/14 0014 下午 5:01
16 | * @email jaygengiii@gmail.com
17 | */
18 | class ToDayAndroidAdapter(data: List?)
19 | : BaseQuickAdapter
20 | (R.layout.item_category, data) {
21 |
22 | @SuppressLint("SimpleDateFormat")
23 | override fun convert(helper: BaseViewHolder, item: CategoryEntity.ResultsBean) {
24 |
25 | val sf = SimpleDateFormat("yyyy-MM-dd")
26 | try {
27 | val date = sf.parse(item.publishedAt)
28 | val calendar = Calendar.getInstance()
29 | calendar.time = date
30 | val time = calendar.get(Calendar.YEAR).toString()+"-"+(calendar.get(Calendar.MONTH) + 1)+"-"+Calendar.DAY_OF_MONTH
31 | helper.setText(R.id.content, item.desc)
32 | .setText(R.id.auther, "auther: "+item.who)
33 | .setText(R.id.time,time)
34 | } catch (e: ParseException) {
35 | e.printStackTrace()
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/ui/fragment/GankTypeFragment.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.ui.fragment
2 |
3 | import android.annotation.SuppressLint
4 | import android.os.Bundle
5 | import android.support.v4.app.Fragment
6 | import com.jaygengi.gank.R
7 | import com.jaygengi.gank.base.BaseFragment
8 | import com.jaygengi.gank.ui.adapter.HomePageAdapter
9 | import com.jaygengi.gank.ui.fragment.home.*
10 | import kotlinx.android.synthetic.main.fragment_gank_type.*
11 |
12 | /**
13 | * @description: GankType
14 | * @author JayGengi
15 | * @date 2018/10/29 0029 下午 1:55
16 | * @email jaygengiii@gmail.com
17 | */
18 | class GankTypeFragment : BaseFragment(){
19 |
20 | private var mTitle: String? = null
21 | private val fragments = ArrayList()
22 |
23 | private val titles = ArrayList()
24 | companion object {
25 | fun getInstance(title: String): GankTypeFragment {
26 | val fragment = GankTypeFragment()
27 | val bundle = Bundle()
28 | fragment.arguments = bundle
29 | fragment.mTitle = title
30 | return fragment
31 | }
32 | }
33 |
34 | override fun getLayoutId(): Int = R.layout.fragment_gank_type
35 |
36 | override fun lazyLoad() {
37 | }
38 |
39 | @SuppressLint("ResourceAsColor", "SetTextI18n")
40 | override fun initView() {
41 | title.text ="GankType"
42 | fragments.add(WelfareFragment())
43 | fragments.add(CategoryFragment.newInstance("Android"))
44 | fragments.add(CategoryFragment.newInstance("App"))
45 | fragments.add(CategoryFragment.newInstance("iOS"))
46 | fragments.add(CategoryFragment.newInstance("休息视频"))
47 | fragments.add(CategoryFragment.newInstance("前端"))
48 | fragments.add(CategoryFragment.newInstance("拓展资源"))
49 | fragments.add(CategoryFragment.newInstance("瞎推荐"))
50 | titles.add("福利")
51 | titles.add("Android")
52 | titles.add("App")
53 | titles.add("iOS")
54 | titles.add("休息视频")
55 | titles.add("前端")
56 | titles.add("拓展资源")
57 | titles.add("瞎推荐")
58 | viewpager.adapter = HomePageAdapter(childFragmentManager).apply {
59 | setData(fragments,titles)
60 | }
61 | viewpager.offscreenPageLimit = 4
62 |
63 | sliding_tabs.setViewPager(viewpager)
64 | }
65 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/ui/fragment/HomeFragment.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.ui.fragment
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Intent
5 | import android.os.Build
6 | import android.os.Bundle
7 | import android.support.v4.app.ActivityOptionsCompat
8 | import android.support.v4.content.ContextCompat
9 | import android.support.v7.widget.LinearLayoutManager
10 | import android.view.View
11 | import com.bumptech.glide.Glide
12 | import com.jaygengi.gank.R
13 | import com.jaygengi.gank.base.BaseFragment
14 | import com.jaygengi.gank.mvp.contract.HomeContract
15 | import com.jaygengi.gank.mvp.model.bean.GirlsEntity
16 | import com.jaygengi.gank.mvp.model.bean.ToDayEntity
17 | import com.jaygengi.gank.mvp.presenter.HomePresenter
18 | import com.jaygengi.gank.net.exception.ErrorStatus
19 | import com.jaygengi.gank.ui.activity.SearchActivity
20 | import com.jaygengi.gank.ui.adapter.ToDaySectionAdapter
21 | import kotlinx.android.synthetic.main.fragment_home.*
22 | import android.support.v7.widget.RecyclerView
23 |
24 |
25 |
26 | /**
27 | * @description: 首页
28 | * @author JayGengi
29 | * @date 2018/11/7 0007 下午 5:00
30 | * @email jaygengiii@gmail.com
31 | */
32 |
33 | class HomeFragment : BaseFragment(), HomeContract.View {
34 |
35 |
36 | private val mPresenter by lazy { HomePresenter() }
37 |
38 | private var mCategoryList = ArrayList()
39 |
40 | private val mAdapter by lazy { activity?.let { ToDaySectionAdapter(context!!, mCategoryList) } }
41 |
42 | private var mTitle: String? = null
43 |
44 |
45 | private val categoryList = ArrayList()
46 | override fun getLayoutId(): Int = R.layout.fragment_home
47 |
48 | override fun lazyLoad() {
49 | loadData()
50 | }
51 | @SuppressLint("ResourceAsColor")
52 | override fun initView() {
53 |
54 | collapsing_topbar_layout.setCollapsedTitleTextColor(R.color.color_black)
55 | topbar.addRightImageButton(R.mipmap.ic_action_search_black,R.id.right).setOnClickListener {
56 | openSearchActivity(topbar.findViewById(R.id.right))
57 | }
58 |
59 | mPresenter.attachView(this)
60 | mLayoutStatusView = multipleStatusView
61 | recyclerView.apply {
62 | setHasFixedSize(true)
63 | // isNestedScrollingEnabled = false
64 | layoutManager = LinearLayoutManager(context)
65 | adapter = mAdapter
66 | }
67 |
68 |
69 | recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
70 |
71 | override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
72 |
73 | super.onScrolled(recyclerView, dx, dy)
74 |
75 | val l = recyclerView!!.layoutManager as LinearLayoutManager
76 |
77 | val adapterNowPos = l.findFirstVisibleItemPosition()
78 |
79 | collapsing_topbar_layout.title = categoryList[adapterNowPos]
80 |
81 | }
82 |
83 | })
84 |
85 | }
86 | private fun loadData(){
87 | mPresenter.requestGirlInfo()
88 | //获取分类信息
89 | mPresenter.requestToDayInfo()
90 | }
91 | override fun showGirlInfo(dataInfo: GirlsEntity) {
92 | Glide.with(this).load(dataInfo.results!![0].url).into(head_img)
93 | }
94 | override fun showToDayInfo(todayInfo: ToDayEntity) {
95 | mAdapter?.run {
96 |
97 |
98 | if (todayInfo.category != null && todayInfo.results!= null) {
99 | categoryList.clear()
100 | categoryList.addAll(todayInfo.category!!)
101 | setNewData(todayInfo.category)
102 | setToDayTypeInfo(todayInfo.results)
103 | } else {
104 | multipleStatusView?.showEmpty()
105 | }
106 | }
107 | }
108 |
109 | private fun openSearchActivity(view :View) {
110 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
111 | val options = activity?.let { ActivityOptionsCompat.makeSceneTransitionAnimation(it, view, "JayGengi") }
112 | startActivity(Intent(activity, SearchActivity::class.java), options?.toBundle())
113 | } else {
114 | startActivity(Intent(activity, SearchActivity::class.java))
115 | }
116 | }
117 |
118 | override fun showError(msg: String, errorCode: Int) {
119 | if (errorCode == ErrorStatus.NETWORK_ERROR) {
120 | multipleStatusView?.showNoNetwork()
121 |
122 | } else {
123 | multipleStatusView?.showError()
124 | }
125 | }
126 | /**
127 | * 显示 Loading (下拉刷新的时候不需要显示 Loading)
128 | */
129 | override fun showLoading() {
130 | mLayoutStatusView?.showLoading()
131 | }
132 | /**
133 | * 隐藏 Loading
134 | */
135 | override fun dismissLoading() {
136 | multipleStatusView?.showContent()
137 | // if(mRefreshLayout!=null && mRefreshLayout.isLoading){
138 | // mRefreshLayout.finishRefresh()
139 | // }
140 | }
141 |
142 | override fun onDestroy() {
143 | super.onDestroy()
144 | mPresenter.detachView()
145 | }
146 | companion object {
147 | fun getInstance(title: String): HomeFragment {
148 | val fragment = HomeFragment()
149 | val bundle = Bundle()
150 | fragment.arguments = bundle
151 | fragment.mTitle = title
152 | return fragment
153 | }
154 | }
155 |
156 | }
157 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/ui/fragment/MineFragment.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.ui.fragment
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import android.view.View
6 | import com.jaygengi.gank.R
7 | import com.jaygengi.gank.base.BaseFragment
8 | import com.jaygengi.gank.ui.activity.WebViewActivity
9 | import kotlinx.android.synthetic.main.fragment_mine.*
10 |
11 | /**
12 | * Created by xuhao on 2017/11/9.
13 | * 我的
14 | */
15 | class MineFragment : BaseFragment(), View.OnClickListener {
16 |
17 |
18 | private var mTitle: String? = null
19 | companion object {
20 | fun getInstance(title: String): MineFragment {
21 | val fragment = MineFragment()
22 | val bundle = Bundle()
23 | fragment.arguments = bundle
24 | fragment.mTitle = title
25 | return fragment
26 | }
27 | }
28 | override fun getLayoutId(): Int= R.layout.fragment_mine
29 |
30 | override fun initView() {
31 | github_url.setOnClickListener(this)
32 | gank_url.setOnClickListener(this)
33 | demo_url.setOnClickListener(this)
34 | qmui_url.setOnClickListener(this)
35 | retrofit_url.setOnClickListener(this)
36 | adapter_url.setOnClickListener(this)
37 | smartRefreshLayout_url.setOnClickListener(this)
38 | multiple_status_view_url.setOnClickListener(this)
39 |
40 | }
41 |
42 | override fun lazyLoad() {
43 |
44 | }
45 |
46 | override fun onClick(v: View?) {
47 | when(v!!.id) {
48 | R.id.github_url -> {
49 | val intent = Intent(context, WebViewActivity::class.java)
50 | intent.putExtra("title","JayGengi")
51 | intent.putExtra("url", "https://github.com/JayGengi/KotlinGankApp")
52 | startActivity(intent)
53 | }
54 | R.id.gank_url -> {
55 | val intent = Intent(context, WebViewActivity::class.java)
56 | intent.putExtra("title","Gank.io")
57 | intent.putExtra("url", "https://gank.io")
58 | startActivity(intent)
59 | }
60 | R.id.demo_url -> {
61 | val intent = Intent(context, WebViewActivity::class.java)
62 | intent.putExtra("title","KotlinMvp")
63 | intent.putExtra("url", "https://github.com/git-xuhao/KotlinMvp")
64 | startActivity(intent)
65 | }
66 | R.id.qmui_url -> {
67 | val intent = Intent(context, WebViewActivity::class.java)
68 | intent.putExtra("title","提高 Android UI 开发效率的 UI 库")
69 | intent.putExtra("url", "https://github.com/QMUI/QMUI_Android")
70 | startActivity(intent)
71 | }
72 | R.id.retrofit_url -> {
73 | val intent = Intent(context, WebViewActivity::class.java)
74 | intent.putExtra("title","retrofit")
75 | intent.putExtra("url", "https://github.com/square/retrofit")
76 | startActivity(intent)
77 | }
78 | R.id.multiple_status_view_url -> {
79 | val intent = Intent(context, WebViewActivity::class.java)
80 | intent.putExtra("title","MultipleStatusView")
81 | intent.putExtra("url", "https://github.com/qyxxjd/MultipleStatusView")
82 | startActivity(intent)
83 | }
84 | R.id.smartRefreshLayout_url -> {
85 | val intent = Intent(context, WebViewActivity::class.java)
86 | intent.putExtra("title","SmartRefreshLayout")
87 | intent.putExtra("url", "https://github.com/scwang90/SmartRefreshLayout")
88 | startActivity(intent)
89 | }
90 |
91 |
92 |
93 | }
94 | }
95 |
96 |
97 |
98 |
99 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/ui/fragment/home/CategoryFragment.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.ui.fragment.home
2 |
3 | import android.content.Intent
4 | import android.net.Uri
5 | import android.os.Bundle
6 | import android.support.v7.widget.LinearLayoutManager
7 | import com.jaygengi.gank.R
8 | import com.jaygengi.gank.base.BaseFragment
9 | import com.jaygengi.gank.mvp.contract.CategoryContract
10 | import com.jaygengi.gank.mvp.model.bean.CategoryEntity
11 | import com.jaygengi.gank.mvp.presenter.CategoryPresenter
12 | import com.jaygengi.gank.net.exception.ErrorStatus
13 | import com.jaygengi.gank.showToast
14 | import com.jaygengi.gank.ui.activity.WebViewActivity
15 | import com.jaygengi.gank.ui.adapter.ToDayAndroidAdapter
16 | import kotlinx.android.synthetic.main.fragment_home_common_type.*
17 |
18 | /**
19 | * @description: android
20 | * @author JayGengi
21 | * @date 2018/11/7 0007 下午 5:00
22 | * @email jaygengiii@gmail.com
23 | */
24 |
25 | class CategoryFragment : BaseFragment(), CategoryContract.View {
26 | private var isFirstLoad = false
27 | //category 后面可接受参数 all | Android | iOS | 休息视频 | 福利 | 拓展资源 | 前端 | 瞎推荐 | App
28 | lateinit var key: String
29 |
30 | private val mPresenter by lazy { CategoryPresenter() }
31 |
32 | private var androidList = ArrayList()
33 |
34 | private val mAdapter by lazy { activity?.let { ToDayAndroidAdapter( androidList) } }
35 |
36 |
37 |
38 | override fun getLayoutId(): Int = R.layout.fragment_home_common_type
39 |
40 | override fun lazyLoad() {
41 | isFirstLoad = true
42 | key = arguments!!.getString("key")
43 | loadData()
44 | }
45 | override fun initView() {
46 | mPresenter.attachView(this)
47 | mLayoutStatusView = multipleStatusView
48 | mRefreshLayout.setOnRefreshListener {
49 | CURRENT_PAGE =1
50 | loadData()
51 | }
52 | mRefreshLayout.setOnLoadMoreListener {
53 | CURRENT_PAGE ++
54 | loadData()
55 | }
56 | recycler.apply {
57 | setHasFixedSize(true)
58 | isNestedScrollingEnabled = false
59 | layoutManager = LinearLayoutManager(context)
60 | adapter = mAdapter
61 | }
62 | mAdapter!!.setOnItemClickListener { adapter, _, position ->
63 | val item :CategoryEntity.ResultsBean = adapter.getItem(position) as CategoryEntity.ResultsBean
64 | val intent = Intent(context, WebViewActivity::class.java)
65 | intent.putExtra("title", item.desc)
66 | intent.putExtra("url", item.url)
67 | startActivity(intent)
68 | }
69 |
70 |
71 | }
72 | private fun loadData(){
73 |
74 | mPresenter.requestCategoryInfo(key,PAGE_CAPACITY,CURRENT_PAGE)
75 | }
76 | override fun getCategoryInfo(todayInfo: CategoryEntity) {
77 | multipleStatusView?.showContent()
78 | mAdapter?.run {
79 | if (todayInfo.results != null && todayInfo.results!!.isNotEmpty()) {
80 | if (CURRENT_PAGE == 1) {
81 | androidList.clear()
82 | }
83 | androidList.addAll(todayInfo.results!!)
84 | setNewData(androidList)
85 | } else {
86 | multipleStatusView?.showEmpty()
87 | }
88 | }
89 | }
90 | override fun showError(msg: String, errorCode: Int) {
91 | showToast(msg)
92 | if (errorCode == ErrorStatus.NETWORK_ERROR) {
93 | multipleStatusView?.showNoNetwork()
94 |
95 | } else {
96 | multipleStatusView?.showError()
97 | }
98 | }
99 |
100 | /**
101 | * 显示 Loading (下拉刷新的时候不需要显示 Loading)
102 | */
103 | override fun showLoading() {
104 | if(isFirstLoad) {
105 | isFirstLoad = false
106 | mLayoutStatusView?.showLoading()
107 | }
108 | }
109 | /**
110 | * 隐藏 Loading
111 | */
112 | override fun dismissLoading() {
113 | if(mRefreshLayout!=null && mRefreshLayout.isRefreshing){
114 | mRefreshLayout.finishRefresh()
115 | }
116 | if(mRefreshLayout!=null && mRefreshLayout.isLoading){
117 | mRefreshLayout.finishLoadMore()
118 | }
119 | }
120 |
121 | override fun onDestroy() {
122 | super.onDestroy()
123 | mPresenter.detachView()
124 | }
125 |
126 | companion object {
127 | fun newInstance(key: String): CategoryFragment {
128 | val args = Bundle()
129 | args.putString("key", key)
130 | val fragment = CategoryFragment()
131 | fragment.arguments = args
132 | return fragment
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/ui/fragment/home/WelfareFragment.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.ui.fragment.home
2 |
3 | import android.support.v7.widget.GridLayoutManager
4 | import cc.shinichi.library.ImagePreview
5 | import com.jaygengi.gank.R
6 | import com.jaygengi.gank.base.BaseFragment
7 | import com.jaygengi.gank.mvp.contract.GirlsContract
8 | import com.jaygengi.gank.mvp.model.bean.GirlsEntity
9 | import com.jaygengi.gank.mvp.presenter.GirlsPresenter
10 | import com.jaygengi.gank.net.exception.ErrorStatus
11 | import com.jaygengi.gank.ui.adapter.GirlsAdapter
12 | import kotlinx.android.synthetic.main.fragment_home_common_type.*
13 | import cc.shinichi.library.bean.ImageInfo
14 |
15 |
16 |
17 |
18 |
19 | /**
20 | * @description: 福利
21 | * @author JayGengi
22 | * @date 2018/11/15 0015 下午 4:59
23 | * @email jaygengiii@gmail.com
24 | */
25 |
26 | class WelfareFragment : BaseFragment(), GirlsContract.View {
27 | private var isRefresh = false
28 | private val imageInfoList: ArrayList = ArrayList()
29 |
30 | private var imageInfo = ImageInfo()
31 | private val mPresenter by lazy { GirlsPresenter() }
32 | private var grilsList = ArrayList()
33 | private val mAdapter by lazy { activity?.let { GirlsAdapter( grilsList) } }
34 | override fun getLayoutId(): Int = R.layout.fragment_welfare
35 |
36 |
37 | override fun initView() {
38 | mPresenter.attachView(this)
39 | mLayoutStatusView = multipleStatusView
40 | mRefreshLayout.setOnRefreshListener {
41 | CURRENT_PAGE =1
42 | loadData()
43 | }
44 | mRefreshLayout.setOnLoadMoreListener {
45 | CURRENT_PAGE++
46 | loadData()
47 | }
48 | recycler.apply {
49 | setHasFixedSize(true)
50 | isNestedScrollingEnabled = false
51 | layoutManager = GridLayoutManager(context,2)
52 | adapter = mAdapter
53 | }
54 | mAdapter!!.setOnItemClickListener { adapter, _, position ->
55 | ImagePreview
56 | .getInstance()
57 | .setContext(context!!)
58 | .setIndex(position)// 从第一张图片开始,索引从0开始哦
59 | .setFolderName("Gank")// 保存的文件夹名称,SD卡根目录
60 | .setImageInfoList(imageInfoList)
61 | .start()
62 | }
63 | }
64 |
65 | override fun lazyLoad() {
66 | isRefresh = true
67 | loadData()
68 | }
69 | private fun loadData(){
70 |
71 | mPresenter.requestGirlInfo(PAGE_CAPACITY,CURRENT_PAGE)
72 | }
73 | override fun showGirlInfo(dataInfo: GirlsEntity) {
74 | multipleStatusView?.showContent()
75 | mAdapter?.run {
76 | if (dataInfo.results != null && dataInfo.results!!.isNotEmpty()) {
77 | if (CURRENT_PAGE == 1) {
78 | grilsList.clear()
79 | imageInfoList.clear()
80 | }
81 | grilsList.addAll(dataInfo.results!!)
82 | for(img : GirlsEntity.ResultsBean in grilsList){
83 | imageInfo = ImageInfo()
84 | imageInfo.originUrl = img.url
85 | imageInfo.thumbnailUrl = img.url
86 | imageInfoList.add(imageInfo)
87 | }
88 | setNewData(grilsList)
89 | } else {
90 | multipleStatusView?.showEmpty()
91 | }
92 | }
93 | }
94 |
95 | override fun showError(msg: String, errorCode: Int) {
96 | if (errorCode == ErrorStatus.NETWORK_ERROR) {
97 | multipleStatusView?.showNoNetwork()
98 |
99 | } else {
100 | multipleStatusView?.showError()
101 | }
102 | }
103 |
104 | /**
105 | * 显示 Loading (下拉刷新的时候不需要显示 Loading)
106 | */
107 | override fun showLoading() {
108 | if(isRefresh) {
109 | isRefresh = false
110 | mLayoutStatusView?.showLoading()
111 | }
112 | }
113 | /**
114 | * 隐藏 Loading
115 | */
116 | override fun dismissLoading() {
117 | if(mRefreshLayout!=null && mRefreshLayout.isRefreshing){
118 | mRefreshLayout.finishRefresh()
119 | }
120 | if(mRefreshLayout!=null && mRefreshLayout.isLoading){
121 | mRefreshLayout.finishLoadMore()
122 | }
123 | }
124 |
125 | override fun onDestroy() {
126 | super.onDestroy()
127 | mPresenter.detachView()
128 | }
129 |
130 | }
131 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/utils/AppUtils.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.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 | * Created by xuhao on 2017/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 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/utils/CleanLeakUtils.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.utils
2 |
3 | import android.content.Context
4 | import android.view.View
5 | import android.view.inputmethod.InputMethodManager
6 |
7 | import java.lang.reflect.Field
8 |
9 | /**
10 | * Created by xuhao on 2017/12/13.
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 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/utils/DensityUtil.java:
--------------------------------------------------------------------------------
1 |
2 | package com.jaygengi.gank.utils;
3 |
4 | import android.content.Context;
5 |
6 | public class DensityUtil {
7 |
8 | public static int getStatusBarHeight(Context context) {
9 | int result = 0;
10 | int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
11 | if (resourceId > 0) {
12 | result = context.getResources().getDimensionPixelSize(resourceId);
13 | }
14 | return result;
15 | }
16 |
17 | public static int dip2px(Context context, float dpValue) {
18 | final float scale = context.getResources().getDisplayMetrics().density;
19 | return (int) (dpValue * scale + 0.5f);
20 | }
21 |
22 | public static int dip2px(Context context, double dpValue) {
23 | float density = context.getResources().getDisplayMetrics().density;
24 | return (int) (dpValue * (double) density + 0.5D);
25 | }
26 |
27 | public static int px2dip(Context context, float pxValue) {
28 | final float scale = context.getResources().getDisplayMetrics().density;
29 | return (int) (pxValue / scale + 0.5f);
30 | }
31 |
32 | public static int px2sp(Context context, float pxValue) {
33 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
34 | return (int) (pxValue / fontScale + 0.5f);
35 | }
36 |
37 | public static int sp2px(Context context, float spValue) {
38 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
39 | return (int) (spValue * fontScale + 0.5f);
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/utils/DisplayManager.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.utils
2 |
3 | import android.content.Context
4 | import android.util.DisplayMetrics
5 |
6 | /**
7 | * Created by xuhao on 2017/11/27.
8 | * desc:
9 | */
10 |
11 | object DisplayManager {
12 | init {
13 |
14 | }
15 |
16 | private var displayMetrics: DisplayMetrics? = null
17 |
18 | private var screenWidth: Int? = null
19 |
20 | private var screenHeight: Int? = null
21 |
22 | private var screenDpi: Int? = null
23 |
24 | fun init(context: Context) {
25 | displayMetrics = context.resources.displayMetrics
26 | screenWidth = displayMetrics?.widthPixels
27 | screenHeight = displayMetrics?.heightPixels
28 | screenDpi = displayMetrics?.densityDpi
29 | }
30 |
31 |
32 | //UI图的大小
33 | private const val STANDARD_WIDTH = 1080
34 | private const val STANDARD_HEIGHT = 1920
35 |
36 |
37 | fun getScreenWidth(): Int? {
38 | return screenWidth
39 | }
40 |
41 | fun getScreenHeight(): Int? {
42 | return screenHeight
43 | }
44 |
45 |
46 | /**
47 | * 传入UI图中问题的高度,单位像素
48 | * @param size
49 | * @return
50 | */
51 | fun getPaintSize(size: Int): Int? {
52 | return getRealHeight(size)
53 | }
54 |
55 | /**
56 | * 输入UI图的尺寸,输出实际的px
57 | *
58 | * @param px ui图中的大小
59 | * @return
60 | */
61 | fun getRealWidth(px: Int): Int? {
62 | //ui图的宽度
63 | return getRealWidth(px, STANDARD_WIDTH.toFloat())
64 | }
65 |
66 | /**
67 | * 输入UI图的尺寸,输出实际的px,第二个参数是父布局
68 | *
69 | * @param px ui图中的大小
70 | * @param parentWidth 父view在ui图中的高度
71 | * @return
72 | */
73 | fun getRealWidth(px: Int, parentWidth: Float): Int? {
74 | return (px / parentWidth * getScreenWidth()!!).toInt()
75 | }
76 |
77 | /**
78 | * 输入UI图的尺寸,输出实际的px
79 | *
80 | * @param px ui图中的大小
81 | * @return
82 | */
83 | fun getRealHeight(px: Int): Int? {
84 | //ui图的宽度
85 | return getRealHeight(px, STANDARD_HEIGHT.toFloat())
86 | }
87 |
88 | /**
89 | * 输入UI图的尺寸,输出实际的px,第二个参数是父布局
90 | *
91 | * @param px ui图中的大小
92 | * @param parentHeight 父view在ui图中的高度
93 | * @return
94 | */
95 | fun getRealHeight(px: Int, parentHeight: Float): Int? {
96 | return (px / parentHeight * getScreenHeight()!!).toInt()
97 | }
98 |
99 | /**
100 | * dip转px
101 | * @param dipValue
102 | * @return int
103 | */
104 | fun dip2px(dipValue: Float): Int? {
105 | val scale = displayMetrics?.density
106 | return (dipValue * scale!! + 0.5f).toInt()
107 | }
108 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/utils/NetworkUtil.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.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 | * Created by xuhao on 2017/11/13.
16 | */
17 | class NetworkUtil{
18 |
19 | companion object {
20 |
21 | var NET_CNNT_BAIDU_OK = 1 // NetworkAvailable
22 | var NET_CNNT_BAIDU_TIMEOUT = 2 // no NetworkAvailable
23 | var NET_NOT_PREPARE = 3 // Net no ready
24 | var NET_ERROR = 4 //net error
25 | private val TIMEOUT = 3000 // TIMEOUT
26 | /**
27 | * check NetworkAvailable
28 | *
29 | * @param context
30 | * @return
31 | */
32 | @JvmStatic
33 | fun isNetworkAvailable(context: Context): Boolean {
34 | val manager = context.applicationContext.getSystemService(
35 | Context.CONNECTIVITY_SERVICE) as ConnectivityManager
36 | val info = manager.activeNetworkInfo
37 | return !(null == info || !info.isAvailable)
38 | }
39 |
40 | /**
41 | * 得到ip地址
42 | *
43 | * @return
44 | */
45 | @JvmStatic
46 | fun getLocalIpAddress(): String {
47 | var ret = ""
48 | try {
49 | val en = NetworkInterface.getNetworkInterfaces()
50 | while (en.hasMoreElements()) {
51 | val enumIpAddress = en.nextElement().inetAddresses
52 | while (enumIpAddress.hasMoreElements()) {
53 | val netAddress = enumIpAddress.nextElement()
54 | if (!netAddress.isLoopbackAddress) {
55 | ret = netAddress.hostAddress.toString()
56 | }
57 | }
58 | }
59 | } catch (ex: SocketException) {
60 | ex.printStackTrace()
61 | }
62 |
63 | return ret
64 | }
65 |
66 |
67 | /**
68 | * ping "http://www.baidu.com"
69 | *
70 | * @return
71 | */
72 | @JvmStatic
73 | private fun pingNetWork(): Boolean {
74 | var result = false
75 | var httpUrl: HttpURLConnection? = null
76 | try {
77 | httpUrl = URL("http://www.baidu.com")
78 | .openConnection() as HttpURLConnection
79 | httpUrl.connectTimeout = TIMEOUT
80 | httpUrl.connect()
81 | result = true
82 | } catch (e: IOException) {
83 | } finally {
84 | if (null != httpUrl) {
85 | httpUrl.disconnect()
86 | }
87 | }
88 | return result
89 | }
90 |
91 | /**
92 | * check is3G
93 | *
94 | * @param context
95 | * @return boolean
96 | */
97 | @JvmStatic
98 | fun is3G(context: Context): Boolean {
99 | val connectivityManager = context
100 | .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
101 | val activeNetInfo = connectivityManager.activeNetworkInfo
102 | return activeNetInfo != null && activeNetInfo.type == ConnectivityManager.TYPE_MOBILE
103 | }
104 |
105 | /**
106 | * isWifi
107 | *
108 | * @param context
109 | * @return boolean
110 | */
111 | @JvmStatic
112 | fun isWifi(context: Context): Boolean {
113 | val connectivityManager = context
114 | .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
115 | val activeNetInfo = connectivityManager.activeNetworkInfo
116 | return activeNetInfo != null && activeNetInfo.type == ConnectivityManager.TYPE_WIFI
117 | }
118 |
119 | /**
120 | * is2G
121 | *
122 | * @param context
123 | * @return boolean
124 | */
125 | @JvmStatic
126 | fun is2G(context: Context): Boolean {
127 | val connectivityManager = context
128 | .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
129 | val activeNetInfo = connectivityManager.activeNetworkInfo
130 | return activeNetInfo != null && (activeNetInfo.subtype == TelephonyManager.NETWORK_TYPE_EDGE
131 | || activeNetInfo.subtype == TelephonyManager.NETWORK_TYPE_GPRS || activeNetInfo
132 | .subtype == TelephonyManager.NETWORK_TYPE_CDMA)
133 | }
134 |
135 | /**
136 | * is wifi on
137 | */
138 | @JvmStatic
139 | fun isWifiEnabled(context: Context): Boolean {
140 | val mgrConn = context
141 | .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
142 | val mgrTel = context
143 | .getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
144 | return mgrConn.activeNetworkInfo != null && mgrConn
145 | .activeNetworkInfo.state == NetworkInfo.State.CONNECTED || mgrTel
146 | .networkType == TelephonyManager.NETWORK_TYPE_UMTS
147 | }
148 |
149 | }
150 |
151 |
152 |
153 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/utils/Preference.kt:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.utils
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import android.content.SharedPreferences
6 | import com.jaygengi.gank.MyApplication
7 | import java.io.*
8 | import kotlin.reflect.KProperty
9 |
10 | /**
11 | * Created by xuhao on 2017/12/11.
12 | * desc:kotlin委托属性+SharedPreference实例
13 | */
14 | class Preference(val name:String, private val default:T) {
15 |
16 |
17 | companion object {
18 | private const val file_name = "kotlin_mvp_file"
19 |
20 | private val prefs: SharedPreferences by lazy {
21 | MyApplication.context.getSharedPreferences(file_name, Context.MODE_PRIVATE)
22 | }
23 | /**
24 | * 删除全部数据
25 | */
26 | fun clearPreference(){
27 | prefs.edit().clear().apply()
28 | }
29 |
30 | /**
31 | * 根据key删除存储数据
32 | */
33 | fun clearPreference(key : String){
34 | prefs.edit().remove(key).apply()
35 | }
36 | }
37 |
38 |
39 |
40 |
41 | operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
42 | return getSharedPreferences(name, default)
43 | }
44 |
45 | operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
46 | putSharedPreferences(name, value)
47 | }
48 |
49 | @SuppressLint("CommitPrefEdits")
50 | private fun putSharedPreferences(name: String, value: T) = with(prefs.edit()) {
51 | when (value) {
52 | is Long -> putLong(name, value)
53 | is String -> putString(name, value)
54 | is Int -> putInt(name, value)
55 | is Boolean -> putBoolean(name, value)
56 | is Float -> putFloat(name, value)
57 | else -> putString(name,serialize(value))
58 | }.apply()
59 | }
60 |
61 | @Suppress("UNCHECKED_CAST")
62 | private fun getSharedPreferences(name: String, default: T): T = with(prefs) {
63 | val res: Any = when (default) {
64 | is Long -> getLong(name, default)
65 | is String -> getString(name, default)
66 | is Int -> getInt(name, default)
67 | is Boolean -> getBoolean(name, default)
68 | is Float -> getFloat(name, default)
69 | else -> deSerialization(getString(name,serialize(default)))
70 | }
71 | return res as T
72 | }
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 | objectOutputStream.writeObject(obj)
91 | var serStr = byteArrayOutputStream.toString("ISO-8859-1")
92 | serStr = java.net.URLEncoder.encode(serStr, "UTF-8")
93 | objectOutputStream.close()
94 | byteArrayOutputStream.close()
95 | return serStr
96 | }
97 |
98 | /**
99 | * 反序列化对象
100 |
101 | * @param str
102 | * *
103 | * @return
104 | * *
105 | * @throws IOException
106 | * *
107 | * @throws ClassNotFoundException
108 | */
109 | @Suppress("UNCHECKED_CAST")
110 | @Throws(IOException::class, ClassNotFoundException::class)
111 | private fun deSerialization(str: String): A {
112 | val redStr = java.net.URLDecoder.decode(str, "UTF-8")
113 | val byteArrayInputStream = ByteArrayInputStream(
114 | redStr.toByteArray(charset("ISO-8859-1")))
115 | val objectInputStream = ObjectInputStream(
116 | byteArrayInputStream)
117 | val obj = objectInputStream.readObject() as A
118 | objectInputStream.close()
119 | byteArrayInputStream.close()
120 | return obj
121 | }
122 |
123 |
124 | /**
125 | * 查询某个key是否已经存在
126 | *
127 | * @param key
128 | * @return
129 | */
130 | fun contains(key: String): Boolean {
131 | return prefs.contains(key)
132 | }
133 |
134 | /**
135 | * 返回所有的键值对
136 | *
137 | * @param context
138 | * @return
139 | */
140 | fun getAll(): Map {
141 | return prefs.all
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/utils/RxUtil.java:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.utils;
2 |
3 | import io.reactivex.FlowableTransformer;
4 | import io.reactivex.ObservableTransformer;
5 | import io.reactivex.android.schedulers.AndroidSchedulers;
6 | import io.reactivex.schedulers.Schedulers;
7 |
8 | /**
9 | * 描述:RxJava工具类
10 | *
11 | * @author JayGengi
12 | */
13 | public class RxUtil {
14 |
15 | /**
16 | * 统一线程处理
17 | *
18 | * @param
19 | * @return
20 | */
21 | public static FlowableTransformer rxSchedulerHelper() {
22 |
23 | //compose简化线程
24 | return observable -> observable.subscribeOn(Schedulers.io())
25 | .observeOn(AndroidSchedulers.mainThread());
26 | }
27 |
28 | /**
29 | * 统一线程处理
30 | *
31 | * @param
32 | * @return
33 | */
34 | public static ObservableTransformer rxObservableSchedulerHelper() {
35 |
36 | //compose简化线程
37 | return observable -> observable.subscribeOn(Schedulers.io())
38 | .observeOn(AndroidSchedulers.mainThread());
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/utils/img/GlideImageLoader.java:
--------------------------------------------------------------------------------
1 | package com.jaygengi.gank.utils.img;
2 |
3 | import android.content.Context;
4 | import android.widget.ImageView;
5 |
6 | import com.bumptech.glide.Glide;
7 | import com.youth.banner.loader.ImageLoader;
8 |
9 | /**
10 | * @description: Image加载器
11 | * @author JayGengi
12 | * @date 2018/11/13 0013 下午 5:18
13 | * @email jaygengiii@gmail.com
14 | */
15 |
16 | public class GlideImageLoader extends ImageLoader {
17 |
18 | @Override
19 | public void displayImage(Context context, Object path, ImageView imageView) {
20 | /**
21 | 注意:
22 | 1.图片加载器由自己选择,这里不限制,只是提供几种使用方法
23 | 2.返回的图片路径为Object类型,由于不能确定你到底使用的那种图片加载器,
24 | 传输的到的是什么格式,那么这种就使用Object接收和返回,你只需要强转成你传输的类型就行,
25 | 切记不要胡乱强转!
26 | */
27 |
28 | //Glide 加载图片简单用法
29 | Glide.with(context).load(path).into(imageView);
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaygengi/gank/widget/SlidingTabStrip.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.jaygengi.gank.widget;
18 |
19 | import android.content.Context;
20 | import android.graphics.Canvas;
21 | import android.graphics.Color;
22 | import android.graphics.Paint;
23 | import android.util.AttributeSet;
24 | import android.util.TypedValue;
25 | import android.view.View;
26 | import android.widget.LinearLayout;
27 |
28 | import com.jaygengi.gank.R;
29 | import com.jaygengi.gank.utils.DensityUtil;
30 |
31 |
32 | class SlidingTabStrip extends LinearLayout {
33 |
34 | private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
35 | private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
36 | private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
37 | private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
38 |
39 | private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
40 | private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
41 | private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
42 |
43 | private final int mBottomBorderThickness;
44 | private final Paint mBottomBorderPaint;
45 |
46 | private final int mSelectedIndicatorThickness;
47 | private final Paint mSelectedIndicatorPaint;
48 |
49 | private final int mDefaultBottomBorderColor;
50 |
51 | private final Paint mDividerPaint;
52 | private final float mDividerHeight;
53 |
54 | private int mSelectedPosition;
55 | private float mSelectionOffset;
56 |
57 | private SlidingTabLayout.TabColorizer mCustomTabColorizer;
58 | private final SimpleTabColorizer mDefaultTabColorizer;
59 |
60 | private Context context;
61 |
62 | private int stripLength;
63 |
64 | SlidingTabStrip(Context context) {
65 | this(context, null);
66 | }
67 |
68 | SlidingTabStrip(Context context, AttributeSet attrs) {
69 | super(context, attrs);
70 | this.context = context;
71 |
72 | setWillNotDraw(false);
73 |
74 | stripLength = DensityUtil.dip2px(context, 20f);
75 |
76 | final float density = getResources().getDisplayMetrics().density;
77 |
78 | TypedValue outValue = new TypedValue();
79 | //TODO
80 | context.getTheme().resolveAttribute(R.attr.colorPrimary, outValue, true);
81 | final int themeForegroundColor = outValue.data;
82 |
83 | mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
84 | DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
85 |
86 | mDefaultTabColorizer = new SimpleTabColorizer();
87 | mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
88 | mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
89 | DEFAULT_DIVIDER_COLOR_ALPHA));
90 |
91 | mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
92 | mBottomBorderPaint = new Paint();
93 | mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
94 |
95 | mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
96 | mSelectedIndicatorPaint = new Paint();
97 | mSelectedIndicatorPaint.setStrokeWidth(DensityUtil.dip2px(context, 2));
98 | mSelectedIndicatorPaint.setStrokeCap(Paint.Cap.ROUND);
99 |
100 | mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
101 | mDividerPaint = new Paint();
102 | mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
103 | this.setBackgroundColor(Color.parseColor("#00000000"));
104 | }
105 |
106 | void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
107 | mCustomTabColorizer = customTabColorizer;
108 | invalidate();
109 | }
110 |
111 | void setSelectedIndicatorColors(int... colors) {
112 | // Make sure that the custom colorizer is removed
113 | mCustomTabColorizer = null;
114 | mDefaultTabColorizer.setIndicatorColors(colors);
115 | invalidate();
116 | }
117 |
118 | void setDividerColors(int... colors) {
119 | // Make sure that the custom colorizer is removed
120 | mCustomTabColorizer = null;
121 | mDefaultTabColorizer.setDividerColors(colors);
122 | invalidate();
123 | }
124 |
125 | void setStripLength(int length){
126 | this.stripLength = length;
127 | invalidate();
128 | }
129 |
130 | void onViewPagerPageChanged(int position, float positionOffset) {
131 | mSelectedPosition = position;
132 | mSelectionOffset = positionOffset;
133 | invalidate();
134 | }
135 |
136 | @Override
137 | protected void onDraw(Canvas canvas) {
138 | final int height = getHeight();
139 | final int childCount = getChildCount();
140 | final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
141 | final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
142 | ? mCustomTabColorizer
143 | : mDefaultTabColorizer;
144 |
145 | // Thick colored underline below the current selection
146 | if (childCount > 0) {
147 |
148 | // int length = DensityUtil.dip2px(context, 20);
149 | int length = stripLength;
150 | View selectedTitle = getChildAt(mSelectedPosition);
151 | int left = selectedTitle.getLeft() + selectedTitle.getPaddingLeft();
152 | int right = selectedTitle.getRight() - selectedTitle.getPaddingRight();
153 |
154 | int middle = (left + right) / 2;
155 | int start = middle - length / 2;
156 | int end = middle + length / 2;
157 |
158 |
159 |
160 | int color = tabColorizer.getIndicatorColor(mSelectedPosition);
161 |
162 | if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
163 | int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
164 | if (color != nextColor) {
165 | color = blendColors(nextColor, color, mSelectionOffset);
166 | }
167 |
168 | View nextTitle = getChildAt(mSelectedPosition + 1);
169 |
170 | int nextLeft = nextTitle.getLeft() + nextTitle.getPaddingLeft();
171 | int nextRight = nextTitle.getRight() - nextTitle.getPaddingRight();
172 | int nextMiddle = (nextLeft + nextRight) / 2;
173 | int nextStart = nextMiddle - length / 2;
174 | int nextEnd = nextMiddle +length / 2;
175 |
176 | // Draw the selection partway between the tabs
177 | // View nextTitle = getChildAt(mSelectedPosition + 1);
178 | // left = (int) (mSelectionOffset * (nextTitle.getLeft() + length) +
179 | // (1.0f - mSelectionOffset) * (left + length));
180 | // right = (int) (mSelectionOffset * (nextTitle.getRight() + length) +
181 | // (1.0f - mSelectionOffset) * (right + length));
182 |
183 | // start = (int) (mSelectionOffset * nextStart + (1.0f - mSelectionOffset) * start);
184 | // end = (int) (mSelectionOffset * nextEnd + (1.0f - mSelectionOffset) * end);
185 | if (mSelectionOffset <= 0.5f) {
186 | // start = start;
187 | end = (int) ((0.5f - mSelectionOffset) / 0.5f * end + mSelectionOffset / 0.5f * nextEnd);
188 | } else {
189 | start = (int) ((1 - mSelectionOffset) / 0.5f * start + (mSelectionOffset - 0.5f) / 0.5f * nextStart);
190 | end = nextEnd;
191 | }
192 |
193 | }
194 |
195 | // mSelectedIndicatorPaint.setColor(color);
196 | mSelectedIndicatorPaint.setColor(getResources().getColor(R.color.md_blue_500));
197 |
198 | // canvas.drawRect(start, height - mSelectedIndicatorThickness, end,
199 | // height, mSelectedIndicatorPaint);
200 | // canvas.drawRect(start, height - Tools.dip2px(context, 2), end,
201 | // height, mSelectedIndicatorPaint);
202 | canvas.drawLine(start, height - DensityUtil.dip2px(context, 1), end, height - DensityUtil.dip2px(context, 1), mSelectedIndicatorPaint);
203 |
204 | }
205 |
206 | // Thin underline along the entire bottom edge
207 | // canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
208 |
209 | // Vertical separators between the titles
210 | // int separatorTop = (height - dividerHeightPx) / 2;
211 | // for (int i = 0; i < childCount - 1; i++) {
212 | // View child = getChildAt(i);
213 | // mDividerPaint.setColor(tabColorizer.getDividerColor(i));
214 | // canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
215 | // separatorTop + dividerHeightPx, mDividerPaint);
216 | // }
217 |
218 | }
219 |
220 | /**
221 | * Set the alpha value of the {@code color} to be the given {@code alpha} value.
222 | */
223 | private static int setColorAlpha(int color, byte alpha) {
224 | return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
225 | }
226 |
227 | /**
228 | * Blend {@code color1} and {@code color2} using the given ratio.
229 | *
230 | * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
231 | * 0.0 will return {@code color2}.
232 | */
233 | private static int blendColors(int color1, int color2, float ratio) {
234 | final float inverseRation = 1f - ratio;
235 | float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
236 | float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
237 | float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
238 | return Color.rgb((int) r, (int) g, (int) b);
239 | }
240 |
241 | private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
242 | private int[] mIndicatorColors;
243 | private int[] mDividerColors;
244 |
245 | @Override
246 | public final int getIndicatorColor(int position) {
247 | return mIndicatorColors[position % mIndicatorColors.length];
248 | }
249 |
250 | @Override
251 | public final int getDividerColor(int position) {
252 | return mDividerColors[position % mDividerColors.length];
253 | }
254 |
255 | void setIndicatorColors(int... colors) {
256 | mIndicatorColors = colors;
257 | }
258 |
259 | void setDividerColors(int... colors) {
260 | mDividerColors = colors;
261 | }
262 | }
263 | }
264 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/anim_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/anim_out.xml:
--------------------------------------------------------------------------------
1 |
4 |
8 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/push_bottom_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/push_bottom_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_left.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/line_h.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
12 |
13 |
18 |
19 |
20 |
26 |
27 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_search.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
12 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_splash.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
16 |
17 |
24 |
25 |
34 |
35 |
36 |
37 |
47 |
48 |
57 |
58 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_webview.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
12 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_app.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_gank_type.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
10 |
20 |
21 |
22 |
27 |
28 |
32 |
33 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
12 |
13 |
24 |
25 |
35 |
36 |
46 |
47 |
48 |
49 |
50 |
59 |
65 |
66 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_home_common_type.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
18 |
19 |
20 |
25 |
26 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_welfare.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
18 |
23 |
24 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_category.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
21 |
28 |
29 |
30 |
31 |
36 |
37 |
43 |
50 |
57 |
58 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_category_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
24 |
25 |
30 |
38 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_grils_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
13 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_home_today.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
17 |
18 |
26 |
27 |
35 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_home_today_img.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_today_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
19 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_empty_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_error_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_loading_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_network_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_action_clear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/app/src/main/res/mipmap-xhdpi/ic_action_clear.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_action_search_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/app/src/main/res/mipmap-xhdpi/ic_action_search_black.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_action_search_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/app/src/main/res/mipmap-xhdpi/ic_action_search_small.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_action_search_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/app/src/main/res/mipmap-xhdpi/ic_action_search_white.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_classification_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/app/src/main/res/mipmap-xhdpi/ic_classification_normal.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_classification_selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/app/src/main/res/mipmap-xhdpi/ic_classification_selected.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/app/src/main/res/mipmap-xhdpi/ic_error.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_home_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/app/src/main/res/mipmap-xhdpi/ic_home_normal.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_home_selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/app/src/main/res/mipmap-xhdpi/ic_home_selected.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_mine_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/app/src/main/res/mipmap-xhdpi/ic_mine_normal.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_mine_selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/app/src/main/res/mipmap-xhdpi/ic_mine_selected.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_no_data.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/app/src/main/res/mipmap-xhdpi/ic_no_data.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_no_network.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/app/src/main/res/mipmap-xhdpi/ic_no_network.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/img_jaygengi_avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/app/src/main/res/mipmap-xhdpi/img_jaygengi_avatar.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/transition-v21/arc_motion.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v19/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
--------------------------------------------------------------------------------
/app/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 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFFFFF
4 | #549cf8
5 | #EF5362
6 | #e1e1e1
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 | #2196F3
22 | #ff4081
23 | #dd2c00
24 | #ee447AA4
25 | #616161
26 |
27 |
28 |
29 | #CCFFFFFF
30 |
31 | #EEEEEE
32 | #E0E0E0
33 |
34 | #FFFFFF
35 | #000000
36 |
37 | #87CEEB
38 |
39 |
40 | #000000
41 |
42 | #ddd
43 | #aaa
44 |
45 | @color/color_lighter_gray
46 |
47 | #00000000
48 |
49 | #7FFFD4
50 |
51 | #5000
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/app/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 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Gank
3 |
4 |
5 | JayGengi
6 | 查看个人主页 >
7 |
8 |
9 |
10 | 加载中…
11 | 请检查网络后点击重新加载
12 | 数据获取失败(ㄒoㄒ)~~
13 | 暂时木有内容呀~~
14 | 干货集中营
15 | 每日分享妹子图 和 技术干货
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
14 |
17 |
27 |
31 |
32 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | ext.kotlin_version = '1.2.71'
4 | repositories {
5 | google()
6 | mavenCentral()
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 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | google()
20 | jcenter()
21 | mavenCentral()
22 | maven {
23 | url "https://jitpack.io"
24 | }
25 | }
26 |
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Oct 16 11:31:23 CST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/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 27
6 | buildToolsVersion "28.0.3"
7 |
8 | defaultConfig {
9 | minSdkVersion 18
10 | targetSdkVersion 27
11 | versionCode 1
12 | versionName "1.1.0"
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', ':multiple-status-view'
2 |
3 |
--------------------------------------------------------------------------------
/show/gank_type.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/show/gank_type.png
--------------------------------------------------------------------------------
/show/gankapp.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/show/gankapp.gif
--------------------------------------------------------------------------------
/show/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/show/home.png
--------------------------------------------------------------------------------
/show/img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/show/img.png
--------------------------------------------------------------------------------
/show/mine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/show/mine.png
--------------------------------------------------------------------------------
/show/type.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JayGengi/KotlinGankApp/8dd5d503160f7fbe4d1dd3313b3dab4e78446b6c/show/type.png
--------------------------------------------------------------------------------