├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── key.properties ├── libs │ ├── jsoup-1.11.2.jar │ └── lite-orm-1.9.2.jar ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── wanandroid │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-web.png │ ├── java │ │ └── com │ │ │ └── wanandroid │ │ │ ├── WanApplication.java │ │ │ ├── business │ │ │ ├── about │ │ │ │ └── AboutMeActivity.java │ │ │ ├── adapter │ │ │ │ ├── CidListAdapter.java │ │ │ │ └── WanAndroidArticleAdapter.java │ │ │ ├── articledetail │ │ │ │ ├── ArticleDetailActivity.java │ │ │ │ ├── ArticleDetailContract.java │ │ │ │ └── ArticleDetailPresenter.java │ │ │ ├── articles │ │ │ │ ├── ArticlesContract.java │ │ │ │ ├── ArticlesFragment.java │ │ │ │ └── ArticlesPresenter.java │ │ │ ├── base │ │ │ │ ├── BaseArticlesFragment.java │ │ │ │ ├── BaseMVPActivity.java │ │ │ │ ├── BaseMVPDialog.java │ │ │ │ ├── BaseMVPFragment.java │ │ │ │ ├── BasePresenter.java │ │ │ │ ├── BasePresenterImpl.java │ │ │ │ └── adapter │ │ │ │ │ ├── FooterRecyclerAdapter.java │ │ │ │ │ └── RefreshRecycleAdapter.java │ │ │ ├── callback │ │ │ │ ├── OnArticleFragmentRefreshListener.java │ │ │ │ ├── OnArticleItemClickListener.java │ │ │ │ ├── OnClassifyClickListener.java │ │ │ │ ├── OnSearchKeyClickListener.java │ │ │ │ └── OnSetToolbarTitleCallBack.java │ │ │ ├── cid │ │ │ │ ├── CidContract.java │ │ │ │ ├── CidFragment.java │ │ │ │ └── CidPresenter.java │ │ │ ├── classify │ │ │ │ ├── ClassifyContract.java │ │ │ │ ├── ClassifyDialog.java │ │ │ │ └── ClassifyPresenter.java │ │ │ ├── collects │ │ │ │ ├── CollectContract.java │ │ │ │ ├── CollectFragment.java │ │ │ │ └── CollectPresenter.java │ │ │ ├── login │ │ │ │ ├── LoginOrRegisterContract.java │ │ │ │ ├── LoginOrRegisterPresenter.java │ │ │ │ └── LoginOrResisterActivity.java │ │ │ ├── main │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainActivityHolder.java │ │ │ ├── search │ │ │ │ ├── SearchContract.java │ │ │ │ ├── SearchFragment.java │ │ │ │ └── SearchPresenter.java │ │ │ └── welcome │ │ │ │ ├── WelcomeActivity.java │ │ │ │ ├── WelcomeContract.java │ │ │ │ └── WelcomePresenter.java │ │ │ ├── model │ │ │ ├── ArticleData.java │ │ │ ├── AuthData.java │ │ │ ├── BaseResponseData.java │ │ │ ├── CidData.java │ │ │ ├── CollectedArticleData.java │ │ │ ├── HotKeyData.java │ │ │ ├── HotSearchData.java │ │ │ ├── SearchData.java │ │ │ ├── TreeData.java │ │ │ ├── api │ │ │ │ ├── WanAndroidApi.java │ │ │ │ ├── WanAndroidApiCompat.java │ │ │ │ ├── WanAndroidRetrofitClient.java │ │ │ │ └── utils │ │ │ │ │ ├── OkHttpClientManger.java │ │ │ │ │ ├── OkHttpConfig.java │ │ │ │ │ └── WanAndroidCookieJar.java │ │ │ ├── db │ │ │ │ └── UserManger.java │ │ │ └── entity │ │ │ │ ├── Article.java │ │ │ │ ├── Cid.java │ │ │ │ ├── CollectedArticle.java │ │ │ │ ├── HotKey.java │ │ │ │ ├── Tree.java │ │ │ │ └── WanAndroidUser.java │ │ │ ├── utils │ │ │ ├── ActivityUtils.java │ │ │ ├── ImeUtils.java │ │ │ ├── NetUtil.java │ │ │ ├── PackageUtils.java │ │ │ ├── ScreenUtil.java │ │ │ ├── SharedPreferencesUtil.java │ │ │ ├── SharesUtils.java │ │ │ ├── SystemBarTintManager.java │ │ │ └── ViewHolder.java │ │ │ └── widget │ │ │ ├── AutoClearEditText.java │ │ │ ├── CircleImageView.java │ │ │ ├── NestedScaleBehavior.java │ │ │ └── WebViewFragment.java │ └── res │ │ ├── drawable │ │ ├── classify_dialog_background.xml │ │ ├── classify_view_item_selector.xml │ │ ├── clear_selector.xml │ │ ├── edit_text_bg.xml │ │ ├── edit_text_cursor.xml │ │ ├── flow_item_selector.xml │ │ ├── ic_launcher_background.xml │ │ ├── login_button_selector.xml │ │ ├── progressbar_horizontal.xml │ │ ├── recycle_view_item_selector.xml │ │ ├── tip_tv_selector.xml │ │ └── welcome_user_bg.xml │ │ ├── layout │ │ ├── activity_about_me.xml │ │ ├── activity_login_or_resister.xml │ │ ├── activity_main.xml │ │ ├── activity_main_content.xml │ │ ├── activity_main_navigation_header.xml │ │ ├── activity_web.xml │ │ ├── activity_welcome.xml │ │ ├── dialg_classify_item.xml │ │ ├── dialog_classify.xml │ │ ├── dialog_progress.xml │ │ ├── fragment_article.xml │ │ ├── fragment_cid.xml │ │ ├── fragment_collect_list.xml │ │ ├── fragment_main.xml │ │ ├── fragment_search.xml │ │ ├── hot_key_item.xml │ │ ├── main_article_item_layout.xml │ │ └── main_article_load_more_layout.xml │ │ ├── menu │ │ ├── main_navigation_menu.xml │ │ ├── menu_main.xml │ │ └── menu_web.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_about.png │ │ ├── ic_back_to_top.png │ │ ├── ic_clear.png │ │ ├── ic_clear_normal.png │ │ ├── ic_clear_pressed.png │ │ ├── ic_collect.png │ │ ├── ic_feedback.png │ │ ├── ic_home.png │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ ├── ic_launcher_foreground_no_boder.png │ │ ├── ic_launcher_round.png │ │ ├── ic_menu.png │ │ ├── ic_more_classify.png │ │ ├── ic_progress_bg.png │ │ ├── ic_quit_login.png │ │ ├── ic_refresh.png │ │ ├── ic_search.png │ │ ├── ic_setting.png │ │ ├── ic_uncollect.png │ │ ├── ic_wanandroid_logo.png │ │ ├── ic_welcome_default.png │ │ ├── icon_password.png │ │ └── icon_user.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── wanandroid │ └── WanAndroidApiTest.java ├── build.gradle ├── doc └── 接口文档 │ ├── 更多接口说明.md │ └── 首页数据.md ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── release └── WanAndroid_1.0.2.apk └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea 3 | .gradle 4 | /local.properties 5 | /.idea/workspace.xml 6 | /.idea/libraries 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | /app/*.jks 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WanAndroid 2 | 3 | > 一个对应www.wanandroid.com的App 4 | 5 | + 整体架构使用 MVP+RxJava2+Retrofit2.0+OkHttp3.0 6 | 7 | ## 最新下载地址 8 | 9 | [v1.0.2](https://raw.githubusercontent.com/CB2Git/WanAndroid/master/release/WanAndroid_1.0.2.apk) 10 | 11 | ### 应用截图 12 | 13 | 懒得截图了.... 14 | 15 | 16 | 17 | > 参考 18 | 19 | 1. [Google MVP Demo](https://github.com/googlesamples/android-architecture) 20 | 2. [WanAndroid接口说明](http://www.wanandroid.com/blog/show/2) 21 | 22 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | //加载签名信息 4 | Properties properties = new Properties() 5 | File keyPropertiesFile = project.file('key.properties') 6 | boolean hasKeyFile = false 7 | if (keyPropertiesFile.exists()) { 8 | hasKeyFile = true 9 | properties.load(keyPropertiesFile.newDataInputStream()) 10 | } else { 11 | hasKeyFile = false 12 | } 13 | 14 | android { 15 | signingConfigs { 16 | release { 17 | keyAlias properties.getProperty("keyAlias") 18 | keyPassword properties.getProperty("keyPassword") 19 | storeFile file(properties.getProperty("storeFile")) 20 | storePassword properties.getProperty("storePassword") 21 | } 22 | } 23 | compileSdkVersion rootProject.ext.compileSdkVersion 24 | buildToolsVersion rootProject.ext.buildToolsVersion 25 | defaultConfig { 26 | applicationId "com.wanandroid" 27 | minSdkVersion rootProject.ext.minSdkVersion 28 | targetSdkVersion rootProject.ext.targetSdkVersion 29 | versionCode 4 30 | versionName "1.0.2" 31 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 32 | } 33 | buildTypes { 34 | 35 | debug { 36 | buildConfigField 'boolean', 'LOG_DEBUG', 'true' 37 | buildConfigField 'String', 'Buyly_App_id', '""' 38 | } 39 | release { 40 | buildConfigField 'boolean', 'LOG_DEBUG', 'false' 41 | buildConfigField 'String', 'Buyly_App_id', '"017b45fe26"' 42 | minifyEnabled false 43 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 44 | debuggable false 45 | if (hasKeyFile) { 46 | signingConfig signingConfigs.release 47 | } 48 | } 49 | } 50 | 51 | def appName = "WanAndroid" 52 | 53 | android.applicationVariants.all { variant -> 54 | variant.outputs.all { 55 | outputFileName = "${appName}_${variant.name}_${variant.versionName}.apk" 56 | } 57 | } 58 | } 59 | 60 | dependencies { 61 | compile fileTree(include: ['*.jar'], dir: 'libs') 62 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 63 | exclude group: 'com.android.support', module: 'support-annotations' 64 | }) 65 | compile files('libs/jsoup-1.11.2.jar') 66 | compile "com.android.support:appcompat-v7:$supportVersion" 67 | compile "com.android.support:design:$supportVersion" 68 | compile "com.android.support:recyclerview-v7:$supportVersion" 69 | compile "com.android.support:cardview-v7:$supportVersion" 70 | compile 'com.squareup.retrofit2:retrofit:2.3.0' 71 | compile 'com.squareup.retrofit2:converter-gson:2.3.0' 72 | compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' 73 | compile 'com.squareup.okhttp3:okhttp:3.9.1' 74 | compile 'com.squareup.okhttp3:logging-interceptor:3.9.1' 75 | compile 'io.reactivex.rxjava2:rxandroid:2.0.1' 76 | compile 'io.reactivex.rxjava2:rxjava:2.1.7' 77 | compile 'com.hyman:flowlayout-lib:1.1.2' 78 | compile 'com.wang.avi:library:2.1.3' 79 | compile 'com.tencent.bugly:crashreport_upgrade:latest.release' 80 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 81 | debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.4' 82 | releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4' 83 | testCompile 'junit:junit:4.12' 84 | implementation files('libs/lite-orm-1.9.2.jar') 85 | } 86 | -------------------------------------------------------------------------------- /app/key.properties: -------------------------------------------------------------------------------- 1 | keyAlias=123456 2 | keyPassword=123456 3 | storeFile=key.jks 4 | storePassword=123456 -------------------------------------------------------------------------------- /app/libs/jsoup-1.11.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CB2Git/WanAndroid/cf6bb28c772f1ab3496176848c41455d6c947213/app/libs/jsoup-1.11.2.jar -------------------------------------------------------------------------------- /app/libs/lite-orm-1.9.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CB2Git/WanAndroid/cf6bb28c772f1ab3496176848c41455d6c947213/app/libs/lite-orm-1.9.2.jar -------------------------------------------------------------------------------- /app/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 E:\Android\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 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | 27 | # buyly 28 | -dontwarn com.tencent.bugly.** 29 | -keep public class com.tencent.bugly.**{*;} 30 | -keep class android.support.**{*;} 31 | 32 | # com.wang.avi:library:2.1.3 33 | -keep class com.wang.avi.** { *; } 34 | -keep class com.wang.avi.indicators.** { *; } 35 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/wanandroid/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import com.litesuits.orm.log.OrmLog; 8 | import com.wanandroid.model.db.UserManger; 9 | import com.wanandroid.model.entity.WanAndroidUser; 10 | 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | import static org.junit.Assert.assertEquals; 18 | import static org.junit.Assert.assertNull; 19 | 20 | /** 21 | * Instrumentation test, which will execute on an Android device. 22 | * 23 | * @see Testing documentation 24 | */ 25 | @RunWith(AndroidJUnit4.class) 26 | public class ExampleInstrumentedTest { 27 | @Test 28 | public void useAppContext() throws Exception { 29 | // Context of the app under test. 30 | Context appContext = InstrumentationRegistry.getTargetContext(); 31 | 32 | assertEquals("com.wanandroid", appContext.getPackageName()); 33 | } 34 | 35 | @Test 36 | public void testUserManger() throws Exception { 37 | WanAndroidUser userInfo = UserManger.getUserInfo(); 38 | assertNull(userInfo); 39 | 40 | List mlit = new ArrayList<>(); 41 | mlit.add(1); 42 | mlit.add(2); 43 | 44 | userInfo = new WanAndroidUser(); 45 | userInfo.setId(123); 46 | userInfo.setUsername("ceshi "); 47 | userInfo.setPassword("测试"); 48 | userInfo.setCollectIds(mlit); 49 | 50 | UserManger.updateUserInfo(userInfo); 51 | 52 | userInfo = UserManger.getUserInfo(); 53 | OrmLog.i(userInfo); 54 | 55 | WanAndroidUser k = new WanAndroidUser(); 56 | k.setId(123); 57 | k.setUsername("ceshi 233"); 58 | k.setPassword("测试3333"); 59 | k.setCollectIds(null); 60 | UserManger.updateUserInfo(k); 61 | userInfo = UserManger.getUserInfo(); 62 | OrmLog.i(userInfo); 63 | 64 | 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 21 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 36 | 37 | 42 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CB2Git/WanAndroid/cf6bb28c772f1ab3496176848c41455d6c947213/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/WanApplication.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.text.TextUtils; 6 | 7 | import com.litesuits.orm.LiteOrm; 8 | import com.litesuits.orm.db.DataBaseConfig; 9 | import com.squareup.leakcanary.LeakCanary; 10 | import com.tencent.bugly.Bugly; 11 | import com.tencent.bugly.crashreport.CrashReport; 12 | 13 | import java.io.BufferedReader; 14 | import java.io.FileReader; 15 | import java.io.IOException; 16 | 17 | /** 18 | * @author Jay 19 | */ 20 | public class WanApplication extends Application { 21 | 22 | private static final String DB_NAME = "wanandroid.db"; 23 | 24 | public static LiteOrm liteOrm; 25 | 26 | private static Context mContext; 27 | 28 | @Override 29 | public void onCreate() { 30 | super.onCreate(); 31 | //link:https://github.com/square/leakcanary 32 | if (LeakCanary.isInAnalyzerProcess(this)) { 33 | // This process is dedicated to LeakCanary for heap analysis. 34 | // You should not init your app in this process. 35 | return; 36 | } 37 | LeakCanary.install(this); 38 | initBuyly(); 39 | initLiteOrm(); 40 | mContext = this; 41 | } 42 | 43 | private void initLiteOrm() { 44 | DataBaseConfig config = new DataBaseConfig(this); 45 | config.debugged = BuildConfig.LOG_DEBUG; 46 | config.dbName = DB_NAME; 47 | config.dbVersion = 1; 48 | config.onUpdateListener = null; 49 | liteOrm = LiteOrm.newSingleInstance(config); 50 | } 51 | 52 | private void initBuyly() { 53 | if (BuildConfig.LOG_DEBUG) { 54 | return; 55 | } 56 | Context context = getApplicationContext(); 57 | // 获取当前包名 58 | String packageName = context.getPackageName(); 59 | // 获取当前进程名 60 | String processName = getProcessName(android.os.Process.myPid()); 61 | // 设置是否为上报进程 62 | CrashReport.UserStrategy strategy = new CrashReport.UserStrategy(context); 63 | strategy.setUploadProcess(processName == null || processName.equals(packageName)); 64 | // 初始化Bugly 65 | Bugly.init(context, BuildConfig.Buyly_App_id, BuildConfig.LOG_DEBUG, strategy); 66 | } 67 | 68 | public static final Context getAppContext() { 69 | return mContext; 70 | } 71 | 72 | /** 73 | * 获取进程号对应的进程名 74 | * 75 | * @param pid 进程号 76 | * @return 进程名 77 | */ 78 | private static String getProcessName(int pid) { 79 | BufferedReader reader = null; 80 | try { 81 | reader = new BufferedReader(new FileReader("/proc/" + pid + "/cmdline")); 82 | String processName = reader.readLine(); 83 | if (!TextUtils.isEmpty(processName)) { 84 | processName = processName.trim(); 85 | } 86 | return processName; 87 | } catch (Throwable throwable) { 88 | throwable.printStackTrace(); 89 | } finally { 90 | try { 91 | if (reader != null) { 92 | reader.close(); 93 | } 94 | } catch (IOException exception) { 95 | exception.printStackTrace(); 96 | } 97 | } 98 | return null; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/about/AboutMeActivity.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.about; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.support.v7.widget.Toolbar; 6 | import android.view.MenuItem; 7 | import android.widget.TextView; 8 | 9 | import com.wanandroid.BuildConfig; 10 | import com.wanandroid.R; 11 | 12 | /** 13 | * 关于我 14 | */ 15 | public class AboutMeActivity extends AppCompatActivity { 16 | 17 | private Toolbar mToolbar; 18 | 19 | private TextView mVersion; 20 | 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_about_me); 25 | initView(); 26 | } 27 | 28 | private void initView() { 29 | mToolbar = findViewById(R.id.about_toolbar); 30 | setSupportActionBar(mToolbar); 31 | if (getSupportActionBar() != null) { 32 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 33 | } 34 | mVersion = findViewById(R.id.about_version); 35 | mVersion.setText("Version:" + BuildConfig.VERSION_NAME); 36 | } 37 | 38 | @Override 39 | public boolean onOptionsItemSelected(MenuItem item) { 40 | if (item.getItemId() == android.R.id.home) { 41 | finish(); 42 | } 43 | return super.onOptionsItemSelected(item); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/adapter/CidListAdapter.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.adapter; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.BaseAdapter; 8 | import android.widget.TextView; 9 | 10 | import com.wanandroid.R; 11 | import com.wanandroid.model.entity.Tree; 12 | import com.wanandroid.utils.ViewHolder; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | /** 18 | * 知识体系分类的Adapter 19 | */ 20 | public class CidListAdapter extends BaseAdapter { 21 | 22 | private Context mContext; 23 | 24 | private List mCids; 25 | 26 | private int mCurrSelect = -1; 27 | 28 | public CidListAdapter(Context context) { 29 | mContext = context; 30 | mCids = new ArrayList<>(); 31 | } 32 | 33 | public CidListAdapter(Context context, List cids) { 34 | mContext = context; 35 | mCids = cids; 36 | } 37 | 38 | public void setCids(List newCids) { 39 | mCurrSelect = -1; 40 | mCids.clear(); 41 | mCids.addAll(newCids); 42 | notifyDataSetChanged(); 43 | } 44 | 45 | @Override 46 | public int getCount() { 47 | return mCids == null ? 0 : mCids.size(); 48 | } 49 | 50 | @Override 51 | public Object getItem(int position) { 52 | return mCids == null ? null : mCids.get(position); 53 | } 54 | 55 | @Override 56 | public long getItemId(int position) { 57 | return position; 58 | } 59 | 60 | public void setCurrSelect(int currSelect) { 61 | mCurrSelect = currSelect; 62 | notifyDataSetChanged(); 63 | } 64 | 65 | @Override 66 | public View getView(int position, View convertView, ViewGroup parent) { 67 | if (convertView == null) { 68 | LayoutInflater inflater = LayoutInflater.from(mContext); 69 | convertView = inflater.inflate(R.layout.dialg_classify_item, parent, false); 70 | } 71 | Tree item = (Tree) getItem(position); 72 | TextView tv = ViewHolder.get(convertView, R.id.classify_cid_text); 73 | tv.setText(item.getName()); 74 | tv.setEnabled(mCurrSelect != position); 75 | return convertView; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/articledetail/ArticleDetailContract.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.articledetail; 2 | 3 | 4 | import com.wanandroid.model.entity.WanAndroidUser; 5 | 6 | public interface ArticleDetailContract { 7 | 8 | interface View { 9 | void collectSuccess(); 10 | 11 | void collectFail(); 12 | 13 | void unCollectSuccess(); 14 | 15 | void unCollectFail(); 16 | } 17 | 18 | interface Presenter { 19 | 20 | WanAndroidUser getUserInfo(); 21 | 22 | void collectArticle(int id); 23 | 24 | void unCollectArticle(int id); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/articledetail/ArticleDetailPresenter.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.articledetail; 2 | 3 | import android.util.Log; 4 | 5 | import com.wanandroid.business.base.BasePresenterImpl; 6 | import com.wanandroid.model.BaseResponseData; 7 | import com.wanandroid.model.api.WanAndroidRetrofitClient; 8 | import com.wanandroid.model.db.UserManger; 9 | import com.wanandroid.model.entity.WanAndroidUser; 10 | 11 | import io.reactivex.android.schedulers.AndroidSchedulers; 12 | import io.reactivex.disposables.Disposable; 13 | import io.reactivex.functions.Consumer; 14 | import io.reactivex.schedulers.Schedulers; 15 | 16 | /** 17 | * Created by ${jay} on ${2016/8/17 18 | */ 19 | 20 | public class ArticleDetailPresenter extends BasePresenterImpl implements ArticleDetailContract.Presenter { 21 | 22 | private static final String TAG = "ArticleDetailPresenter"; 23 | 24 | @Override 25 | public WanAndroidUser getUserInfo() { 26 | return UserManger.getUserInfo(); 27 | } 28 | 29 | @Override 30 | public void collectArticle(int id) { 31 | Disposable subscribe = WanAndroidRetrofitClient 32 | .getApiService() 33 | .collectArticle(id) 34 | .observeOn(AndroidSchedulers.mainThread()) 35 | .subscribeOn(Schedulers.io()) 36 | .subscribe(new Consumer() { 37 | @Override 38 | public void accept(BaseResponseData baseResponseData) throws Exception { 39 | if (baseResponseData.errorCode == 0) { 40 | getView().collectSuccess(); 41 | } else { 42 | getView().collectFail(); 43 | } 44 | } 45 | }, new Consumer() { 46 | @Override 47 | public void accept(Throwable throwable) throws Exception { 48 | Log.e(TAG, "accept: " + throwable.getMessage()); 49 | } 50 | }); 51 | addDisposable(subscribe); 52 | } 53 | 54 | @Override 55 | public void unCollectArticle(int id) { 56 | Disposable disposable = WanAndroidRetrofitClient 57 | .getApiService() 58 | .unCollectArticle(id) 59 | .observeOn(AndroidSchedulers.mainThread()) 60 | .subscribeOn(Schedulers.io()) 61 | .subscribe(new Consumer() { 62 | @Override 63 | public void accept(BaseResponseData baseResponseData) throws Exception { 64 | if (baseResponseData.errorCode == 0) { 65 | getView().unCollectSuccess(); 66 | } else { 67 | getView().unCollectFail(); 68 | } 69 | } 70 | }, new Consumer() { 71 | @Override 72 | public void accept(Throwable throwable) throws Exception { 73 | Log.e(TAG, "accept: " + throwable.getMessage()); 74 | } 75 | }); 76 | addDisposable(disposable); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/articles/ArticlesContract.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.articles; 2 | 3 | import com.wanandroid.model.entity.Article; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * 主界面契约类,主要定义P、V层的接口 9 | */ 10 | public interface ArticlesContract { 11 | 12 | interface View { 13 | 14 | void hideLoading(); 15 | 16 | void hideLoadMore(); 17 | 18 | void showErrorMsg(String ErrorMsg); 19 | 20 | /** 21 | * 展示文章列表 22 | * 23 | * @param articles 获取到的文章数据 24 | * @param clearOld 是否清除旧数据 25 | */ 26 | void displayArticles(List
articles, boolean clearOld); 27 | } 28 | 29 | interface Presenter { 30 | 31 | /** 32 | * 加载下一页数据 33 | */ 34 | void loadNextPage(); 35 | 36 | /** 37 | * 刷新所有数据,清除旧有数据 38 | */ 39 | void refreshArticles(); 40 | 41 | void cancelCurrentLoadArticles(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/articles/ArticlesFragment.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.articles; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.design.widget.Snackbar; 6 | import android.support.v4.widget.SwipeRefreshLayout; 7 | import android.util.Log; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | 12 | import com.wanandroid.R; 13 | import com.wanandroid.business.base.BaseArticlesFragment; 14 | import com.wanandroid.business.callback.OnArticleFragmentRefreshListener; 15 | import com.wanandroid.model.entity.Article; 16 | 17 | import java.util.List; 18 | 19 | /** 20 | * 显示首页数据的Fragment 21 | */ 22 | public class ArticlesFragment extends BaseArticlesFragment implements ArticlesContract.View { 23 | 24 | private static final String TAG = "ArticlesFragment"; 25 | 26 | @Override 27 | public View bindFragmentView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 28 | View root = inflater.inflate(R.layout.fragment_main, container, false); 29 | return root; 30 | } 31 | 32 | @Override 33 | public void onActivityCreated(@Nullable Bundle savedInstanceState) { 34 | super.onActivityCreated(savedInstanceState); 35 | initView(); 36 | loadData(); 37 | } 38 | 39 | private void initView() { 40 | enablePullBottomRefresh(true); 41 | enablePullRefresh(true); 42 | setOnArticleFragmentRefreshListener(getOnArticleFragmentRefreshListener()); 43 | } 44 | 45 | @Override 46 | public ArticlesPresenter bindPresenter() { 47 | return new ArticlesPresenter(); 48 | } 49 | 50 | @Override 51 | public ArticlesContract.View bindView() { 52 | return this; 53 | } 54 | 55 | private void loadData() { 56 | //TODO 做一个预加载 57 | getBindPresenter().refreshArticles(); 58 | } 59 | 60 | public boolean onBackPressed() { 61 | SwipeRefreshLayout swipeRefreshLayout = getSwipeRefreshLayout(); 62 | if (swipeRefreshLayout != null && swipeRefreshLayout.isRefreshing()) { 63 | swipeRefreshLayout.setRefreshing(false); 64 | getBindPresenter().cancelCurrentLoadArticles(); 65 | return true; 66 | } 67 | return false; 68 | } 69 | 70 | @Override 71 | 72 | public void hideLoading() { 73 | SwipeRefreshLayout swipeRefreshLayout = getSwipeRefreshLayout(); 74 | if (swipeRefreshLayout != null) { 75 | swipeRefreshLayout.setRefreshing(false); 76 | } 77 | } 78 | 79 | @Override 80 | public void hideLoadMore() { 81 | getArticleAdapter().setRefreshing(false); 82 | } 83 | 84 | @Override 85 | public void showErrorMsg(String errorMsg) { 86 | Log.i(TAG, "showErrorMsg: " + errorMsg); 87 | getSwipeRefreshLayout().setRefreshing(false); 88 | Snackbar.make(getSwipeRefreshLayout(), R.string.load_more_error, Snackbar.LENGTH_SHORT).show(); 89 | } 90 | 91 | @Override 92 | public void displayArticles(List
articles, boolean clearOld) { 93 | if (clearOld) { 94 | getArticleAdapter().setArticles(articles); 95 | getRecyclerView().scrollToPosition(0); 96 | } else { 97 | getArticleAdapter().addArticles(articles); 98 | } 99 | } 100 | 101 | public OnArticleFragmentRefreshListener getOnArticleFragmentRefreshListener() { 102 | return new OnArticleFragmentRefreshListener() { 103 | @Override 104 | public void onPullTopRefresh() { 105 | getBindPresenter().cancelCurrentLoadArticles(); 106 | getBindPresenter().refreshArticles(); 107 | } 108 | 109 | @Override 110 | public void onPullBottomRefresh() { 111 | getArticleAdapter().setRefreshing(true); 112 | getBindPresenter().loadNextPage(); 113 | } 114 | }; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/articles/ArticlesPresenter.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.articles; 2 | 3 | import com.wanandroid.model.ArticleData; 4 | import com.wanandroid.model.api.WanAndroidRetrofitClient; 5 | import com.wanandroid.model.entity.Article; 6 | import com.wanandroid.business.base.BasePresenterImpl; 7 | 8 | import java.util.List; 9 | 10 | import io.reactivex.android.schedulers.AndroidSchedulers; 11 | import io.reactivex.annotations.NonNull; 12 | import io.reactivex.disposables.Disposable; 13 | import io.reactivex.functions.Action; 14 | import io.reactivex.functions.Consumer; 15 | import io.reactivex.functions.Function; 16 | import io.reactivex.schedulers.Schedulers; 17 | 18 | /** 19 | * Created by ${jay} on ${2016/8/17 20 | */ 21 | public class ArticlesPresenter extends BasePresenterImpl implements ArticlesContract.Presenter { 22 | 23 | private Disposable mLastLoadArticles; 24 | 25 | private int mCurrPage = 0; 26 | 27 | public ArticlesPresenter() { 28 | } 29 | 30 | @Override 31 | public void loadNextPage() { 32 | mLastLoadArticles = WanAndroidRetrofitClient 33 | .getApiService() 34 | .getArticleData(mCurrPage + 1) 35 | .map(new Function>() { 36 | @Override 37 | public List
apply(@NonNull ArticleData articleData) throws Exception { 38 | return articleData.getData().getDatas(); 39 | } 40 | }) 41 | .subscribeOn(Schedulers.io()) 42 | .observeOn(AndroidSchedulers.mainThread()) 43 | .doOnComplete(new Action() { 44 | @Override 45 | public void run() throws Exception { 46 | if (isViewAttached()) { 47 | getView().hideLoadMore(); 48 | } 49 | } 50 | }) 51 | .subscribe(new Consumer>() { 52 | @Override 53 | public void accept(List
articles) throws Exception { 54 | if (isViewAttached()) { 55 | mCurrPage++; 56 | getView().displayArticles(articles, false); 57 | } 58 | } 59 | }, new Consumer() { 60 | @Override 61 | public void accept(Throwable throwable) throws Exception { 62 | if (isViewAttached()) { 63 | getView().showErrorMsg(throwable.getMessage()); 64 | } 65 | } 66 | }); 67 | addDisposable(mLastLoadArticles); 68 | } 69 | 70 | @Override 71 | public void refreshArticles() { 72 | mLastLoadArticles = WanAndroidRetrofitClient 73 | .getApiService() 74 | .getArticleData(0) 75 | .map(new Function>() { 76 | @Override 77 | public List
apply(@NonNull ArticleData articleData) throws Exception { 78 | return articleData.getData().getDatas(); 79 | } 80 | }) 81 | .subscribeOn(Schedulers.io()) 82 | .observeOn(AndroidSchedulers.mainThread()) 83 | .doOnComplete(new Action() { 84 | @Override 85 | public void run() throws Exception { 86 | if (isViewAttached()) { 87 | getView().hideLoading(); 88 | } 89 | } 90 | }) 91 | .subscribe(new Consumer>() { 92 | @Override 93 | public void accept(List
articles) throws Exception { 94 | if (isViewAttached()) { 95 | mCurrPage = 0; 96 | getView().displayArticles(articles, true); 97 | } 98 | } 99 | }, new Consumer() { 100 | @Override 101 | public void accept(Throwable throwable) throws Exception { 102 | if (isViewAttached()) { 103 | getView().showErrorMsg(throwable.getMessage()); 104 | } 105 | } 106 | }); 107 | addDisposable(mLastLoadArticles); 108 | } 109 | 110 | @Override 111 | public void cancelCurrentLoadArticles() { 112 | if (mLastLoadArticles != null && !mLastLoadArticles.isDisposed()) { 113 | mLastLoadArticles.dispose(); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/base/BaseMVPActivity.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.base; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | 7 | /** 8 | * MVP公共类 9 | *

10 | * 负责View以及Presenter的自动绑定与解绑 11 | *

12 | */ 13 | public abstract class BaseMVPActivity> extends AppCompatActivity { 14 | 15 | private P mPresenter; 16 | 17 | private V mView; 18 | 19 | @Override 20 | protected void onCreate(@Nullable Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | mPresenter = bindPresenter(); 23 | mView = bindView(); 24 | if (mPresenter != null) { 25 | mPresenter.attachView(mView); 26 | } 27 | } 28 | 29 | public abstract P bindPresenter(); 30 | 31 | public abstract V bindView(); 32 | 33 | protected P getBindPresenter() { 34 | return mPresenter; 35 | } 36 | 37 | protected V getBindView() { 38 | return mView; 39 | } 40 | 41 | @Override 42 | protected void onDestroy() { 43 | if (mPresenter != null) { 44 | mPresenter.detachView(); 45 | } 46 | super.onDestroy(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/base/BaseMVPDialog.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.base; 2 | 3 | import android.app.Dialog; 4 | import android.content.Context; 5 | import android.support.annotation.NonNull; 6 | import android.support.annotation.Nullable; 7 | import android.support.annotation.StyleRes; 8 | 9 | /** 10 | * Created by ${jay} on ${2016/8/17 11 | */ 12 | public abstract class BaseMVPDialog> extends Dialog { 13 | 14 | private static final String TAG = "BaseMVPDialog"; 15 | 16 | private P mPresenter; 17 | 18 | private V mView; 19 | 20 | public BaseMVPDialog(@NonNull Context context) { 21 | super(context); 22 | } 23 | 24 | public BaseMVPDialog(@NonNull Context context, @StyleRes int themeResId) { 25 | super(context, themeResId); 26 | } 27 | 28 | public BaseMVPDialog(@NonNull Context context, boolean cancelable, @Nullable OnCancelListener cancelListener) { 29 | super(context, cancelable, cancelListener); 30 | } 31 | 32 | @Override 33 | protected void onStart() { 34 | super.onStart(); 35 | mPresenter = bindPresenter(); 36 | mView = bindView(); 37 | if (mPresenter != null) { 38 | mPresenter.attachView(mView); 39 | } 40 | } 41 | 42 | @Override 43 | protected void onStop() { 44 | if (mPresenter != null) { 45 | mPresenter.detachView(); 46 | } 47 | super.onStop(); 48 | } 49 | 50 | public abstract P bindPresenter(); 51 | 52 | public abstract V bindView(); 53 | 54 | protected P getBindPresenter() { 55 | return mPresenter; 56 | } 57 | 58 | protected V getBindView() { 59 | return mView; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/base/BaseMVPFragment.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.base; 2 | 3 | import android.content.Context; 4 | import android.support.v4.app.Fragment; 5 | 6 | /** 7 | * Created by ${jay} on ${2016/8/17 8 | */ 9 | 10 | public abstract class BaseMVPFragment> extends Fragment { 11 | 12 | private P mPresenter; 13 | 14 | private V mView; 15 | 16 | @Override 17 | public void onAttach(Context context) { 18 | super.onAttach(context); 19 | mPresenter = bindPresenter(); 20 | mView = bindView(); 21 | if (mPresenter != null) { 22 | mPresenter.attachView(mView); 23 | } 24 | } 25 | 26 | @Override 27 | public void onDetach() { 28 | if (mPresenter != null) { 29 | mPresenter.detachView(); 30 | } 31 | super.onDetach(); 32 | } 33 | 34 | public abstract P bindPresenter(); 35 | 36 | public abstract V bindView(); 37 | 38 | protected P getBindPresenter() { 39 | return mPresenter; 40 | } 41 | 42 | protected V getBindView() { 43 | return mView; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/base/BasePresenter.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.base; 2 | 3 | /** 4 | * BasePresenter,负责Presenter的公共操作 5 | *

6 | * 参考《Android源码设计模式与实战》第二十六章 7 | *

8 | */ 9 | public interface BasePresenter { 10 | 11 | void attachView(V view); 12 | 13 | void detachView(); 14 | 15 | V getView(); 16 | 17 | boolean isViewAttached(); 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/base/BasePresenterImpl.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.base; 2 | 3 | import java.lang.ref.Reference; 4 | import java.lang.ref.WeakReference; 5 | 6 | import io.reactivex.disposables.CompositeDisposable; 7 | import io.reactivex.disposables.Disposable; 8 | 9 | /** 10 | * 将Presenter的一些公共操作抽取到这里 11 | *

12 | * 负责RxJava的解绑以及View的公共操作 13 | *

14 | */ 15 | public class BasePresenterImpl implements BasePresenter { 16 | 17 | private static final String TAG = "BasePresenterImpl"; 18 | 19 | private CompositeDisposable mCompositeDisposable = new CompositeDisposable(); 20 | 21 | private Reference mReference; 22 | 23 | @Override 24 | public void attachView(V view) { 25 | mReference = new WeakReference(view); 26 | } 27 | 28 | @Override 29 | public void detachView() { 30 | dispose(); 31 | if (mReference != null) { 32 | mReference.clear(); 33 | mReference = null; 34 | } 35 | } 36 | 37 | @Override 38 | public V getView() { 39 | return mReference == null ? null : mReference.get(); 40 | } 41 | 42 | @Override 43 | public boolean isViewAttached() { 44 | return getView() != null; 45 | } 46 | 47 | protected void addDisposable(Disposable disposable) { 48 | if (mCompositeDisposable == null) { 49 | synchronized (this) { 50 | if (mCompositeDisposable == null) { 51 | mCompositeDisposable = new CompositeDisposable(); 52 | } 53 | } 54 | } 55 | mCompositeDisposable.add(disposable); 56 | } 57 | 58 | protected void dispose() { 59 | if (mCompositeDisposable != null) { 60 | mCompositeDisposable.dispose(); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/base/adapter/FooterRecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.base.adapter; 2 | 3 | import android.os.Handler; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.ViewGroup; 6 | 7 | /** 8 | * 拥有底部布局的RecycleAdapter 9 | */ 10 | public abstract class FooterRecyclerAdapter extends RecyclerView.Adapter { 11 | 12 | public static final int VIEW_TYPE_NORMAL = 0; 13 | 14 | public static final int VIEW_TYPE_FOOTER = -1; 15 | 16 | //底部布局是否显示 17 | private boolean isFooterVisible = false; 18 | 19 | public FooterRecyclerAdapter() { 20 | } 21 | 22 | @Override 23 | public T onCreateViewHolder(ViewGroup parent, int viewType) { 24 | if (viewType == VIEW_TYPE_FOOTER) { 25 | return onCreateFooterViewHolder(parent); 26 | } 27 | return onCreateNormalViewHolder(parent, viewType); 28 | } 29 | 30 | @Override 31 | public void onBindViewHolder(T holder, int position) { 32 | if (holder.getItemViewType() == VIEW_TYPE_FOOTER) { 33 | onBindFooterViewHolder(holder); 34 | return; 35 | } 36 | onBindNormalViewHolder(holder, position); 37 | } 38 | 39 | /** 40 | * 创建普通的视图 41 | */ 42 | protected abstract T onCreateNormalViewHolder(ViewGroup parent, int viewType); 43 | 44 | /** 45 | * 绑定普通的视图 46 | */ 47 | protected abstract void onBindNormalViewHolder(T holder, int position); 48 | 49 | /** 50 | * 创建底部视图 51 | */ 52 | protected abstract T onCreateFooterViewHolder(ViewGroup parent); 53 | 54 | /** 55 | * 绑定底部视图 56 | */ 57 | protected abstract void onBindFooterViewHolder(T holder); 58 | 59 | /** 60 | * 获取正常布局的个数 61 | */ 62 | public abstract int getNormalItemCount(); 63 | 64 | @Override 65 | public int getItemCount() { 66 | return getNormalItemCount() + (isFooterVisible ? 1 : 0); 67 | } 68 | 69 | @Override 70 | public int getItemViewType(int position) { 71 | if (position == getNormalItemCount()) { 72 | return VIEW_TYPE_FOOTER; 73 | } else { 74 | return VIEW_TYPE_NORMAL; 75 | } 76 | } 77 | 78 | /** 79 | * 对外接口,隐藏底部布局 80 | */ 81 | public void hideFooter() { 82 | if (isFooterVisible) { 83 | deleteItem(getNormalItemCount()); 84 | isFooterVisible = false; 85 | } 86 | } 87 | 88 | /** 89 | * 对外接口,显示底部布局 90 | */ 91 | public void showFooter() { 92 | if (!isFooterVisible) { 93 | 94 | isFooterVisible = true; 95 | insertItem(getNormalItemCount()); 96 | } 97 | } 98 | 99 | /** 100 | * 对外接口,显示底部布局 101 | */ 102 | public void updateFooter() { 103 | if (!isFooterVisible) { 104 | showFooter(); 105 | } 106 | updateItem(getItemCount() - 1); 107 | } 108 | 109 | /** 110 | * 对外接口,底部布局是否显示 111 | */ 112 | public boolean isFooterVisible() { 113 | return isFooterVisible; 114 | } 115 | 116 | private void deleteItem(int position) { 117 | syncItem(position, 0); 118 | } 119 | 120 | 121 | private void insertItem(final int position) { 122 | syncItem(position, 1); 123 | } 124 | 125 | 126 | private void updateItem(final int position) { 127 | syncItem(position, 2); 128 | } 129 | 130 | private void syncItem(final int position, final int mode) { 131 | if (getItemCount() > position) { 132 | // Cannot call this method in a scroll callback. 133 | // Scroll callbacks might be run during a measure & layout pass where you cannot change the RecyclerView data. 134 | // Any method call that might change the structure of the RecyclerView or the adapter contents should be postponed to the next frame. 135 | Handler handler = new Handler(); 136 | Runnable r = new Runnable() { 137 | public void run() { 138 | switch (mode) { 139 | //删除数据 140 | case 0: 141 | notifyItemRemoved(position); 142 | break; 143 | //插入数据 144 | case 1: 145 | notifyItemInserted(position); 146 | break; 147 | //更新数据 148 | case 2: 149 | notifyItemChanged(position); 150 | break; 151 | } 152 | } 153 | }; 154 | handler.post(r); 155 | } 156 | } 157 | } -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/base/adapter/RefreshRecycleAdapter.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.base.adapter; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | 5 | import static com.wanandroid.business.base.adapter.RefreshRecycleAdapter.RefreshState.COMPLETED; 6 | import static com.wanandroid.business.base.adapter.RefreshRecycleAdapter.RefreshState.LOADING; 7 | import static com.wanandroid.business.base.adapter.RefreshRecycleAdapter.RefreshState.LOADING_ERROR; 8 | import static com.wanandroid.business.base.adapter.RefreshRecycleAdapter.RefreshState.NO_MORE; 9 | 10 | /** 11 | * 在{@link FooterRecyclerAdapter}基础上面延伸出来上拉刷新的RecycleAdapter 12 | */ 13 | public abstract class RefreshRecycleAdapter extends FooterRecyclerAdapter { 14 | 15 | //"上拉加载"的视图状态 16 | private RefreshState mCurrRefreshState = COMPLETED; 17 | 18 | //"上拉加载"视图的几种状态 19 | public enum RefreshState { 20 | LOADING, LOADING_ERROR, COMPLETED, NO_MORE 21 | } 22 | 23 | @Override 24 | protected void onBindFooterViewHolder(T holder) { 25 | onBindRefreshFooterViewHolder(holder, mCurrRefreshState); 26 | } 27 | 28 | protected abstract void onBindRefreshFooterViewHolder(T holder, RefreshState state); 29 | 30 | public boolean isRefreshing() { 31 | return isFooterVisible() && mCurrRefreshState == LOADING; 32 | } 33 | 34 | public void setRefreshing(boolean refreshing) { 35 | if (refreshing) { 36 | mCurrRefreshState = LOADING; 37 | updateFooter(); 38 | } else { 39 | mCurrRefreshState = COMPLETED; 40 | hideFooter(); 41 | } 42 | } 43 | 44 | public void setRefreshNoMore() { 45 | mCurrRefreshState = NO_MORE; 46 | updateFooter(); 47 | } 48 | 49 | public void setRefreshError() { 50 | mCurrRefreshState = LOADING_ERROR; 51 | updateFooter(); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/callback/OnArticleFragmentRefreshListener.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.callback; 2 | 3 | /** 4 | * {@link com.wanandroid.business.base.BaseArticlesFragment}中的刷新回调 5 | */ 6 | public interface OnArticleFragmentRefreshListener { 7 | 8 | /** 9 | * 下拉刷新 10 | */ 11 | void onPullTopRefresh(); 12 | 13 | /** 14 | * 上拉刷新 15 | */ 16 | void onPullBottomRefresh(); 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/callback/OnArticleItemClickListener.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.callback; 2 | 3 | import com.wanandroid.model.entity.Article; 4 | 5 | /** 6 | * 文章被点击的回调 7 | */ 8 | public interface OnArticleItemClickListener { 9 | 10 | void OnArticleItemClick(Article article); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/callback/OnClassifyClickListener.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.callback; 2 | 3 | import com.wanandroid.model.entity.Tree; 4 | 5 | /** 6 | * "知识体系"被点击的回调 7 | */ 8 | public interface OnClassifyClickListener { 9 | 10 | void onClassifyClickListener(Tree cid); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/callback/OnSearchKeyClickListener.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.callback; 2 | 3 | /** 4 | * 点击"大家都在搜"的回调 5 | */ 6 | public interface OnSearchKeyClickListener { 7 | 8 | void OnSearchKeyClick(String key); 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/callback/OnSetToolbarTitleCallBack.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.callback; 2 | 3 | /** 4 | * 需要设置Toolbar标题的回调 5 | */ 6 | public interface OnSetToolbarTitleCallBack { 7 | 8 | void setToolbarTitle(String title); 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/cid/CidContract.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.cid; 2 | 3 | import com.wanandroid.model.entity.Article; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Created by ${jay} on ${2016/8/17 9 | */ 10 | public interface CidContract { 11 | 12 | interface View { 13 | 14 | void hideLoading(); 15 | 16 | void hideLoadMore(); 17 | 18 | void showNoMore(); 19 | 20 | void showErrorMsg(String ErrorMsg); 21 | 22 | /** 23 | * 展示文章列表 24 | * 25 | * @param articles 获取到的文章数据 26 | * @param clearOld 是否清除旧数据 27 | */ 28 | void displayArticles(List
articles, boolean clearOld); 29 | } 30 | 31 | interface Presenter { 32 | 33 | void refreshCidDetail(int cidID); 34 | 35 | void loadNextCidPage(); 36 | 37 | void cancelRefresh(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/cid/CidFragment.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.cid; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.widget.SwipeRefreshLayout; 7 | import android.util.Log; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | 12 | import com.wanandroid.R; 13 | import com.wanandroid.business.base.BaseArticlesFragment; 14 | import com.wanandroid.business.callback.OnArticleFragmentRefreshListener; 15 | import com.wanandroid.business.callback.OnClassifyClickListener; 16 | import com.wanandroid.business.callback.OnSetToolbarTitleCallBack; 17 | import com.wanandroid.model.entity.Article; 18 | import com.wanandroid.model.entity.Tree; 19 | 20 | import java.util.List; 21 | 22 | /** 23 | * 显示"知识体系分类"的Fragment 24 | */ 25 | public class CidFragment extends BaseArticlesFragment implements CidContract.View, OnClassifyClickListener { 26 | 27 | private static final String TAG = "CidFragment"; 28 | 29 | private Tree mCurrCid; 30 | 31 | private OnSetToolbarTitleCallBack mOnSetToolbarTitleCallBack; 32 | 33 | @Override 34 | public View bindFragmentView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 35 | View root = inflater.inflate(R.layout.fragment_cid, container, false); 36 | return root; 37 | } 38 | 39 | @Override 40 | public void onAttach(Context context) { 41 | super.onAttach(context); 42 | if (context instanceof OnSetToolbarTitleCallBack) { 43 | mOnSetToolbarTitleCallBack = (OnSetToolbarTitleCallBack) context; 44 | } 45 | } 46 | 47 | @Override 48 | public void onActivityCreated(@Nullable Bundle savedInstanceState) { 49 | super.onActivityCreated(savedInstanceState); 50 | initView(); 51 | } 52 | 53 | private void initView() { 54 | enablePullRefresh(true); 55 | enablePullBottomRefresh(true); 56 | setOnArticleFragmentRefreshListener(getOnArticleFragmentRefreshListener()); 57 | if (mOnSetToolbarTitleCallBack != null) { 58 | mOnSetToolbarTitleCallBack.setToolbarTitle(getString(R.string.cid)); 59 | } 60 | } 61 | 62 | @Override 63 | public void onDestroy() { 64 | super.onDestroy(); 65 | if (mOnSetToolbarTitleCallBack != null) { 66 | mOnSetToolbarTitleCallBack.setToolbarTitle(getString(R.string.app_name)); 67 | } 68 | } 69 | 70 | @Override 71 | public CidPresenter bindPresenter() { 72 | return new CidPresenter(); 73 | } 74 | 75 | @Override 76 | public CidContract.View bindView() { 77 | return this; 78 | } 79 | 80 | @Override 81 | public void onClassifyClickListener(Tree cid) { 82 | mCurrCid = cid; 83 | getBindPresenter().refreshCidDetail(mCurrCid.getId()); 84 | } 85 | 86 | @Override 87 | public void hideLoading() { 88 | SwipeRefreshLayout swipeRefreshLayout = getSwipeRefreshLayout(); 89 | if (swipeRefreshLayout != null) { 90 | swipeRefreshLayout.setRefreshing(false); 91 | } 92 | } 93 | 94 | @Override 95 | public void hideLoadMore() { 96 | getArticleAdapter().setRefreshing(false); 97 | } 98 | 99 | @Override 100 | public void showNoMore() { 101 | getArticleAdapter().setRefreshNoMore(); 102 | } 103 | 104 | @Override 105 | public void showErrorMsg(String errorMsg) { 106 | //TODO Error提示 107 | Log.i(TAG, "showErrorMsg: " + errorMsg); 108 | } 109 | 110 | @Override 111 | public void displayArticles(List
articles, boolean clearOld) { 112 | if (clearOld) { 113 | getArticleAdapter().setArticles(articles); 114 | getRecyclerView().scrollToPosition(0); 115 | } else { 116 | getArticleAdapter().addArticles(articles); 117 | } 118 | } 119 | 120 | public OnArticleFragmentRefreshListener getOnArticleFragmentRefreshListener() { 121 | return new OnArticleFragmentRefreshListener() { 122 | @Override 123 | public void onPullTopRefresh() { 124 | getBindPresenter().cancelRefresh(); 125 | getBindPresenter().refreshCidDetail(mCurrCid.getId()); 126 | } 127 | 128 | @Override 129 | public void onPullBottomRefresh() { 130 | getArticleAdapter().setRefreshing(true); 131 | getBindPresenter().loadNextCidPage(); 132 | } 133 | }; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/cid/CidPresenter.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.cid; 2 | 3 | import com.wanandroid.business.base.BasePresenterImpl; 4 | import com.wanandroid.model.ArticleData; 5 | import com.wanandroid.model.api.WanAndroidRetrofitClient; 6 | import com.wanandroid.model.entity.Article; 7 | 8 | import java.util.List; 9 | 10 | import io.reactivex.android.schedulers.AndroidSchedulers; 11 | import io.reactivex.annotations.NonNull; 12 | import io.reactivex.disposables.Disposable; 13 | import io.reactivex.functions.Action; 14 | import io.reactivex.functions.Consumer; 15 | import io.reactivex.functions.Function; 16 | import io.reactivex.schedulers.Schedulers; 17 | 18 | /** 19 | * Created by ${jay} on ${2016/8/17 20 | */ 21 | public class CidPresenter extends BasePresenterImpl implements CidContract.Presenter { 22 | 23 | private int mCurrPage = 0; 24 | 25 | private int mCurrCidId = -1; 26 | 27 | private Disposable mCurrRefreshDisposable; 28 | 29 | 30 | public void refreshCidDetail(final int cidID) { 31 | Disposable subscribe = WanAndroidRetrofitClient.getApiService() 32 | .getCidData(0, cidID) 33 | .map(new Function>() { 34 | @Override 35 | public List
apply(@NonNull ArticleData articleData) throws Exception { 36 | return articleData.getData().getDatas(); 37 | } 38 | }) 39 | .subscribeOn(Schedulers.io()) 40 | .observeOn(AndroidSchedulers.mainThread()) 41 | .doOnComplete(new Action() { 42 | @Override 43 | public void run() throws Exception { 44 | getView().hideLoading(); 45 | } 46 | }) 47 | .subscribe(new Consumer>() { 48 | @Override 49 | public void accept(List
articles) throws Exception { 50 | mCurrPage = 0; 51 | mCurrCidId = cidID; 52 | getView().displayArticles(articles, true); 53 | } 54 | }, new Consumer() { 55 | @Override 56 | public void accept(Throwable throwable) throws Exception { 57 | getView().showErrorMsg(throwable.getMessage()); 58 | } 59 | }); 60 | mCurrRefreshDisposable = subscribe; 61 | addDisposable(subscribe); 62 | } 63 | 64 | @Override 65 | public void loadNextCidPage() { 66 | Disposable subscribe = WanAndroidRetrofitClient.getApiService() 67 | .getCidData(mCurrPage + 1, mCurrCidId) 68 | .map(new Function>() { 69 | @Override 70 | public List
apply(@NonNull ArticleData articleData) throws Exception { 71 | return articleData.getData().getDatas(); 72 | } 73 | }) 74 | .subscribeOn(Schedulers.io()) 75 | .observeOn(AndroidSchedulers.mainThread()) 76 | .subscribe(new Consumer>() { 77 | @Override 78 | public void accept(List
articles) throws Exception { 79 | if (articles.size() == 0) { 80 | getView().showNoMore(); 81 | } else { 82 | mCurrPage++; 83 | getView().displayArticles(articles, false); 84 | getView().hideLoadMore(); 85 | } 86 | } 87 | }, new Consumer() { 88 | @Override 89 | public void accept(Throwable throwable) throws Exception { 90 | 91 | } 92 | }); 93 | addDisposable(subscribe); 94 | } 95 | 96 | @Override 97 | public void cancelRefresh() { 98 | if (mCurrRefreshDisposable != null && !mCurrRefreshDisposable.isDisposed()) { 99 | mCurrRefreshDisposable.dispose(); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/classify/ClassifyContract.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.classify; 2 | 3 | import com.wanandroid.model.entity.Tree; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Created by ${jay} on ${2016/8/17 9 | */ 10 | public class ClassifyContract { 11 | 12 | interface View { 13 | 14 | void displayTreeData(List treeData); 15 | } 16 | 17 | interface Presenter { 18 | 19 | /** 20 | * 获取"知识体系"分类 21 | */ 22 | void loadTree(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/classify/ClassifyDialog.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.classify; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.support.annotation.NonNull; 6 | import android.view.Gravity; 7 | import android.view.View; 8 | import android.view.Window; 9 | import android.view.WindowManager; 10 | import android.widget.AdapterView; 11 | import android.widget.ListView; 12 | 13 | import com.wanandroid.R; 14 | import com.wanandroid.business.adapter.CidListAdapter; 15 | import com.wanandroid.business.base.BaseMVPDialog; 16 | import com.wanandroid.business.callback.OnClassifyClickListener; 17 | import com.wanandroid.model.entity.Tree; 18 | import com.wanandroid.utils.ScreenUtil; 19 | import com.wang.avi.AVLoadingIndicatorView; 20 | 21 | import java.util.List; 22 | 23 | /** 24 | * 显示"知识体系"的分类对话框 25 | */ 26 | 27 | /* 28 | mOneCidIndicator.setVisibility(View.INVISIBLE); 29 | mOneCidIndicator.hide(); 30 | mCidOne.setVisibility(View.VISIBLE);*/ 31 | public class ClassifyDialog extends BaseMVPDialog implements ClassifyContract.View { 32 | 33 | private View mAnchorView; 34 | 35 | //一级分类 36 | private ListView mCidOne; 37 | 38 | //适配器 39 | private CidListAdapter mCidOneAdapter; 40 | 41 | //二级分类 42 | private ListView mCidTwo; 43 | 44 | //适配器 45 | private CidListAdapter mCidTwoAdapter; 46 | 47 | //"知识体系"数据 48 | private List mTreeData; 49 | 50 | //是否第一次加载,如果是第一次,则需要将第二级也加载一次 51 | private boolean isFirstLoadCid = true; 52 | 53 | //外界回调 54 | private OnClassifyClickListener mOnClassifyClickListener; 55 | 56 | private AVLoadingIndicatorView mOneCidIndicator; 57 | 58 | private AVLoadingIndicatorView mTwoCidIndicator; 59 | 60 | public ClassifyDialog(@NonNull Context context, View anchorView) { 61 | super(context, R.style.ClassifyDialogStyle); 62 | mAnchorView = anchorView; 63 | } 64 | 65 | @Override 66 | protected void onCreate(Bundle savedInstanceState) { 67 | super.onCreate(savedInstanceState); 68 | setContentView(R.layout.dialog_classify); 69 | initDialog(); 70 | } 71 | 72 | @Override 73 | public ClassifyPresenter bindPresenter() { 74 | return new ClassifyPresenter(); 75 | } 76 | 77 | @Override 78 | public ClassifyContract.View bindView() { 79 | return this; 80 | } 81 | 82 | 83 | /** 84 | * 将Dialog的位置定位到anchorView的正下方,并初始化控件 85 | */ 86 | private void initDialog() { 87 | //初始化窗体属性 88 | Window window = getWindow(); 89 | WindowManager.LayoutParams attributes = window.getAttributes(); 90 | attributes.width = ScreenUtil.getScrWidth(getContext()); 91 | attributes.y = (int) (mAnchorView.getY() + mAnchorView.getHeight()); 92 | window.setGravity(Gravity.TOP); 93 | window.setAttributes(attributes); 94 | 95 | mOneCidIndicator = (AVLoadingIndicatorView) findViewById(R.id.classify_one_cid_avi_indicator); 96 | mTwoCidIndicator = (AVLoadingIndicatorView) findViewById(R.id.classify_two_cid_avi_indicator); 97 | 98 | mCidOne = (ListView) findViewById(R.id.classify_one_cid); 99 | mCidTwo = (ListView) findViewById(R.id.classify_two_cid); 100 | 101 | mCidOneAdapter = new CidListAdapter(getContext()); 102 | mCidTwoAdapter = new CidListAdapter(getContext()); 103 | 104 | mCidOne.setAdapter(mCidOneAdapter); 105 | mCidTwo.setAdapter(mCidTwoAdapter); 106 | 107 | mCidOne.setOnItemClickListener(getOnItemClickListener()); 108 | mCidTwo.setOnItemClickListener(getOnItemClickListener()); 109 | } 110 | 111 | private void displayRootTree(List root) { 112 | mCidOneAdapter.setCids(root); 113 | } 114 | 115 | private void displayChildrenTree(List childRoot) { 116 | mCidTwoAdapter.setCids(childRoot); 117 | } 118 | 119 | @NonNull 120 | private AdapterView.OnItemClickListener getOnItemClickListener() { 121 | return new AdapterView.OnItemClickListener() { 122 | @Override 123 | public void onItemClick(AdapterView parent, View view, int position, long id) { 124 | Tree cidItem = (Tree) parent.getAdapter().getItem(position); 125 | if (parent.getId() == R.id.classify_one_cid) { 126 | mCidOneAdapter.setCurrSelect(position); 127 | displayChildrenTree(cidItem.getChildren()); 128 | } 129 | if (parent.getId() == R.id.classify_two_cid) { 130 | mCidTwoAdapter.setCurrSelect(position); 131 | if (mOnClassifyClickListener != null) { 132 | mOnClassifyClickListener.onClassifyClickListener(cidItem); 133 | } 134 | dismiss(); 135 | } 136 | } 137 | }; 138 | } 139 | 140 | public void setOnClassifyClickListener(OnClassifyClickListener listener) { 141 | mOnClassifyClickListener = listener; 142 | } 143 | 144 | @Override 145 | protected void onStart() { 146 | super.onStart(); 147 | //如果没有加载过,则加载"知识体系" 148 | if (mTreeData == null) { 149 | getBindPresenter().loadTree(); 150 | } 151 | } 152 | 153 | @Override 154 | public void displayTreeData(List treeData) { 155 | mOneCidIndicator.hide(); 156 | mTwoCidIndicator.hide(); 157 | mTreeData = treeData; 158 | mCidOneAdapter.setCurrSelect(0); 159 | mCidTwoAdapter.setCurrSelect(0); 160 | displayRootTree(mTreeData); 161 | displayChildrenTree(mTreeData.get(0).getChildren()); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/classify/ClassifyPresenter.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.classify; 2 | 3 | import android.util.Log; 4 | 5 | import com.wanandroid.business.base.BasePresenterImpl; 6 | import com.wanandroid.model.TreeData; 7 | import com.wanandroid.model.api.WanAndroidRetrofitClient; 8 | import com.wanandroid.model.entity.Tree; 9 | 10 | import java.util.List; 11 | 12 | import io.reactivex.android.schedulers.AndroidSchedulers; 13 | import io.reactivex.disposables.Disposable; 14 | import io.reactivex.functions.Consumer; 15 | import io.reactivex.functions.Function; 16 | import io.reactivex.schedulers.Schedulers; 17 | 18 | /** 19 | * Created by ${jay} on ${2016/8/17 20 | */ 21 | public class ClassifyPresenter extends BasePresenterImpl implements ClassifyContract.Presenter { 22 | 23 | private static final String TAG = "ClassifyPresenter"; 24 | 25 | @Override 26 | public void loadTree() { 27 | Disposable disposable = WanAndroidRetrofitClient.getApiService() 28 | .getTreeData() 29 | .map(new Function>() { 30 | @Override 31 | public List apply(TreeData treeData) throws Exception { 32 | return treeData.getData(); 33 | } 34 | }) 35 | .subscribeOn(Schedulers.io()) 36 | .observeOn(AndroidSchedulers.mainThread()) 37 | .subscribe(new Consumer>() { 38 | @Override 39 | public void accept(List trees) throws Exception { 40 | getView().displayTreeData(trees); 41 | } 42 | }, new Consumer() { 43 | @Override 44 | public void accept(Throwable throwable) throws Exception { 45 | Log.e(TAG, "accept: " + throwable); 46 | } 47 | }); 48 | addDisposable(disposable); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/collects/CollectContract.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.collects; 2 | 3 | import com.wanandroid.model.entity.Article; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Created by ${jay} on ${2016/8/17 9 | */ 10 | public interface CollectContract { 11 | 12 | interface View { 13 | 14 | /** 15 | * 显示顶部刷新 16 | */ 17 | void showPullTopRefresh(); 18 | 19 | /** 20 | * 隐藏顶部刷新 21 | */ 22 | void hidePullTopRefresh(); 23 | 24 | 25 | /** 26 | * 隐藏底部刷新 27 | */ 28 | void hideBottomRefresh(); 29 | 30 | /** 31 | * 显示"没有更多" 32 | */ 33 | void showNoMore(); 34 | 35 | /** 36 | * 隐藏"没有更多" 37 | */ 38 | void hideNoMore(); 39 | 40 | /** 41 | * 显示加载更多失败 42 | */ 43 | void showLoadMoreError(); 44 | 45 | /** 46 | * 展示文章列表 47 | * 48 | * @param articles 获取到的文章数据 49 | * @param clearOld 是否清除旧数据 50 | */ 51 | void displayArticles(List
articles, boolean clearOld); 52 | } 53 | 54 | interface Presenter { 55 | 56 | /** 57 | * 刷新收藏列表 58 | */ 59 | void refreshData(); 60 | 61 | /** 62 | * 加载下一页数据 63 | */ 64 | void loadNextPage(); 65 | 66 | void cancelCurrentLoad(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/collects/CollectFragment.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.collects; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | 10 | import com.wanandroid.R; 11 | import com.wanandroid.business.base.BaseArticlesFragment; 12 | import com.wanandroid.business.callback.OnArticleFragmentRefreshListener; 13 | import com.wanandroid.business.callback.OnSetToolbarTitleCallBack; 14 | import com.wanandroid.model.entity.Article; 15 | 16 | import java.util.List; 17 | 18 | /** 19 | * 显示收藏列表的Fragment 20 | */ 21 | public class CollectFragment extends BaseArticlesFragment implements CollectContract.View { 22 | 23 | private OnSetToolbarTitleCallBack mOnSetToolbarTitleCallBack; 24 | 25 | @Override 26 | public void onAttach(Context context) { 27 | super.onAttach(context); 28 | if (context instanceof OnSetToolbarTitleCallBack) { 29 | mOnSetToolbarTitleCallBack = (OnSetToolbarTitleCallBack) context; 30 | } 31 | } 32 | 33 | @Override 34 | public CollectPresenter bindPresenter() { 35 | return new CollectPresenter(); 36 | } 37 | 38 | @Override 39 | public CollectContract.View bindView() { 40 | return this; 41 | } 42 | 43 | @Override 44 | public View bindFragmentView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 45 | return inflater.inflate(R.layout.fragment_collect_list, container, false); 46 | } 47 | 48 | @Override 49 | public void onActivityCreated(@Nullable Bundle savedInstanceState) { 50 | super.onActivityCreated(savedInstanceState); 51 | initView(); 52 | initDat(); 53 | } 54 | 55 | private void initView() { 56 | if (mOnSetToolbarTitleCallBack != null) { 57 | mOnSetToolbarTitleCallBack.setToolbarTitle(getString(R.string.collect)); 58 | } 59 | setOnArticleFragmentRefreshListener(getOnRefreshListener()); 60 | } 61 | 62 | private void initDat() { 63 | getBindPresenter().refreshData(); 64 | } 65 | 66 | 67 | @Override 68 | public void onDestroy() { 69 | super.onDestroy(); 70 | if (mOnSetToolbarTitleCallBack != null) { 71 | mOnSetToolbarTitleCallBack.setToolbarTitle(getString(R.string.app_name)); 72 | } 73 | } 74 | 75 | @Override 76 | public void showPullTopRefresh() { 77 | getSwipeRefreshLayout().setRefreshing(true); 78 | } 79 | 80 | @Override 81 | public void hidePullTopRefresh() { 82 | getSwipeRefreshLayout().setRefreshing(false); 83 | } 84 | 85 | @Override 86 | public void hideBottomRefresh() { 87 | getArticleAdapter().setRefreshing(false); 88 | } 89 | 90 | @Override 91 | public void showNoMore() { 92 | getArticleAdapter().setRefreshNoMore(); 93 | } 94 | 95 | @Override 96 | public void hideNoMore() { 97 | getArticleAdapter().setRefreshing(false); 98 | } 99 | 100 | @Override 101 | public void showLoadMoreError() { 102 | getArticleAdapter().setRefreshError(); 103 | } 104 | 105 | @Override 106 | public void displayArticles(List
articles, boolean clearOld) { 107 | if (clearOld) { 108 | getArticleAdapter().setArticles(articles); 109 | } else { 110 | getArticleAdapter().addArticles(articles); 111 | } 112 | } 113 | 114 | /*** 115 | * 上拉,下拉监听 116 | */ 117 | public OnArticleFragmentRefreshListener getOnRefreshListener() { 118 | return new OnArticleFragmentRefreshListener() { 119 | @Override 120 | public void onPullTopRefresh() { 121 | getBindPresenter().refreshData(); 122 | } 123 | 124 | @Override 125 | public void onPullBottomRefresh() { 126 | getBindPresenter().loadNextPage(); 127 | } 128 | }; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/login/LoginOrRegisterContract.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.login; 2 | 3 | import android.content.Context; 4 | 5 | import com.wanandroid.model.entity.WanAndroidUser; 6 | 7 | /** 8 | * Created by ${jay} on ${2016/8/17 9 | */ 10 | 11 | public class LoginOrRegisterContract { 12 | 13 | interface View { 14 | 15 | void showProgress(); 16 | 17 | void hideProgress(); 18 | 19 | void loginSuccess(WanAndroidUser user); 20 | 21 | void registerSuccess(WanAndroidUser user); 22 | 23 | void loginFail(); 24 | 25 | void registerFail(); 26 | } 27 | 28 | interface Presenter { 29 | 30 | void login(String userName, String pwd); 31 | 32 | void register(String userName, String pwd); 33 | 34 | void saveUserData(WanAndroidUser user); 35 | 36 | void clearUserData(); 37 | 38 | /** 39 | * 更新本地用户登录状态 40 | */ 41 | void modifyLocalLoginStatue(Context context, boolean isLogin); 42 | 43 | WanAndroidUser loadUserData(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/login/LoginOrRegisterPresenter.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.login; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.Nullable; 5 | import android.util.Log; 6 | 7 | import com.wanandroid.business.base.BasePresenterImpl; 8 | import com.wanandroid.model.AuthData; 9 | import com.wanandroid.model.api.WanAndroidRetrofitClient; 10 | import com.wanandroid.model.db.UserManger; 11 | import com.wanandroid.model.entity.WanAndroidUser; 12 | import com.wanandroid.utils.SharedPreferencesUtil; 13 | 14 | import io.reactivex.android.schedulers.AndroidSchedulers; 15 | import io.reactivex.annotations.NonNull; 16 | import io.reactivex.disposables.Disposable; 17 | import io.reactivex.functions.Action; 18 | import io.reactivex.functions.Consumer; 19 | import io.reactivex.functions.Function; 20 | import io.reactivex.schedulers.Schedulers; 21 | 22 | /** 23 | * Created by ${jay} on ${2016/8/17 24 | */ 25 | public class LoginOrRegisterPresenter extends BasePresenterImpl implements LoginOrRegisterContract.Presenter { 26 | 27 | private static final String TAG = "LoginOrRegisterPresente"; 28 | 29 | @Override 30 | public void login(String userName, String pwd) { 31 | WanAndroidRetrofitClient 32 | .getApiService() 33 | .login(userName, pwd) 34 | .subscribeOn(Schedulers.io()) 35 | .observeOn(AndroidSchedulers.mainThread()) 36 | .doOnSubscribe(new Consumer() { 37 | @Override 38 | public void accept(Disposable disposable) throws Exception { 39 | getView().showProgress(); 40 | } 41 | }) 42 | .doFinally(new Action() { 43 | @Override 44 | public void run() throws Exception { 45 | getView().hideProgress(); 46 | } 47 | }) 48 | .map(new Function() { 49 | @Override 50 | public WanAndroidUser apply(@NonNull AuthData authData) throws Exception { 51 | return authData.getData(); 52 | } 53 | }) 54 | .subscribe(new Consumer() { 55 | @Override 56 | public void accept(WanAndroidUser authData) throws Exception { 57 | getView().loginSuccess(authData); 58 | } 59 | }, new Consumer() { 60 | @Override 61 | public void accept(Throwable throwable) throws Exception { 62 | Log.i(TAG, "accept: " + throwable.getMessage()); 63 | getView().loginFail(); 64 | } 65 | }); 66 | } 67 | 68 | @Override 69 | public void register(String userName, String pwd) { 70 | WanAndroidRetrofitClient 71 | .getApiService() 72 | .register(userName, pwd, pwd) 73 | .subscribeOn(Schedulers.io()) 74 | .observeOn(AndroidSchedulers.mainThread()) 75 | .map(new Function() { 76 | @Override 77 | public WanAndroidUser apply(@NonNull AuthData authData) throws Exception { 78 | return authData.getData(); 79 | } 80 | }) 81 | .doOnSubscribe(new Consumer() { 82 | @Override 83 | public void accept(Disposable disposable) throws Exception { 84 | getView().showProgress(); 85 | } 86 | }) 87 | .doFinally(new Action() { 88 | @Override 89 | public void run() throws Exception { 90 | getView().hideProgress(); 91 | } 92 | }) 93 | .subscribe(new Consumer() { 94 | @Override 95 | public void accept(WanAndroidUser authData) throws Exception { 96 | getView().registerSuccess(authData); 97 | } 98 | }, new Consumer() { 99 | @Override 100 | public void accept(Throwable throwable) throws Exception { 101 | Log.i(TAG, "accept: " + throwable.getMessage()); 102 | getView().registerFail(); 103 | } 104 | }); 105 | } 106 | 107 | @Override 108 | public void saveUserData(WanAndroidUser user) { 109 | UserManger.updateUserInfo(user); 110 | } 111 | 112 | @Override 113 | public void clearUserData() { 114 | UserManger.clearUserInfo(); 115 | } 116 | 117 | @Override 118 | public void modifyLocalLoginStatue(Context context, boolean isLogin) { 119 | SharedPreferencesUtil.put(context, "isLogin", isLogin); 120 | } 121 | 122 | @Override 123 | @Nullable 124 | public WanAndroidUser loadUserData() { 125 | return UserManger.getUserInfo(); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/main/MainActivityHolder.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.main; 2 | 3 | import android.support.design.widget.NavigationView; 4 | import android.support.v4.widget.DrawerLayout; 5 | import android.support.v7.widget.Toolbar; 6 | import android.util.Log; 7 | import android.view.Gravity; 8 | import android.view.View; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | import com.wanandroid.R; 13 | import com.wanandroid.model.entity.WanAndroidUser; 14 | import com.wanandroid.widget.AutoClearEditText; 15 | 16 | /** 17 | * 由于MainActivity包裹的View过多,所有使用一个Holder进行统一管理 18 | */ 19 | public class MainActivityHolder { 20 | 21 | private static final String TAG = "MainActivityHolder"; 22 | 23 | private MainActivity mMainActivity; 24 | 25 | private Toolbar mToolbar; 26 | 27 | private AutoClearEditText mSearchEdit; 28 | 29 | //侧滑控件 30 | private DrawerLayout mDrawerLayout; 31 | 32 | //侧滑菜单 33 | private NavigationView mNavigationView; 34 | 35 | //侧滑菜单顶部布局 36 | private View mNavigationViewHeader; 37 | 38 | private ImageView mUserImage; 39 | 40 | private TextView mUserName; 41 | 42 | public MainActivityHolder(MainActivity mainActivity) { 43 | mMainActivity = mainActivity; 44 | //对视图进行初始化 45 | mDrawerLayout = mMainActivity.findViewById(R.id.main_drawer); 46 | mSearchEdit = mMainActivity.findViewById(R.id.main_edit_search); 47 | mToolbar = mMainActivity.findViewById(R.id.main_toolbar); 48 | mNavigationView = mMainActivity.findViewById(R.id.main_left_menu); 49 | mNavigationViewHeader = mNavigationView.getHeaderView(0); 50 | mUserImage = mNavigationViewHeader.findViewById(R.id.menu_user_icon); 51 | mUserName = mNavigationViewHeader.findViewById(R.id.menu_user_name); 52 | 53 | //对视图进行设置 54 | mMainActivity.setSupportActionBar(mToolbar); 55 | 56 | mToolbar.setNavigationOnClickListener(new View.OnClickListener() { 57 | @Override 58 | public void onClick(View v) { 59 | mDrawerLayout.openDrawer(Gravity.START); 60 | } 61 | }); 62 | } 63 | 64 | public void initUserInfo(WanAndroidUser user) { 65 | if (user == null) { 66 | Log.w(TAG, "initUserInfo: user info is null"); 67 | return; 68 | } 69 | //TODO 加载真实地址 70 | mUserImage.setImageResource(R.mipmap.ic_launcher); 71 | mUserName.setText(user.getUsername()); 72 | } 73 | 74 | public void clearUserInfo() { 75 | mUserImage.setImageResource(R.mipmap.ic_launcher); 76 | mUserName.setText(R.string.unlogin); 77 | } 78 | 79 | public boolean isDrawerOpen() { 80 | return mDrawerLayout.isDrawerOpen(Gravity.START); 81 | } 82 | 83 | public void closeDrawer() { 84 | mDrawerLayout.closeDrawer(Gravity.START); 85 | } 86 | 87 | public void openDrawer() { 88 | mDrawerLayout.openDrawer(Gravity.START); 89 | } 90 | 91 | 92 | public Toolbar getToolbar() { 93 | return mToolbar; 94 | } 95 | 96 | public AutoClearEditText getSearchEdit() { 97 | return mSearchEdit; 98 | } 99 | 100 | public DrawerLayout getDrawerLayout() { 101 | return mDrawerLayout; 102 | } 103 | 104 | public NavigationView getNavigationView() { 105 | return mNavigationView; 106 | } 107 | 108 | public View getNavigationViewHeader() { 109 | return mNavigationViewHeader; 110 | } 111 | 112 | public ImageView getUserImage() { 113 | return mUserImage; 114 | } 115 | 116 | public TextView getUserName() { 117 | return mUserName; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/search/SearchContract.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.search; 2 | 3 | import com.wanandroid.model.entity.Article; 4 | import com.wanandroid.model.entity.HotKey; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by ${jay} on ${2016/8/17 10 | */ 11 | 12 | public interface SearchContract { 13 | 14 | interface View { 15 | /** 16 | * 显示"大家都在搜" 17 | */ 18 | void displayHotKeys(List hotKeys); 19 | 20 | /** 21 | * 显示搜索结果 22 | */ 23 | void displaySearchResult(List
articles); 24 | 25 | /** 26 | * 添加搜索结果 27 | */ 28 | void appendSearchResult(List
articles); 29 | 30 | /** 31 | * 显示没有更多数据 32 | */ 33 | void displayNoMore(); 34 | 35 | /** 36 | * 加载更多数据出错 37 | */ 38 | void displayLoadMoreError(); 39 | 40 | /** 41 | * 显示正在搜索的视图 42 | */ 43 | void displaySearching(); 44 | 45 | /** 46 | * 显示搜索失败的视图 47 | */ 48 | void displaySearchError(); 49 | } 50 | 51 | interface Presenter { 52 | /** 53 | * 获取大家都在搜 54 | */ 55 | void getHotKeys(); 56 | 57 | /** 58 | * 执行搜索操作 59 | */ 60 | void doSearch(String key); 61 | 62 | /** 63 | * 获取下一页 64 | */ 65 | void doSearchNextPage(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/search/SearchPresenter.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.search; 2 | 3 | import com.wanandroid.business.base.BasePresenterImpl; 4 | import com.wanandroid.model.ArticleData; 5 | import com.wanandroid.model.HotKeyData; 6 | import com.wanandroid.model.api.WanAndroidRetrofitClient; 7 | import com.wanandroid.model.entity.Article; 8 | import com.wanandroid.model.entity.HotKey; 9 | 10 | import java.util.List; 11 | 12 | import io.reactivex.android.schedulers.AndroidSchedulers; 13 | import io.reactivex.annotations.NonNull; 14 | import io.reactivex.disposables.Disposable; 15 | import io.reactivex.functions.Consumer; 16 | import io.reactivex.functions.Function; 17 | import io.reactivex.functions.Predicate; 18 | import io.reactivex.schedulers.Schedulers; 19 | 20 | /** 21 | * Created by ${jay} on ${2016/8/17 22 | */ 23 | public class SearchPresenter extends BasePresenterImpl implements SearchContract.Presenter { 24 | 25 | private String mCurrentKey; 26 | 27 | private int mCurrentPage = 0; 28 | 29 | @Override 30 | public void getHotKeys() { 31 | Disposable disposable = WanAndroidRetrofitClient 32 | .getApiService() 33 | .getHotKey() 34 | .map(new Function>() { 35 | 36 | @Override 37 | public List apply(HotKeyData hotKeyData) throws Exception { 38 | return hotKeyData.getData(); 39 | } 40 | }) 41 | .filter(new Predicate>() { 42 | @Override 43 | public boolean test(List hotKeys) throws Exception { 44 | return hotKeys != null && hotKeys.size() > 0; 45 | } 46 | }) 47 | .subscribeOn(Schedulers.io()) 48 | .observeOn(AndroidSchedulers.mainThread()) 49 | .subscribe(new Consumer>() { 50 | @Override 51 | public void accept(List hotKeys) throws Exception { 52 | if (getView() != null) { 53 | getView().displayHotKeys(hotKeys); 54 | } 55 | } 56 | }); 57 | addDisposable(disposable); 58 | } 59 | 60 | @Override 61 | public void doSearch(String key) { 62 | mCurrentKey = key; 63 | mCurrentPage = 0; 64 | Disposable disposable = WanAndroidRetrofitClient 65 | .getApiService() 66 | .searchArticle(mCurrentPage, mCurrentKey) 67 | .map(new Function>() { 68 | @Override 69 | public List
apply(@NonNull ArticleData articleData) throws Exception { 70 | return articleData.getData().getDatas(); 71 | } 72 | }) 73 | .subscribeOn(Schedulers.io()) 74 | .observeOn(AndroidSchedulers.mainThread()) 75 | .doOnSubscribe(new Consumer() { 76 | @Override 77 | public void accept(Disposable disposable) throws Exception { 78 | getView().displaySearching(); 79 | } 80 | }) 81 | .subscribe(new Consumer>() { 82 | @Override 83 | public void accept(List
articles) throws Exception { 84 | getView().displaySearchResult(articles); 85 | } 86 | }, new Consumer() { 87 | @Override 88 | public void accept(Throwable throwable) throws Exception { 89 | getView().displaySearchError(); 90 | } 91 | }); 92 | addDisposable(disposable); 93 | } 94 | 95 | @Override 96 | public void doSearchNextPage() { 97 | Disposable disposable = WanAndroidRetrofitClient 98 | .getApiService() 99 | .searchArticle(mCurrentPage + 1, mCurrentKey) 100 | .map(new Function>() { 101 | @Override 102 | public List
apply(@NonNull ArticleData articleData) throws Exception { 103 | return articleData.getData().getDatas(); 104 | } 105 | }) 106 | .subscribeOn(Schedulers.io()) 107 | .observeOn(AndroidSchedulers.mainThread()) 108 | .filter(new Predicate>() { 109 | @Override 110 | public boolean test(@NonNull List
articles) throws Exception { 111 | if (articles.size() == 0) { 112 | getView().displayNoMore(); 113 | } 114 | return articles.size() != 0; 115 | } 116 | }) 117 | .subscribe(new Consumer>() { 118 | @Override 119 | public void accept(List
articles) throws Exception { 120 | //搜索有结果才+1 121 | mCurrentPage++; 122 | getView().appendSearchResult(articles); 123 | } 124 | }, new Consumer() { 125 | @Override 126 | public void accept(Throwable throwable) throws Exception { 127 | getView().displayLoadMoreError(); 128 | } 129 | }); 130 | addDisposable(disposable); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/welcome/WelcomeActivity.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.welcome; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.os.Handler; 6 | import android.view.Window; 7 | import android.view.WindowManager; 8 | import android.widget.Toast; 9 | 10 | import com.wanandroid.R; 11 | import com.wanandroid.business.base.BaseMVPActivity; 12 | import com.wanandroid.business.main.MainActivity; 13 | 14 | /** 15 | * Apk启动的欢迎界面 16 | * TODO:每日一句??? 17 | */ 18 | public class WelcomeActivity extends BaseMVPActivity implements WelcomeContract.View { 19 | 20 | private Handler mHandler = new Handler(); 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | requestWindowFeature(Window.FEATURE_NO_TITLE); 25 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_welcome); 28 | getBindPresenter().modifyLocalLoginStatue(this, false); 29 | getBindPresenter().autoLogin(); 30 | mHandler.postDelayed(new Runnable() { 31 | @Override 32 | public void run() { 33 | Intent intent = new Intent(); 34 | intent.setClass(WelcomeActivity.this, MainActivity.class); 35 | startActivity(intent); 36 | finish(); 37 | overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); 38 | } 39 | }, 3000); 40 | } 41 | 42 | @Override 43 | public WelcomePresenter bindPresenter() { 44 | return new WelcomePresenter(); 45 | } 46 | 47 | @Override 48 | public WelcomeContract.View bindView() { 49 | return this; 50 | } 51 | 52 | @Override 53 | public void autoLoginFail() { 54 | Toast.makeText(this, R.string.auto_login_error, Toast.LENGTH_SHORT).show(); 55 | getBindPresenter().modifyLocalLoginStatue(this, false); 56 | } 57 | 58 | @Override 59 | public void autoLoginSuccess() { 60 | getBindPresenter().modifyLocalLoginStatue(this, true); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/welcome/WelcomeContract.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.welcome; 2 | 3 | import android.content.Context; 4 | 5 | /** 6 | * Created by ${jay} on ${2016/8/17 7 | */ 8 | public interface WelcomeContract { 9 | 10 | interface View { 11 | void autoLoginFail(); 12 | 13 | void autoLoginSuccess(); 14 | } 15 | 16 | interface Presenter { 17 | 18 | /** 19 | * 根据本地的用户信息自动登录下 20 | */ 21 | void autoLogin(); 22 | 23 | /** 24 | * 更新本地用户登录状态 25 | */ 26 | void modifyLocalLoginStatue(Context context, boolean isLogin); 27 | 28 | /** 29 | * 加载每日一句 30 | */ 31 | void loadEveryDayNiceWord(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/business/welcome/WelcomePresenter.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.business.welcome; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import com.wanandroid.business.base.BasePresenterImpl; 7 | import com.wanandroid.model.AuthData; 8 | import com.wanandroid.model.api.WanAndroidRetrofitClient; 9 | import com.wanandroid.model.db.UserManger; 10 | import com.wanandroid.model.entity.WanAndroidUser; 11 | import com.wanandroid.utils.SharedPreferencesUtil; 12 | 13 | import io.reactivex.android.schedulers.AndroidSchedulers; 14 | import io.reactivex.disposables.Disposable; 15 | import io.reactivex.functions.Consumer; 16 | import io.reactivex.functions.Function; 17 | import io.reactivex.schedulers.Schedulers; 18 | 19 | /** 20 | * app启动闪屏界面 21 | */ 22 | public class WelcomePresenter extends BasePresenterImpl implements WelcomeContract.Presenter { 23 | 24 | private static final String TAG = "WelcomePresenter"; 25 | 26 | @Override 27 | public void autoLogin() { 28 | WanAndroidUser userInfo = UserManger.getUserInfo(); 29 | if (userInfo != null) { 30 | Disposable disposable = WanAndroidRetrofitClient 31 | .getApiService() 32 | .login(userInfo.getUsername(), userInfo.getPassword()) 33 | .map(new Function() { 34 | @Override 35 | public WanAndroidUser apply(AuthData authData) throws Exception { 36 | return authData.getData(); 37 | } 38 | }) 39 | .subscribeOn(Schedulers.io()) 40 | .observeOn(AndroidSchedulers.mainThread()) 41 | .subscribe(new Consumer() { 42 | @Override 43 | public void accept(WanAndroidUser wanAndroidUser) throws Exception { 44 | Log.i(TAG, "accept: auto login success"); 45 | getView().autoLoginSuccess(); 46 | } 47 | }, new Consumer() { 48 | @Override 49 | public void accept(Throwable throwable) throws Exception { 50 | Log.w(TAG, "accept: auto autoLogin error"); 51 | getView().autoLoginFail(); 52 | } 53 | }); 54 | addDisposable(disposable); 55 | } else { 56 | Log.w(TAG, "autoLogin: local userinfo is empty"); 57 | } 58 | } 59 | 60 | @Override 61 | public void modifyLocalLoginStatue(Context context, boolean isLogin) { 62 | SharedPreferencesUtil.put(context, "isLogin", isLogin); 63 | } 64 | 65 | @Override 66 | public void loadEveryDayNiceWord() { 67 | // OkHttpClientManger.getOkHttpClient(). 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/model/AuthData.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.model; 2 | 3 | import com.wanandroid.model.entity.WanAndroidUser; 4 | 5 | /** 6 | * WanAndroid注册/登录返回信息 7 | *

8 | * 登录注册均使用的此数据结构 9 | */ 10 | public class AuthData { 11 | 12 | 13 | /** 14 | * errorCode : 0 15 | * errorMsg : null 16 | * data : {"id":880,"username":"test~11111","password":"1234567890","icon":null,"type":0,"collectIds":[]} 17 | */ 18 | private int errorCode; 19 | private String errorMsg; 20 | private WanAndroidUser data; 21 | 22 | public int getErrorCode() { 23 | return errorCode; 24 | } 25 | 26 | public void setErrorCode(int errorCode) { 27 | this.errorCode = errorCode; 28 | } 29 | 30 | public String getErrorMsg() { 31 | return errorMsg; 32 | } 33 | 34 | public void setErrorMsg(String errorMsg) { 35 | this.errorMsg = errorMsg; 36 | } 37 | 38 | public WanAndroidUser getData() { 39 | return data; 40 | } 41 | 42 | public void setData(WanAndroidUser data) { 43 | this.data = data; 44 | } 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/model/BaseResponseData.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.model; 2 | 3 | /** 4 | * Created by ${jay} on ${2016/8/17 5 | */ 6 | public class BaseResponseData { 7 | 8 | /** 9 | * errorCode : 0 10 | * errorMsg : null 11 | * data : null 12 | */ 13 | public int errorCode = -1; 14 | public String errorMsg; 15 | 16 | public int getErrorCode() { 17 | return errorCode; 18 | } 19 | 20 | public void setErrorCode(int errorCode) { 21 | this.errorCode = errorCode; 22 | } 23 | 24 | public String getErrorMsg() { 25 | return errorMsg; 26 | } 27 | 28 | public void setErrorMsg(String errorMsg) { 29 | this.errorMsg = errorMsg; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/model/CidData.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.model; 2 | 3 | import com.wanandroid.model.entity.Cid; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * 分类数据 9 | *

10 | * 被弃用,替代为{@link TreeData} 11 | */ 12 | @Deprecated 13 | public class CidData extends BaseResponseData { 14 | 15 | private List cids; 16 | 17 | public List getCids() { 18 | return cids; 19 | } 20 | 21 | public void setCids(List cids) { 22 | this.cids = cids; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/model/HotKeyData.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.model; 2 | 3 | import com.wanandroid.model.entity.HotKey; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * 热搜数据 9 | */ 10 | public class HotKeyData { 11 | 12 | /** 13 | * errorCode : 0 14 | * errorMsg : null 15 | * data : [{"id":6,"name":"面试","link":null,"visible":1,"order":1},{"id":9,"name":"Studio3","link":null,"visible":1,"order":1},{"id":5,"name":"动画","link":null,"visible":1,"order":2},{"id":1,"name":"自定义View","link":null,"visible":1,"order":3},{"id":2,"name":"性能优化 速度","link":null,"visible":1,"order":4},{"id":3,"name":"gradle","link":null,"visible":1,"order":5},{"id":4,"name":"Camera 相机","link":null,"visible":1,"order":6},{"id":7,"name":"代码混淆 安全","link":null,"visible":1,"order":7},{"id":8,"name":"逆向 加固","link":null,"visible":1,"order":8}] 16 | */ 17 | 18 | private int errorCode; 19 | private String errorMsg; 20 | private List data; 21 | 22 | public int getErrorCode() { 23 | return errorCode; 24 | } 25 | 26 | public void setErrorCode(int errorCode) { 27 | this.errorCode = errorCode; 28 | } 29 | 30 | public String getErrorMsg() { 31 | return errorMsg; 32 | } 33 | 34 | public void setErrorMsg(String errorMsg) { 35 | this.errorMsg = errorMsg; 36 | } 37 | 38 | public List getData() { 39 | return data; 40 | } 41 | 42 | public void setData(List data) { 43 | this.data = data; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/model/HotSearchData.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.model; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * "大家都在搜"的数据 7 | */ 8 | @Deprecated 9 | public class HotSearchData extends BaseResponseData { 10 | 11 | private List hotKeys; 12 | 13 | public List getHotKeys() { 14 | return hotKeys; 15 | } 16 | 17 | public void setHotKeys(List hotKeys) { 18 | this.hotKeys = hotKeys; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/model/SearchData.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.model; 2 | 3 | import com.wanandroid.model.entity.Article; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * 由于{@link com.wanandroid.model.api.WanAndroidApiCompat#searchArticle(String)}接口被弃用,所以 9 | * 此数据结构也被弃用,请参看新接口{@link com.wanandroid.model.api.WanAndroidApi#searchArticle(int, String)} 10 | *
11 | * 搜索结果数据 12 | */ 13 | @Deprecated 14 | public class SearchData extends BaseResponseData { 15 | 16 | private List

mArticles; 17 | 18 | public List
getArticles() { 19 | return mArticles; 20 | } 21 | 22 | public void setArticles(List
articles) { 23 | mArticles = articles; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/model/api/WanAndroidApi.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.model.api; 2 | 3 | import com.wanandroid.model.ArticleData; 4 | import com.wanandroid.model.AuthData; 5 | import com.wanandroid.model.BaseResponseData; 6 | import com.wanandroid.model.CollectedArticleData; 7 | import com.wanandroid.model.HotKeyData; 8 | import com.wanandroid.model.TreeData; 9 | 10 | import io.reactivex.Observable; 11 | import retrofit2.http.Field; 12 | import retrofit2.http.FormUrlEncoded; 13 | import retrofit2.http.GET; 14 | import retrofit2.http.Headers; 15 | import retrofit2.http.POST; 16 | import retrofit2.http.Path; 17 | import retrofit2.http.Query; 18 | 19 | /** 20 | * WanAndroid的可用API 21 | */ 22 | public interface WanAndroidApi { 23 | 24 | //获取首页数据 25 | @GET("article/list/{page}/json") 26 | Observable getArticleData(@Path("page") int page); 27 | 28 | //注册 29 | @POST("user/register") 30 | @FormUrlEncoded 31 | Observable register(@Field("username") String userName, @Field("password") String pwd, @Field("repassword") String rePwd); 32 | 33 | //登录 34 | @POST("user/login") 35 | @FormUrlEncoded 36 | Observable login(@Field("username") String userName, @Field("password") String pwd); 37 | 38 | //收藏列表 39 | @GET("lg/collect/list/{page}/json") 40 | Observable getCollectData(@Path("page") int page); 41 | 42 | //取消收藏文章 43 | @POST("lg/uncollect_originId/{id}/json") 44 | Observable unCollectArticle(@Path("id") int id); 45 | 46 | //收藏文章 47 | @POST("lg/collect/{id}/json") 48 | Observable collectArticle(@Path("id") int id); 49 | 50 | //获取"知识体系" 51 | @GET("tree/json") 52 | @Headers("Cache-Control: public, max-age=3600") 53 | Observable getTreeData(); 54 | 55 | //获取"知识体系"数据 56 | @GET("article/list/{page}/json") 57 | Observable getCidData(@Path("page") int page, @Query("cid") int cid); 58 | 59 | //搜索接口 60 | @POST("article/query/{page}/json") 61 | @FormUrlEncoded 62 | Observable searchArticle(@Path("page") int page, @Field("k") String key); 63 | 64 | //热词接口 65 | @GET("hotkey/json") 66 | @Headers("Cache-Control: public, max-age=36000") 67 | Observable getHotKey(); 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/model/api/WanAndroidRetrofitClient.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.model.api; 2 | 3 | import com.wanandroid.model.api.utils.OkHttpClientManger; 4 | 5 | import retrofit2.Retrofit; 6 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; 7 | import retrofit2.converter.gson.GsonConverterFactory; 8 | 9 | /** 10 | * WanAndroid Api RetrofitClient管理类 11 | * @author Jay 12 | */ 13 | public class WanAndroidRetrofitClient { 14 | 15 | private static final String BASE_URL = "http://wanandroid.com/"; 16 | 17 | private WanAndroidRetrofitClient() { 18 | 19 | } 20 | 21 | private static Retrofit mRetrofitClient = null; 22 | 23 | public static Retrofit getRetroClient() { 24 | if (mRetrofitClient == null) { 25 | synchronized (WanAndroidRetrofitClient.class) { 26 | if (mRetrofitClient == null) { 27 | mRetrofitClient = new Retrofit 28 | .Builder() 29 | .baseUrl(BASE_URL) 30 | .client(OkHttpClientManger.getOkHttpClient()) 31 | .addConverterFactory(GsonConverterFactory.create()) 32 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 33 | .build(); 34 | } 35 | } 36 | } 37 | return mRetrofitClient; 38 | } 39 | 40 | /** 41 | * 获取WanAndroid API 42 | */ 43 | public static WanAndroidApi getApiService() { 44 | return getRetroClient().create(WanAndroidApi.class); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/model/api/utils/OkHttpClientManger.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.model.api.utils; 2 | 3 | import com.wanandroid.WanApplication; 4 | 5 | import java.io.File; 6 | 7 | import okhttp3.Cache; 8 | import okhttp3.OkHttpClient; 9 | 10 | /** 11 | * 由于一个应用只应该存在一个OkHttp客户端,所以通过这个管理 12 | */ 13 | public class OkHttpClientManger { 14 | 15 | private static OkHttpClient okHttpClient; 16 | 17 | public static OkHttpClient getOkHttpClient() { 18 | if (okHttpClient == null) { 19 | synchronized (OkHttpClientManger.class) { 20 | // 指定缓存路径,缓存大小50Mb 21 | Cache cache = new Cache(new File(WanApplication.getAppContext().getExternalCacheDir(), "HttpCache"), 1024 * 1024 * 50); 22 | if (okHttpClient == null) { 23 | okHttpClient = new OkHttpClient.Builder() 24 | .cache(cache) 25 | .cookieJar(new WanAndroidCookieJar()) 26 | .addNetworkInterceptor(OkHttpConfig.sCacheControlInterceptor) 27 | .addInterceptor(OkHttpConfig.sCacheControlInterceptor) 28 | .addNetworkInterceptor(OkHttpConfig.sLoggingInterceptor) 29 | .retryOnConnectionFailure(true) 30 | .build(); 31 | } 32 | } 33 | } 34 | return okHttpClient; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/model/api/utils/OkHttpConfig.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.model.api.utils; 2 | 3 | import com.wanandroid.BuildConfig; 4 | import com.wanandroid.WanApplication; 5 | import com.wanandroid.utils.NetUtil; 6 | 7 | import java.io.IOException; 8 | 9 | import okhttp3.CacheControl; 10 | import okhttp3.Interceptor; 11 | import okhttp3.Request; 12 | import okhttp3.Response; 13 | import okhttp3.logging.HttpLoggingInterceptor; 14 | 15 | /** 16 | * OKHttp的主要配置都在这里 17 | */ 18 | public class OkHttpConfig { 19 | 20 | //设缓存有效期为1天 21 | private static final long CACHE_STALE_SEC = 60 * 60 * 24; 22 | 23 | //查询缓存的Cache-Control设置,为if-only-cache时只查询缓存而不会请求服务器,max-stale可以配合设置缓存失效时间 24 | private static final String CACHE_CONTROL_CACHE = "only-if-cached, max-stale=" + CACHE_STALE_SEC; 25 | 26 | //用于控制是否输出日志 27 | public static final HttpLoggingInterceptor sLoggingInterceptor = new HttpLoggingInterceptor(); 28 | 29 | //用户控制Http缓存 30 | public static final Interceptor sCacheControlInterceptor = new Interceptor() { 31 | @Override 32 | public Response intercept(Chain chain) throws IOException { 33 | Request request = chain.request(); 34 | if ("GET".equals(request.method())) { 35 | return doCache(chain); 36 | } else { 37 | return chain.proceed(request); 38 | } 39 | } 40 | 41 | private Response doCache(Chain chain) throws IOException { 42 | Request request = chain.request(); 43 | //如果没网,则只使用缓存 44 | if (!NetUtil.isNetworkAvailable(WanApplication.getAppContext())) { 45 | request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build(); 46 | } 47 | 48 | Response originalResponse = chain.proceed(request); 49 | 50 | if (NetUtil.isNetworkAvailable(WanApplication.getAppContext())) { 51 | //有网的时候读接口上的@Headers里的配置,你可以在这里进行统一的设置 52 | String cacheControl = request.cacheControl().toString(); 53 | return originalResponse.newBuilder() 54 | .header("Cache-Control", cacheControl) 55 | .removeHeader("Pragma") 56 | .build(); 57 | } else { 58 | //没网则缓存 59 | return originalResponse.newBuilder() 60 | .header("Cache-Control", "public, " + CACHE_CONTROL_CACHE) 61 | .removeHeader("Pragma") 62 | .build(); 63 | } 64 | } 65 | }; 66 | 67 | static { 68 | sLoggingInterceptor.setLevel(BuildConfig.LOG_DEBUG ? HttpLoggingInterceptor.Level.BODY : HttpLoggingInterceptor.Level.NONE); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/model/api/utils/WanAndroidCookieJar.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.model.api.utils; 2 | 3 | import android.util.Log; 4 | 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | 9 | import okhttp3.Cookie; 10 | import okhttp3.CookieJar; 11 | import okhttp3.HttpUrl; 12 | 13 | /** 14 | * 用于管理与{@url http://www.wanandroid.com}通信的Cookie 15 | */ 16 | public class WanAndroidCookieJar implements CookieJar { 17 | 18 | private static final String TAG = "WanAndroidCookieJar"; 19 | 20 | private static HashMap> map = new HashMap<>(); 21 | 22 | @Override 23 | public void saveFromResponse(HttpUrl url, List cookies) { 24 | Log.i(TAG, "saveFromResponse: " + url); 25 | Log.i(TAG, "saveFromResponse: " + cookies); 26 | map.put(url.host(), cookies); 27 | } 28 | 29 | @Override 30 | public List loadForRequest(HttpUrl url) { 31 | List cookies = map.get(url.host()); 32 | Log.i(TAG, "loadForRequest: " + url); 33 | Log.i(TAG, "loadForRequest: " + cookies); 34 | return cookies != null ? cookies : new ArrayList(); 35 | } 36 | 37 | public static void clearCookie() { 38 | map.clear(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/model/db/UserManger.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.model.db; 2 | 3 | import android.support.annotation.Nullable; 4 | 5 | import com.wanandroid.WanApplication; 6 | import com.wanandroid.model.entity.WanAndroidUser; 7 | 8 | import java.util.ArrayList; 9 | 10 | /** 11 | * 用于管理当前用户信息的类 12 | */ 13 | public class UserManger { 14 | 15 | public static int clearUserInfo() { 16 | return WanApplication.liteOrm.deleteAll(WanAndroidUser.class); 17 | } 18 | 19 | /** 20 | * 获取当前登陆的用户信息 21 | * 22 | * @return 如果没有登录,返回null 23 | */ 24 | @Nullable 25 | public static WanAndroidUser getUserInfo() { 26 | ArrayList users = WanApplication.liteOrm.query(WanAndroidUser.class); 27 | return users == null || users.size() == 0 ? null : users.get(0); 28 | } 29 | 30 | /** 31 | * 更新用户信息,如果对应用户不存在,则自动保存 32 | * 33 | * @param newUserInfo 新用户信息,如果不存在,则直接保存 34 | */ 35 | public static void updateUserInfo(WanAndroidUser newUserInfo) { 36 | 37 | WanAndroidUser androidUser = WanApplication.liteOrm.queryById(newUserInfo.getId(), WanAndroidUser.class); 38 | //如果不存在用户,则直接保存 39 | if (androidUser == null) { 40 | WanApplication.liteOrm.deleteAll(WanAndroidUser.class); 41 | WanApplication.liteOrm.save(newUserInfo); 42 | } else { 43 | WanApplication.liteOrm.update(newUserInfo); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/model/entity/Article.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.model.entity; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 首页文章信息 7 | */ 8 | public class Article implements Serializable { 9 | /** 10 | * id : 1541 11 | * title : Android开发中dip,dpi,density,px等详解 12 | * chapterId : 198 13 | * chapterName : 基础概念 14 | * envelopePic : null 15 | * link : http://www.jianshu.com/p/cd66b7e01d4a 16 | * author : 希灵丶 17 | * origin : null 18 | * publishTime : 1511766156000 19 | * zan : null 20 | * desc : null 21 | * visible : 0 22 | * niceDate : 1天前 23 | * courseId : 13 24 | * collect : false 25 | */ 26 | private int id; 27 | private String title; 28 | private int chapterId; 29 | private String chapterName; 30 | private Object envelopePic; 31 | private String link; 32 | private String author; 33 | private Object origin; 34 | private long publishTime; 35 | private int zan; 36 | private String desc; 37 | private int visible; 38 | private String niceDate; 39 | private int courseId; 40 | private boolean collect; 41 | 42 | public int getId() { 43 | return id; 44 | } 45 | 46 | public void setId(int id) { 47 | this.id = id; 48 | } 49 | 50 | public String getTitle() { 51 | return title; 52 | } 53 | 54 | public void setTitle(String title) { 55 | this.title = title; 56 | } 57 | 58 | public int getChapterId() { 59 | return chapterId; 60 | } 61 | 62 | public void setChapterId(int chapterId) { 63 | this.chapterId = chapterId; 64 | } 65 | 66 | public String getChapterName() { 67 | return chapterName; 68 | } 69 | 70 | public void setChapterName(String chapterName) { 71 | this.chapterName = chapterName; 72 | } 73 | 74 | public Object getEnvelopePic() { 75 | return envelopePic; 76 | } 77 | 78 | public void setEnvelopePic(Object envelopePic) { 79 | this.envelopePic = envelopePic; 80 | } 81 | 82 | public String getLink() { 83 | return link; 84 | } 85 | 86 | public void setLink(String link) { 87 | this.link = link; 88 | } 89 | 90 | public String getAuthor() { 91 | return author; 92 | } 93 | 94 | public void setAuthor(String author) { 95 | this.author = author; 96 | } 97 | 98 | public Object getOrigin() { 99 | return origin; 100 | } 101 | 102 | public void setOrigin(Object origin) { 103 | this.origin = origin; 104 | } 105 | 106 | public long getPublishTime() { 107 | return publishTime; 108 | } 109 | 110 | public void setPublishTime(long publishTime) { 111 | this.publishTime = publishTime; 112 | } 113 | 114 | public int getZan() { 115 | return zan; 116 | } 117 | 118 | public void setZan(int zan) { 119 | this.zan = zan; 120 | } 121 | 122 | public String getDesc() { 123 | return desc; 124 | } 125 | 126 | public void setDesc(String desc) { 127 | this.desc = desc; 128 | } 129 | 130 | public int getVisible() { 131 | return visible; 132 | } 133 | 134 | public void setVisible(int visible) { 135 | this.visible = visible; 136 | } 137 | 138 | public String getNiceDate() { 139 | return niceDate; 140 | } 141 | 142 | public void setNiceDate(String niceDate) { 143 | this.niceDate = niceDate; 144 | } 145 | 146 | public int getCourseId() { 147 | return courseId; 148 | } 149 | 150 | public void setCourseId(int courseId) { 151 | this.courseId = courseId; 152 | } 153 | 154 | public boolean isCollect() { 155 | return collect; 156 | } 157 | 158 | public void setCollect(boolean collect) { 159 | this.collect = collect; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/model/entity/Cid.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.model.entity; 2 | 3 | /** 4 | * 分类数据 替代{@link Tree} 5 | */ 6 | @Deprecated 7 | public class Cid { 8 | 9 | private int cidId; 10 | 11 | private String cidTitle; 12 | 13 | public int getCidId() { 14 | return cidId; 15 | } 16 | 17 | public void setCidId(int cidId) { 18 | this.cidId = cidId; 19 | } 20 | 21 | public String getCidTitle() { 22 | return cidTitle; 23 | } 24 | 25 | public void setCidTitle(String cidTitle) { 26 | this.cidTitle = cidTitle; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/model/entity/CollectedArticle.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.model.entity; 2 | 3 | /** 4 | * 收藏的文章信息 5 | *

6 | * 由于收藏的文章信息大部分与Article相同
7 | * 并且存在一个BaseArticlesFragment,为了重用,所以这里直接继承Article 8 | *

9 | */ 10 | public class CollectedArticle extends Article { 11 | 12 | /** 13 | * id : 783 14 | * originId : 1494 15 | * title : 我的 Android 面试故事 | 13家面试记录 16 | * chapterId : 73 17 | * chapterName : 面试相关 18 | * envelopePic : null 19 | * link : https://mp.weixin.qq.com/s/N_3jUA1C-W1B-X6DQSXVTQ 20 | * author : 剑西 21 | * origin : null 22 | * publishTime : 1512020515000 23 | * zan : 0 24 | * desc : null 25 | * visible : 0 26 | * niceDate : 18分钟前 27 | * courseId : 13 28 | * userId : 883 29 | */ 30 | 31 | //originId才是文章的id 32 | private int originId; 33 | private int userId; 34 | 35 | public int getOriginId() { 36 | return originId; 37 | } 38 | 39 | public void setOriginId(int originId) { 40 | this.originId = originId; 41 | } 42 | 43 | public int getUserId() { 44 | return userId; 45 | } 46 | 47 | public void setUserId(int userId) { 48 | this.userId = userId; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/model/entity/HotKey.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.model.entity; 2 | 3 | /** 4 | * Created by ${jay} on ${2016/8/17 5 | */ 6 | 7 | public class HotKey { 8 | /** 9 | * id : 6 10 | * name : 面试 11 | * link : null 12 | * visible : 1 13 | * order : 1 14 | */ 15 | private int id; 16 | private String name; 17 | private String link; 18 | private int visible; 19 | private int order; 20 | 21 | public int getId() { 22 | return id; 23 | } 24 | 25 | public void setId(int id) { 26 | this.id = id; 27 | } 28 | 29 | public String getName() { 30 | return name; 31 | } 32 | 33 | public void setName(String name) { 34 | this.name = name; 35 | } 36 | 37 | public String getLink() { 38 | return link; 39 | } 40 | 41 | public void setLink(String link) { 42 | this.link = link; 43 | } 44 | 45 | public int getVisible() { 46 | return visible; 47 | } 48 | 49 | public void setVisible(int visible) { 50 | this.visible = visible; 51 | } 52 | 53 | public int getOrder() { 54 | return order; 55 | } 56 | 57 | public void setOrder(int order) { 58 | this.order = order; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/model/entity/Tree.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.model.entity; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * "知识体系" 7 | */ 8 | public class Tree { 9 | /** 10 | * id : 150 11 | * name : 开发环境 12 | * courseId : 13 13 | * parentChapterId : 0 14 | * order : 1 15 | * visible : 1 16 | * children : [{"id":60,"name":"Android Studio相关","courseId":13,"parentChapterId":150,"order":1000,"visible":1,"children":[]},{"id":169,"name":"gradle","courseId":13,"parentChapterId":150,"order":1001,"visible":1,"children":[]}] 17 | */ 18 | 19 | private int id; 20 | private String name; 21 | private int courseId; 22 | private int parentChapterId; 23 | private int order; 24 | private int visible; 25 | private List children; 26 | 27 | public int getId() { 28 | return id; 29 | } 30 | 31 | public void setId(int id) { 32 | this.id = id; 33 | } 34 | 35 | public String getName() { 36 | return name; 37 | } 38 | 39 | public void setName(String name) { 40 | this.name = name; 41 | } 42 | 43 | public int getCourseId() { 44 | return courseId; 45 | } 46 | 47 | public void setCourseId(int courseId) { 48 | this.courseId = courseId; 49 | } 50 | 51 | public int getParentChapterId() { 52 | return parentChapterId; 53 | } 54 | 55 | public void setParentChapterId(int parentChapterId) { 56 | this.parentChapterId = parentChapterId; 57 | } 58 | 59 | public int getOrder() { 60 | return order; 61 | } 62 | 63 | public void setOrder(int order) { 64 | this.order = order; 65 | } 66 | 67 | public int getVisible() { 68 | return visible; 69 | } 70 | 71 | public void setVisible(int visible) { 72 | this.visible = visible; 73 | } 74 | 75 | public List getChildren() { 76 | return children; 77 | } 78 | 79 | public void setChildren(List children) { 80 | this.children = children; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/model/entity/WanAndroidUser.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.model.entity; 2 | 3 | 4 | import com.litesuits.orm.db.annotation.PrimaryKey; 5 | import com.litesuits.orm.db.annotation.Table; 6 | import com.litesuits.orm.db.enums.AssignType; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * WanAndroid用户信息 12 | */ 13 | @Table("user") 14 | public class WanAndroidUser { 15 | 16 | /** 17 | * id : 880 18 | * username : test~11111 19 | * password : 123****890 20 | * icon : null 21 | * type : 0 22 | * collectIds : [] 23 | */ 24 | @PrimaryKey(AssignType.BY_MYSELF) 25 | private int id; 26 | private String username; 27 | private String password; 28 | private String icon; 29 | private int type; 30 | 31 | private List collectIds; 32 | 33 | public int getId() { 34 | return id; 35 | } 36 | 37 | public void setId(int id) { 38 | this.id = id; 39 | } 40 | 41 | public String getUsername() { 42 | return username; 43 | } 44 | 45 | public void setUsername(String username) { 46 | this.username = username; 47 | } 48 | 49 | public String getPassword() { 50 | return password; 51 | } 52 | 53 | public void setPassword(String password) { 54 | this.password = password; 55 | } 56 | 57 | public String getIcon() { 58 | return icon; 59 | } 60 | 61 | public void setIcon(String icon) { 62 | this.icon = icon; 63 | } 64 | 65 | public int getType() { 66 | return type; 67 | } 68 | 69 | public void setType(int type) { 70 | this.type = type; 71 | } 72 | 73 | public List getCollectIds() { 74 | return collectIds; 75 | } 76 | 77 | public void setCollectIds(List collectIds) { 78 | this.collectIds = collectIds; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/utils/ActivityUtils.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.utils; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.support.v4.app.Fragment; 5 | import android.support.v4.app.FragmentManager; 6 | import android.support.v4.app.FragmentTransaction; 7 | import android.util.Log; 8 | 9 | /** 10 | * Created by ${jay} on ${2016/8/17 11 | */ 12 | public class ActivityUtils { 13 | 14 | private static final String TAG = "ActivityUtils"; 15 | 16 | public static void addFragmentToActivity(@NonNull FragmentManager fragmentManager, 17 | @NonNull Fragment fragment, int frameId) { 18 | addFragmentToActivity(fragmentManager, fragment, frameId, true); 19 | } 20 | 21 | public static void addFragmentToActivity(@NonNull FragmentManager fragmentManager, 22 | @NonNull Fragment fragment, int frameId, boolean addToBackStack) { 23 | if (fragment.isAdded()) { 24 | Log.w(TAG, "addFragmentToActivity: fragment is added:" + fragment.getClass().getName()); 25 | return; 26 | } 27 | FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); 28 | fragmentTransaction.add(frameId, fragment); 29 | if (addToBackStack) { 30 | fragmentTransaction.addToBackStack(null); 31 | } 32 | fragmentTransaction.commit(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/utils/ImeUtils.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.utils; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.NonNull; 5 | import android.view.View; 6 | import android.view.inputmethod.InputMethodManager; 7 | 8 | 9 | public class ImeUtils { 10 | private ImeUtils() { 11 | } 12 | 13 | public static void showIme(@NonNull View view) { 14 | InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 15 | // 接受软键盘输入的编辑文本或其它视图 16 | assert imm != null; 17 | imm.showSoftInput(view, InputMethodManager.SHOW_FORCED); 18 | } 19 | 20 | public static void hideIme(@NonNull View view) { 21 | InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context 22 | .INPUT_METHOD_SERVICE); 23 | if (imm != null) { 24 | imm.hideSoftInputFromWindow(view.getWindowToken(), 0); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/utils/PackageUtils.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.utils; 2 | 3 | import android.content.Context; 4 | import android.content.pm.ApplicationInfo; 5 | import android.content.pm.PackageManager; 6 | 7 | /** 8 | * Created by ${jay} on ${2016/8/17 9 | */ 10 | public class PackageUtils { 11 | 12 | public static boolean checkApkExist(Context context, String packageName) { 13 | if (packageName == null || "".equals(packageName)) { 14 | return false; 15 | } 16 | try { 17 | ApplicationInfo info = context.getPackageManager().getApplicationInfo(packageName, 18 | PackageManager.GET_UNINSTALLED_PACKAGES); 19 | return true; 20 | } catch (PackageManager.NameNotFoundException e) { 21 | return false; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/utils/ScreenUtil.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.utils; 2 | 3 | import android.content.Context; 4 | import android.util.DisplayMetrics; 5 | import android.view.Display; 6 | import android.view.WindowManager; 7 | 8 | import java.lang.reflect.Method; 9 | 10 | /** 11 | * 12 | */ 13 | public class ScreenUtil { 14 | /** 15 | * 获取屏幕宽(不含虚拟键盘) 16 | */ 17 | public static int getScrWidth(Context context) { 18 | int widthPixels = context.getResources().getDisplayMetrics().widthPixels; 19 | return widthPixels; 20 | } 21 | 22 | /** 23 | * 获取屏幕高(不含虚拟键盘) 24 | */ 25 | public static int getScrHeight(Context context) { 26 | int heightPixels = context.getResources().getDisplayMetrics().heightPixels; 27 | return heightPixels; 28 | } 29 | 30 | /** 31 | * 获取屏幕真实高度(包含虚拟键盘) 32 | */ 33 | public static int getScrRealWidth(Context context) { 34 | int px = 0; 35 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 36 | Display display = windowManager.getDefaultDisplay(); 37 | DisplayMetrics displayMetrics = new DisplayMetrics(); 38 | Class c; 39 | try { 40 | c = Class.forName("android.view.Display"); 41 | Method method = c.getMethod("getRealMetrics", DisplayMetrics.class); 42 | method.invoke(display, displayMetrics); 43 | px = displayMetrics.widthPixels; 44 | } catch (Exception e) { 45 | e.printStackTrace(); 46 | } 47 | return px; 48 | } 49 | 50 | /** 51 | * 获取屏幕真实高度(包含虚拟键盘) 52 | */ 53 | public static int getScrRealHeight(Context context) { 54 | int px = 0; 55 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 56 | Display display = windowManager.getDefaultDisplay(); 57 | DisplayMetrics displayMetrics = new DisplayMetrics(); 58 | Class c; 59 | try { 60 | c = Class.forName("android.view.Display"); 61 | Method method = c.getMethod("getRealMetrics", DisplayMetrics.class); 62 | method.invoke(display, displayMetrics); 63 | px = displayMetrics.heightPixels; 64 | } catch (Exception e) { 65 | e.printStackTrace(); 66 | } 67 | return px; 68 | } 69 | 70 | /** 71 | * 获取虚拟键盘高度 72 | */ 73 | public static int getNavigationButtonHeight(Context context) { 74 | return getScrRealHeight(context) - getScrHeight(context); 75 | } 76 | 77 | /** 78 | * 获取虚拟键盘的宽度 79 | */ 80 | public static int getNavigationButtonWidth(Context context) { 81 | return getScrRealWidth(context) - getScrWidth(context); 82 | } 83 | 84 | /** 85 | * 获取状态栏高度 86 | */ 87 | public static int getTitleBarHeight(Context context) { 88 | int statusHeight = -1; 89 | try { 90 | Class clazz = Class.forName("com.android.internal.R$dimen"); 91 | Object object = clazz.newInstance(); 92 | int height = Integer.parseInt(clazz.getField("status_bar_height") 93 | .get(object).toString()); 94 | statusHeight = context.getResources().getDimensionPixelSize(height); 95 | } catch (Exception e) { 96 | e.printStackTrace(); 97 | } 98 | return statusHeight; 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/utils/SharesUtils.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.utils; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | 7 | import com.wanandroid.R; 8 | 9 | /** 10 | * Created by ${jay} on ${2016/8/17 11 | */ 12 | 13 | public class SharesUtils { 14 | 15 | public static void share(Context context, int stringRes) { 16 | share(context, context.getString(stringRes)); 17 | } 18 | 19 | 20 | public static void shareImage(Context context, Uri uri, String title) { 21 | Intent shareIntent = new Intent(); 22 | shareIntent.setAction(Intent.ACTION_SEND); 23 | shareIntent.putExtra(Intent.EXTRA_STREAM, uri); 24 | shareIntent.setType("image/jpeg"); 25 | context.startActivity(Intent.createChooser(shareIntent, title)); 26 | } 27 | 28 | 29 | public static void share(Context context, String extraText) { 30 | Intent intent = new Intent(Intent.ACTION_SEND); 31 | intent.setType("text/plain"); 32 | intent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.action_share)); 33 | intent.putExtra(Intent.EXTRA_TEXT, extraText); 34 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 35 | context.startActivity( 36 | Intent.createChooser(intent, context.getString(R.string.action_share))); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/utils/ViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.utils; 2 | 3 | import android.util.SparseArray; 4 | import android.view.View; 5 | 6 | public class ViewHolder { 7 | 8 | @SuppressWarnings("unchecked") 9 | public static T get(View view, int id) { 10 | SparseArray viewHolder = (SparseArray) view.getTag(); 11 | if (viewHolder == null) { 12 | viewHolder = new SparseArray(); 13 | view.setTag(viewHolder); 14 | } 15 | View childView = viewHolder.get(id); 16 | if (childView == null) { 17 | childView = view.findViewById(id); 18 | viewHolder.put(id, childView); 19 | } 20 | return (T) childView; 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/widget/AutoClearEditText.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import android.util.AttributeSet; 6 | import android.view.MotionEvent; 7 | 8 | /** 9 | * 自定义带删除图标的EditText控件,使用android:drawableRight属性设置删除图标 10 | */ 11 | public class AutoClearEditText extends android.support.v7.widget.AppCompatEditText { 12 | 13 | private Drawable mClearDrawable; 14 | 15 | private OnClearTextListener mOnClearTextListener; 16 | 17 | public AutoClearEditText(Context context) { 18 | super(context); 19 | initClearDrawable(); 20 | } 21 | 22 | public AutoClearEditText(Context context, AttributeSet attrs) { 23 | super(context, attrs); 24 | initClearDrawable(); 25 | } 26 | 27 | public AutoClearEditText(Context context, AttributeSet attrs, int defStyleAttr) { 28 | super(context, attrs, defStyleAttr); 29 | initClearDrawable(); 30 | } 31 | 32 | public void setOnClearTextListener(OnClearTextListener onClearTextListener) { 33 | mOnClearTextListener = onClearTextListener; 34 | } 35 | 36 | private void initClearDrawable() { 37 | Drawable[] drawables = getCompoundDrawables(); 38 | //在这里获取删除图标 39 | if (mClearDrawable == null) { 40 | mClearDrawable = drawables[2]; 41 | } 42 | //默认不显示清空按钮 43 | setCompoundDrawables(drawables[0], drawables[1], null, drawables[3]); 44 | } 45 | 46 | @Override 47 | protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { 48 | super.onTextChanged(text, start, lengthBefore, lengthAfter); 49 | if (mClearDrawable == null) { 50 | return; 51 | } 52 | Drawable[] drawables = getCompoundDrawables(); 53 | if (getText().length() > 0) { 54 | setCompoundDrawables(drawables[0], drawables[1], mClearDrawable, drawables[3]); 55 | } else { 56 | setCompoundDrawables(drawables[0], drawables[1], null, drawables[3]); 57 | } 58 | } 59 | 60 | @Override 61 | public boolean onTouchEvent(MotionEvent event) { 62 | if (event.getAction() == MotionEvent.ACTION_DOWN && mClearDrawable != null) { 63 | float x = event.getX(); 64 | if (x < getWidth() - getPaddingRight() && x > getWidth() - getTotalPaddingRight()) { 65 | setText(""); 66 | if (mOnClearTextListener != null) { 67 | mOnClearTextListener.onClearText(); 68 | } 69 | return true; 70 | } 71 | } 72 | return super.onTouchEvent(event); 73 | } 74 | 75 | /** 76 | * 当EditText清空按钮被点击的回调 77 | */ 78 | public interface OnClearTextListener { 79 | void onClearText(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/widget/NestedScaleBehavior.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.widget; 2 | 3 | import android.animation.AnimatorSet; 4 | import android.animation.ObjectAnimator; 5 | import android.content.Context; 6 | import android.support.design.widget.CoordinatorLayout; 7 | import android.support.v4.widget.SwipeRefreshLayout; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.util.AttributeSet; 10 | import android.view.View; 11 | 12 | /** 13 | * 随着RecycleView滑动放大的Behavior 14 | */ 15 | public class NestedScaleBehavior extends CoordinatorLayout.Behavior { 16 | 17 | private AnimatorSet animatorSet; 18 | 19 | public NestedScaleBehavior(Context context, AttributeSet attrs) { 20 | super(context, attrs); 21 | } 22 | 23 | @Override 24 | public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) { 25 | return true; 26 | } 27 | 28 | @Override 29 | public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { 30 | 31 | //由于RecycleView很常见于与SwipeRefreshLayout使用,所以提取 32 | if (target instanceof SwipeRefreshLayout) { 33 | target = ((SwipeRefreshLayout) target).getChildAt(0); 34 | } 35 | 36 | if (target instanceof RecyclerView) { 37 | int scrollY = ((RecyclerView) target).computeVerticalScrollOffset(); 38 | if (scrollY > coordinatorLayout.getHeight() / 3 && child.getScaleX() < 1.0f) { 39 | if (animatorSet == null || !animatorSet.isRunning()) { 40 | ObjectAnimator scaleX = ObjectAnimator.ofFloat(child, "scaleX", 0.0f, 1.0f); 41 | ObjectAnimator scaleY = ObjectAnimator.ofFloat(child, "scaleY", 0.0f, 1.0f); 42 | animatorSet = new AnimatorSet(); 43 | animatorSet.playTogether(scaleX, scaleY); 44 | animatorSet.setDuration(500); 45 | animatorSet.start(); 46 | } 47 | } 48 | if (scrollY <= 60) { 49 | if (animatorSet != null && animatorSet.isRunning()) { 50 | animatorSet.end(); 51 | } 52 | child.setScaleY(0.0f); 53 | child.setScaleX(0.0f); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanandroid/widget/WebViewFragment.java: -------------------------------------------------------------------------------- 1 | package com.wanandroid.widget; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.webkit.WebView; 9 | 10 | /** 11 | * 一个包含WebView的Fragment,来自Android Sdk 12 | */ 13 | public class WebViewFragment extends Fragment { 14 | private WebView mWebView; 15 | private boolean mIsWebViewAvailable; 16 | 17 | public WebViewFragment() { 18 | } 19 | 20 | /** 21 | * Called to instantiate the view. Creates and returns the WebView. 22 | */ 23 | @Override 24 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 25 | Bundle savedInstanceState) { 26 | if (mWebView != null) { 27 | mWebView.destroy(); 28 | } 29 | mWebView = new WebView(getContext()); 30 | mIsWebViewAvailable = true; 31 | return mWebView; 32 | } 33 | 34 | /** 35 | * Called when the fragment is visible to the user and actively running. Resumes the WebView. 36 | */ 37 | @Override 38 | public void onPause() { 39 | super.onPause(); 40 | mWebView.onPause(); 41 | mWebView.pauseTimers(); 42 | } 43 | 44 | /** 45 | * Called when the fragment is no longer resumed. Pauses the WebView. 46 | */ 47 | @Override 48 | public void onResume() { 49 | mWebView.onResume(); 50 | mWebView.resumeTimers(); 51 | super.onResume(); 52 | } 53 | 54 | /** 55 | * Called when the WebView has been detached from the fragment. 56 | * The WebView is no longer available after this time. 57 | */ 58 | @Override 59 | public void onDestroyView() { 60 | mIsWebViewAvailable = false; 61 | super.onDestroyView(); 62 | } 63 | 64 | /** 65 | * Called when the fragment is no longer in use. Destroys the internal state of the WebView. 66 | */ 67 | @Override 68 | public void onDestroy() { 69 | if (mWebView != null) { 70 | mWebView.destroy(); 71 | mWebView = null; 72 | } 73 | super.onDestroy(); 74 | } 75 | 76 | /** 77 | * Gets the WebView. 78 | */ 79 | public WebView getWebView() { 80 | return mIsWebViewAvailable ? mWebView : null; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/classify_dialog_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/classify_view_item_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/clear_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/edit_text_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/edit_text_cursor.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/flow_item_selector.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 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/login_button_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/progressbar_horizontal.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 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/recycle_view_item_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/tip_tv_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/welcome_user_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_about_me.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 13 | 14 | 23 | 24 | 30 | 31 | 36 | 37 | 45 | 46 | 47 | 48 | 49 | 53 | 54 | 59 | 60 | 65 | 66 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_login_or_resister.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 29 | 30 | 35 | 36 | 43 | 44 | 45 | 46 | 52 | 53 | 60 | 61 | 62 | 69 | 70 | 77 | 78 | 79 | 84 | 85 |