├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── test.png │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_home_black_24dp.png │ │ │ │ ├── ic_ios_black_24dp.png │ │ │ │ ├── ic_android_black_24dp.png │ │ │ │ ├── ic_card_giftcard_black_24dp.png │ │ │ │ └── ic_important_devices_black_24dp.png │ │ │ ├── drawable-hdpi │ │ │ │ ├── ic_list.png │ │ │ │ ├── ic_favorite.png │ │ │ │ ├── ic_brightness_2.png │ │ │ │ └── ic_favorite_border.png │ │ │ ├── drawable-mdpi │ │ │ │ ├── ic_list.png │ │ │ │ ├── ic_favorite.png │ │ │ │ ├── ic_brightness_2.png │ │ │ │ └── ic_favorite_border.png │ │ │ ├── drawable-xhdpi │ │ │ │ ├── ic_list.png │ │ │ │ ├── ic_favorite.png │ │ │ │ ├── ic_brightness_2.png │ │ │ │ └── ic_favorite_border.png │ │ │ ├── drawable-xxhdpi │ │ │ │ ├── ic_list.png │ │ │ │ ├── ic_favorite.png │ │ │ │ ├── ic_brightness_2.png │ │ │ │ └── ic_favorite_border.png │ │ │ ├── drawable-xxxhdpi │ │ │ │ ├── ic_list.png │ │ │ │ ├── ic_favorite.png │ │ │ │ ├── ic_brightness_2.png │ │ │ │ └── ic_favorite_border.png │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable-v21 │ │ │ │ └── selecte_bk.xml │ │ │ ├── values-v19 │ │ │ │ └── styles.xml │ │ │ ├── values │ │ │ │ ├── dimens.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── styles.xml │ │ │ │ └── strings.xml │ │ │ ├── drawable │ │ │ │ ├── selecte_bk.xml │ │ │ │ ├── home_card_foreground.xml │ │ │ │ ├── classification_card_foreground.xml │ │ │ │ ├── home_card_foreground_selector.xml │ │ │ │ └── classification_card_foreground_selector.xml │ │ │ ├── values-w820dp │ │ │ │ └── dimens.xml │ │ │ ├── menu │ │ │ │ ├── menu_photo.xml │ │ │ │ ├── menu_home.xml │ │ │ │ └── menu_webview.xml │ │ │ ├── values-v21 │ │ │ │ └── styles.xml │ │ │ ├── values-night │ │ │ │ └── colors.xml │ │ │ └── layout │ │ │ │ ├── activity_photo.xml │ │ │ │ ├── view_image.xml │ │ │ │ ├── fragment_home.xml │ │ │ │ ├── activity_collection.xml │ │ │ │ ├── fragment_fuli.xml │ │ │ │ ├── fragment_classification.xml │ │ │ │ ├── webview_activity.xml │ │ │ │ ├── fragment_setting.xml │ │ │ │ ├── view_home_item.xml │ │ │ │ └── view_classification_item.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── fall │ │ │ │ └── gank │ │ │ │ ├── callback │ │ │ │ ├── SwipeRefreshListener.java │ │ │ │ ├── OnScrollLastListener.java │ │ │ │ ├── ViewClickCallback.java │ │ │ │ ├── BaseListCallback.java │ │ │ │ ├── HomeTitleListener.java │ │ │ │ ├── LikeClickListener.java │ │ │ │ └── BaseActivityCallback.java │ │ │ │ ├── viewmodel │ │ │ │ ├── ITestModel.java │ │ │ │ ├── HomeViewModel.java │ │ │ │ ├── ImageItemViewModel.java │ │ │ │ ├── WebViewModel.java │ │ │ │ ├── SettingViewModel.java │ │ │ │ ├── FuliViewModel.java │ │ │ │ ├── MainViewModel.java │ │ │ │ ├── BaseListViewModel.java │ │ │ │ ├── ClassificationViewModel.java │ │ │ │ ├── ClassificationItemViewModel.java │ │ │ │ └── HomeItemViewModel.java │ │ │ │ ├── entity │ │ │ │ ├── SettingData.java │ │ │ │ ├── BaseEntity.java │ │ │ │ ├── ClassificationResultsEntity.java │ │ │ │ ├── ClassificationEntity.java │ │ │ │ └── GankDateData.java │ │ │ │ ├── network │ │ │ │ ├── converter │ │ │ │ │ ├── ResultException.java │ │ │ │ │ ├── GsonResponseBodyConverter.java │ │ │ │ │ ├── GsonRequestBodyConverter.java │ │ │ │ │ └── GsonConverterFactory.java │ │ │ │ ├── model │ │ │ │ │ ├── IGankModel.java │ │ │ │ │ ├── IDataManager.java │ │ │ │ │ └── impl │ │ │ │ │ │ ├── GankModel.java │ │ │ │ │ │ └── DataManager.java │ │ │ │ ├── GankService.java │ │ │ │ └── HttpMethods.java │ │ │ │ ├── presenter │ │ │ │ ├── factory │ │ │ │ │ ├── PresenterFactory.java │ │ │ │ │ └── IPresenterFactory.java │ │ │ │ ├── WebViewActivityPresenter.java │ │ │ │ ├── MainActivityPresenter.java │ │ │ │ ├── TestPresenter.java │ │ │ │ ├── SettingFragmentPresenter.java │ │ │ │ ├── FuliFragmentPresenter.java │ │ │ │ ├── HomeFragmentPresenter.java │ │ │ │ └── ClassificationPresenter.java │ │ │ │ ├── core │ │ │ │ ├── BaseView.java │ │ │ │ ├── IPresenter.java │ │ │ │ ├── BaseListFragment.java │ │ │ │ ├── AttachPresenterHelper.java │ │ │ │ ├── BasePresenter.java │ │ │ │ ├── BaseActivity.java │ │ │ │ ├── BaseListPresenter.java │ │ │ │ └── BaseFragment.java │ │ │ │ ├── Utils │ │ │ │ ├── Utils.java │ │ │ │ ├── TimeUtils.java │ │ │ │ ├── ListLoadNextHelper.java │ │ │ │ ├── RxUtils.java │ │ │ │ ├── BindingUtils.java │ │ │ │ └── MDStatusBarCompat.java │ │ │ │ ├── adapter │ │ │ │ ├── HomeAdapterDecorator.java │ │ │ │ ├── ViewPagerAdapter.java │ │ │ │ └── ClassificationAdapterDecorator.java │ │ │ │ ├── view │ │ │ │ ├── widget │ │ │ │ │ ├── NoScrollViewPager.java │ │ │ │ │ ├── SlowlyScrollLinearLayoutManager.java │ │ │ │ │ └── RecyclerviewScrollHelper.java │ │ │ │ ├── activity │ │ │ │ │ ├── CollectionActivity.java │ │ │ │ │ ├── PhotoActivity.java │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── WebViewActivity.java │ │ │ │ └── fragment │ │ │ │ │ ├── SettingFragment.java │ │ │ │ │ ├── FuliFragment.java │ │ │ │ │ ├── HomeFragment.java │ │ │ │ │ └── ClassificationFragment.java │ │ │ │ ├── database │ │ │ │ └── Collection.java │ │ │ │ └── MyApplicationLike.java │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── fall │ │ │ └── gank │ │ │ ├── PrettyTest.java │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── com │ │ └── fall │ │ └── gank │ │ └── ExampleInstrumentedTest.java ├── proguard-rules.pro ├── keep_in_main_dex.txt ├── tinkerpatch.gradle └── build.gradle ├── settings.gradle ├── gank.apk ├── meitu_0.jpg ├── meitu_1.jpg ├── .idea ├── copyright │ └── profiles_settings.xml ├── vcs.xml ├── modules.xml ├── runConfigurations.xml ├── gradle.xml ├── compiler.xml └── misc.xml ├── Screenshot_20161213-172915.png ├── Screenshot_20161213-173122.png ├── Screenshot_20161213-173128.png ├── Screenshot_20161213-173133.png ├── Screenshot_20161213-173248.png ├── Screenshot_20161213-173348.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── gradle.properties ├── README.md ├── gradlew.bat └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /gank.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/gank.apk -------------------------------------------------------------------------------- /meitu_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/meitu_0.jpg -------------------------------------------------------------------------------- /meitu_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/meitu_1.jpg -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Screenshot_20161213-172915.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/Screenshot_20161213-172915.png -------------------------------------------------------------------------------- /Screenshot_20161213-173122.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/Screenshot_20161213-173122.png -------------------------------------------------------------------------------- /Screenshot_20161213-173128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/Screenshot_20161213-173128.png -------------------------------------------------------------------------------- /Screenshot_20161213-173133.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/Screenshot_20161213-173133.png -------------------------------------------------------------------------------- /Screenshot_20161213-173248.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/Screenshot_20161213-173248.png -------------------------------------------------------------------------------- /Screenshot_20161213-173348.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/Screenshot_20161213-173348.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/mipmap-xxxhdpi/test.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/drawable-hdpi/ic_list.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/drawable-mdpi/ic_list.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/drawable-xhdpi/ic_list.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/drawable-xxhdpi/ic_list.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/drawable-xxxhdpi/ic_list.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_favorite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/drawable-hdpi/ic_favorite.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_favorite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/drawable-mdpi/ic_favorite.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_favorite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/drawable-xhdpi/ic_favorite.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_brightness_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/drawable-hdpi/ic_brightness_2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_brightness_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/drawable-mdpi/ic_brightness_2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_favorite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/drawable-xxhdpi/ic_favorite.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_favorite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/drawable-xxxhdpi/ic_favorite.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_brightness_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/drawable-xhdpi/ic_brightness_2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_brightness_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/drawable-xxhdpi/ic_brightness_2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_favorite_border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/drawable-hdpi/ic_favorite_border.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_favorite_border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/drawable-mdpi/ic_favorite_border.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_favorite_border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/drawable-xhdpi/ic_favorite_border.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_favorite_border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/drawable-xxhdpi/ic_favorite_border.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_brightness_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/drawable-xxxhdpi/ic_brightness_2.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_home_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_home_black_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_ios_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_ios_black_24dp.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_favorite_border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/drawable-xxxhdpi/ic_favorite_border.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_android_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_android_black_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_card_giftcard_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_card_giftcard_black_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_important_devices_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoxikang/gank.io-with-MVVM/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_important_devices_black_24dp.png -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/selecte_bk.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values-v19/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/callback/SwipeRefreshListener.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.callback; 2 | 3 | /** 4 | * Created by qqq34 on 2016/11/30. 5 | */ 6 | 7 | public interface SwipeRefreshListener { 8 | void onRefresh(); 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/callback/OnScrollLastListener.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.callback; 2 | 3 | /** 4 | * Created by qqq34 on 2016/12/1. 5 | */ 6 | 7 | public interface OnScrollLastListener { 8 | void onScrollLast(); 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/callback/ViewClickCallback.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.callback; 2 | 3 | import android.view.View; 4 | 5 | /** 6 | * Created by qqq34 on 2016/11/24. 7 | */ 8 | 9 | public interface ViewClickCallback { 10 | void onClick(View view); 11 | } 12 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/viewmodel/ITestModel.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.viewmodel; 2 | 3 | /** 4 | * Created by qqq34 on 2017/1/18. 5 | * 多presenter 情况的演示 6 | * 如果需要用到这个模块的界面的ViewModel实现这个接口就行 7 | */ 8 | 9 | public interface ITestModel { 10 | void changeTitle(String s); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/callback/BaseListCallback.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.callback; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by qqq34 on 2016/12/1. 7 | */ 8 | 9 | public interface BaseListCallback { 10 | 11 | void onListLoadFinished(List list); 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/selecte_bk.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/callback/HomeTitleListener.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.callback; 2 | 3 | import com.fall.gank.viewmodel.HomeItemViewModel; 4 | 5 | /** 6 | * Created by qqq34 on 2016/12/13. 7 | */ 8 | 9 | public interface HomeTitleListener { 10 | void onHomeTitleClick(HomeItemViewModel homeItemViewModel); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/callback/LikeClickListener.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.callback; 2 | 3 | import com.fall.gank.viewmodel.ClassificationItemViewModel; 4 | 5 | /** 6 | * Created by qqq34 on 2016/12/8. 7 | */ 8 | 9 | public interface LikeClickListener { 10 | void onLikeClick(ClassificationItemViewModel classificationItemViewModel); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/home_card_foreground.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/classification_card_foreground.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/entity/SettingData.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.entity; 2 | 3 | /** 4 | * Created by qqq34 on 2016/12/9. 5 | */ 6 | 7 | public class SettingData { 8 | private boolean isDarkTheme; 9 | 10 | public boolean isDarkTheme() { 11 | return isDarkTheme; 12 | } 13 | 14 | public void setDarkTheme(boolean darkTheme) { 15 | isDarkTheme = darkTheme; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/viewmodel/HomeViewModel.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.viewmodel; 2 | 3 | import android.databinding.ObservableInt; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * Created by 康颢曦 on 2016/11/27. 9 | */ 10 | 11 | public class HomeViewModel extends BaseListViewModel implements Serializable { 12 | public final ObservableInt lastPosition = new ObservableInt(0); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_photo.xml: -------------------------------------------------------------------------------- 1 | 4 | 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/callback/BaseActivityCallback.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.callback; 2 | 3 | import android.content.Intent; 4 | 5 | import rx.Observable; 6 | 7 | /** 8 | * Created by 康颢曦 on 2016/11/27. 9 | */ 10 | 11 | public interface BaseActivityCallback { 12 | void onShowSnackBar(String s); 13 | void onStartActivity(Intent intent); 14 | Observable checkPermission(int resString, String... mPerms); 15 | } 16 | -------------------------------------------------------------------------------- /app/src/test/java/com/fall/gank/PrettyTest.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank; 2 | 3 | /** 4 | * Created by qqq34 on 2016/12/1. 5 | */ 6 | 7 | public class PrettyTest { 8 | /** 9 | * 根据输入值的大小返回字符串 10 | * @param a 输入值 11 | * @return 返回的字符串结果 12 | */ 13 | public String print(int a){ 14 | System.out.println("==========current input number is: " + a + "=========="); 15 | return a > 0? "大":"小"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/entity/BaseEntity.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.entity; 2 | 3 | /** 4 | * Created by qqq34 on 2016/11/25. 5 | */ 6 | public class BaseEntity 7 | { 8 | 9 | /** 10 | * error : false 11 | */ 12 | 13 | private boolean error; 14 | 15 | public boolean isError() { 16 | return error; 17 | } 18 | 19 | public void setError(boolean error) { 20 | this.error = error; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/network/converter/ResultException.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.network.converter; 2 | 3 | /** 4 | * Created by 康颢曦 on 2016/8/3. 5 | */ 6 | 7 | public class ResultException extends RuntimeException { 8 | 9 | private String errCode = "0"; 10 | 11 | public ResultException(String errCode, String msg) { 12 | super(msg); 13 | this.errCode = errCode; 14 | } 15 | 16 | public String getErrCode() { 17 | return errCode; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/presenter/factory/PresenterFactory.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.presenter.factory; 2 | 3 | import com.fall.gank.presenter.TestPresenter; 4 | import com.fall.gank.viewmodel.ITestModel; 5 | 6 | /** 7 | * Created by qqq34 on 2017/1/18. 8 | */ 9 | 10 | 11 | 12 | public class PresenterFactory implements IPresenterFactory{ 13 | @Override 14 | public TestPresenter getTextPresenter(ITestModel iTestModel) { 15 | return new TestPresenter(iTestModel); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/network/model/IGankModel.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.network.model; 2 | 3 | import com.fall.gank.entity.ClassificationEntity; 4 | import com.fall.gank.entity.HomeEntity; 5 | 6 | import rx.Observable; 7 | 8 | /** 9 | * Created by qqq34 on 2016/11/25. 10 | */ 11 | 12 | public abstract interface IGankModel 13 | { 14 | Observable getHomeData( int year, int mouth, int day); 15 | 16 | Observable getClassifiData(String section, int page); 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/presenter/factory/IPresenterFactory.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.presenter.factory; 2 | 3 | import com.fall.gank.presenter.TestPresenter; 4 | import com.fall.gank.viewmodel.ITestModel; 5 | 6 | /** 7 | * Created by qqq34 on 2017/1/18. 8 | * 一个多presenter情况的例子。 9 | * 如果有一部分业务逻辑相同的话,那么我们可以把相同的部分抽离出来作为一个通用的presenter。 10 | * 因为通用的presenter会有很多界面使用,所以为了方便以后的修改,通用的presenter需要用工厂来生成 11 | */ 12 | 13 | public interface IPresenterFactory { 14 | TestPresenter getTextPresenter(ITestModel iTestModel); 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/viewmodel/ImageItemViewModel.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.viewmodel; 2 | 3 | import android.databinding.BaseObservable; 4 | import android.databinding.ObservableField; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * Created by qqq34 on 2016/12/6. 10 | */ 11 | 12 | public class ImageItemViewModel extends BaseObservable implements Serializable{ 13 | public ObservableField url = new ObservableField<>(); 14 | 15 | public ImageItemViewModel(String url) { 16 | this.url.set(url); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/viewmodel/WebViewModel.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.viewmodel; 2 | 3 | import android.databinding.BaseObservable; 4 | import android.databinding.ObservableField; 5 | import android.databinding.ObservableInt; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * Created by qqq34 on 2016/12/8. 11 | */ 12 | 13 | public class WebViewModel extends BaseObservable implements Serializable{ 14 | public final ObservableField webViewUrl = new ObservableField<>(); 15 | public final ObservableInt progress = new ObservableInt(); 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/network/model/IDataManager.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.network.model; 2 | 3 | import com.fall.gank.entity.ClassificationEntity; 4 | import com.fall.gank.viewmodel.ClassificationItemViewModel; 5 | 6 | import java.text.ParseException; 7 | 8 | import rx.Observable; 9 | 10 | /** 11 | * Created by qqq34 on 2016/11/30. 12 | */ 13 | 14 | public interface IDataManager { 15 | 16 | Observable getHomeData(int page); 17 | 18 | Observable getClassificationData(String section, int page); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/presenter/WebViewActivityPresenter.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.presenter; 2 | 3 | import com.fall.gank.core.BasePresenter; 4 | import com.fall.gank.viewmodel.WebViewModel; 5 | 6 | /** 7 | * Created by qqq34 on 2016/12/8. 8 | */ 9 | 10 | public class WebViewActivityPresenter extends BasePresenter { 11 | private WebViewModel mViewModel; 12 | 13 | public WebViewActivityPresenter(WebViewModel viewModel) { 14 | mViewModel = viewModel; 15 | } 16 | 17 | @Override 18 | public void onPresenterCreate(boolean isNewCreate) { 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/core/BaseView.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.core; 2 | 3 | import android.databinding.BaseObservable; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by 康颢曦 on 2016/11/27. 11 | */ 12 | 13 | public interface BaseView { 14 | 15 | void initView(@Nullable Bundle savedInstanceState); 16 | 17 | void initListeners(); 18 | 19 | void initOldData(@Nullable BaseObservable baseObservable); 20 | 21 | void initData(); 22 | 23 | 24 | 25 | 26 | BaseObservable getViewModel(); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/presenter/MainActivityPresenter.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.presenter; 2 | 3 | import com.fall.gank.core.BasePresenter; 4 | import com.fall.gank.viewmodel.MainViewModel; 5 | 6 | 7 | /** 8 | * Created by qqq34 on 2016/11/24. 9 | */ 10 | 11 | public class MainActivityPresenter extends BasePresenter { 12 | 13 | private MainViewModel mMainViewModel; 14 | 15 | public MainActivityPresenter(MainViewModel mainViewModel) { 16 | mMainViewModel = mainViewModel; 17 | } 18 | 19 | @Override 20 | public void onPresenterCreate(boolean isNewCreate) { 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_home.xml: -------------------------------------------------------------------------------- 1 | 4 | 9 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/home_card_foreground_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/Utils/Utils.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.Utils; 2 | 3 | import android.content.ClipData; 4 | import android.content.ClipboardManager; 5 | import android.content.Context; 6 | import android.widget.Toast; 7 | 8 | /** 9 | * Created by qqq34 on 2016/12/8. 10 | */ 11 | 12 | public class Utils { 13 | public static boolean copyToClipBoard(Context context, String text) { 14 | ClipData clipData = ClipData.newPlainText("copy", text); 15 | ClipboardManager manager = (ClipboardManager) context.getSystemService( 16 | Context.CLIPBOARD_SERVICE); 17 | manager.setPrimaryClip(clipData); 18 | return true; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/classification_card_foreground_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 11 | 14 | -------------------------------------------------------------------------------- /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:\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 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/network/GankService.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.network; 2 | 3 | import com.fall.gank.entity.ClassificationEntity; 4 | import com.fall.gank.entity.HomeEntity; 5 | 6 | import retrofit2.http.GET; 7 | import retrofit2.http.Path; 8 | import rx.Observable; 9 | 10 | /** 11 | * Created by qqq34 on 2016/11/25. 12 | */ 13 | 14 | public abstract interface GankService 15 | { 16 | @GET("day/{year}/{mouth}/{day}") 17 | Observable getHomeData(@Path("year") int year, @Path("mouth") int mouth,@Path("day") int day); 18 | 19 | @GET("data/{section}/{count}/{page}") 20 | Observable getClassifiData(@Path("section") String section, @Path("count") int count,@Path("page") int page); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /app/keep_in_main_dex.txt: -------------------------------------------------------------------------------- 1 | # you can copy the tinker keep rule at 2 | # build/intermediates/tinker_intermediates/tinker_multidexkeep.pro 3 | 4 | -keep class com.tencent.tinker.loader.** { 5 | *; 6 | } 7 | 8 | -keep class com.fall.gank.Application { 9 | *; 10 | } 11 | 12 | -keep public class * implements com.tencent.tinker.loader.app.ApplicationLifeCycle { 13 | *; 14 | } 15 | 16 | -keep public class * extends com.tencent.tinker.loader.TinkerLoader { 17 | *; 18 | } 19 | 20 | -keep public class * extends com.tencent.tinker.loader.app.TinkerApplication { 21 | *; 22 | } 23 | 24 | # here, it is your own keep rules. 25 | # you must be careful that the class name you write won't be proguard 26 | # but the tinker class above is OK, we have already keep for you! 27 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #455a64 4 | #607d8b 5 | #00bcd4 6 | #cfd8dc 7 | #bcbcbc 8 | #757575 9 | #ffffff 10 | #666666 11 | #30000000 12 | #30000000 13 | 14 | #D34F4F 15 | 16 | #303030 17 | 18 | #00000000 19 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #00BCD4 4 | #0097A7 5 | #607D8B 6 | #B2EBF2 7 | #212121 8 | #757575 9 | #ffffff 10 | #BDBDBD 11 | #30000000 12 | #20000000 13 | #ED5656 14 | #fafafa 15 | 16 | #00000000 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/viewmodel/SettingViewModel.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.viewmodel; 2 | 3 | import android.databinding.BaseObservable; 4 | import android.databinding.Bindable; 5 | import android.databinding.ObservableBoolean; 6 | 7 | import com.fall.gank.BR; 8 | 9 | import java.io.Serializable; 10 | 11 | /** 12 | * Created by qqq34 on 2016/11/29. 13 | */ 14 | 15 | public class SettingViewModel extends BaseObservable implements Serializable { 16 | public boolean isDarkTheme ; 17 | 18 | public SettingViewModel(boolean isDarkTheme) { 19 | this.isDarkTheme =isDarkTheme; 20 | } 21 | 22 | public boolean isDarkTheme() { 23 | return isDarkTheme; 24 | } 25 | 26 | public void setDarkTheme(boolean darkTheme) { 27 | isDarkTheme = darkTheme; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/adapter/HomeAdapterDecorator.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.adapter; 2 | 3 | import com.fall.gank.BR; 4 | import com.fall.gank.callback.HomeTitleListener; 5 | import com.fall.gank.viewmodel.HomeItemViewModel; 6 | import com.github.markzhai.recyclerview.BaseViewAdapter; 7 | import com.github.markzhai.recyclerview.BindingViewHolder; 8 | 9 | /** 10 | * Created by qqq34 on 2016/12/13. 11 | */ 12 | 13 | public abstract class HomeAdapterDecorator implements BaseViewAdapter.Decorator { 14 | @Override 15 | public void decorator(BindingViewHolder holder, int position, int viewType) { 16 | holder.getBinding().setVariable(BR.title, (HomeTitleListener) homeItemViewModel -> onHomeClick(homeItemViewModel)); 17 | } 18 | public abstract void onHomeClick(HomeItemViewModel homeItemViewModel); 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_webview.xml: -------------------------------------------------------------------------------- 1 | 4 | 9 | 14 | 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/view/widget/NoScrollViewPager.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.view.widget; 2 | 3 | import android.content.Context; 4 | import android.support.v4.view.ViewPager; 5 | import android.util.AttributeSet; 6 | import android.view.MotionEvent; 7 | 8 | /** 9 | * Created by qqq34 on 2016/11/29. 10 | */ 11 | 12 | public class NoScrollViewPager extends ViewPager { 13 | public NoScrollViewPager(Context context) { 14 | super(context); 15 | } 16 | 17 | public NoScrollViewPager(Context context, AttributeSet attrs) { 18 | super(context, attrs); 19 | } 20 | 21 | @Override 22 | public boolean onTouchEvent(MotionEvent arg0) { 23 | return false; 24 | } 25 | 26 | @Override 27 | public boolean onInterceptTouchEvent(MotionEvent arg0) { 28 | return false; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ###APP介绍 2 | - 一款精致的gankio客户端,日夜主题,收藏,MVVM,数据恢复等,用到了时下热门的框架:[热更新框架Tinker](https://github.com/Tencent/tinker),[Rxjava](https://github.com/ReactiveX/RxJava),[RxAndorid](https://github.com/ReactiveX/RxAndroid),[图片框架fresco](https://github.com/facebook/fresco),[缓存框架](https://github.com/anupcowkur/Reservoir),[数据库框架](https://github.com/satyan/sugar) 3 | ,还用到了自己的一款开源权限管理工具[RxPermission](https://github.com/348476129/RxPermission)。 4 | 5 | ###框架介绍 6 | - MVVM 其中用了presenter 做一个中间的管理 严格的来说应该叫 MVPVM。 7 | 8 | ###下载地址 9 | - [gank.io](https://github.com/348476129/gank.io-with-MVVM/blob/master/gank.apk) 10 | 11 | ###预览 12 | ![gank/io](https://github.com/348476129/gank.io-with-MVVM/blob/master/meitu_0.jpg) 13 | 14 | 15 | ![gank/io](https://github.com/348476129/gank.io-with-MVVM/blob/master/meitu_1.jpg) 16 | 17 | 18 | ###感谢 19 | - [干货集中营](http://gank.io/)提供接口 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/presenter/TestPresenter.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.presenter; 2 | 3 | import android.databinding.BaseObservable; 4 | 5 | import com.fall.gank.core.BasePresenter; 6 | import com.fall.gank.viewmodel.ITestModel; 7 | 8 | import rx.Observable; 9 | 10 | /** 11 | * Created by qqq34 on 2017/1/18. 12 | * 由于此项目比较简单,并未用到多presenter的情况 所以我们在这里写一个 简单的例子 13 | * 14 | */ 15 | 16 | public class TestPresenter extends BasePresenter{ 17 | 18 | private ITestModel mITestModel; 19 | 20 | public TestPresenter(ITestModel ITestModel) { 21 | mITestModel = ITestModel; 22 | } 23 | 24 | @Override 25 | public void onPresenterCreate(boolean isNewCreate) { 26 | 27 | } 28 | 29 | public void onTextClick(){ 30 | //doSomeThing 共同的业务逻辑 31 | 32 | mITestModel.changeTitle("多presenter测试"); 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/core/IPresenter.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.core; 2 | 3 | import android.content.Intent; 4 | import android.databinding.BaseObservable; 5 | 6 | import com.fall.gank.callback.BaseActivityCallback; 7 | import com.fall.gank.callback.BaseListCallback; 8 | 9 | import java.util.List; 10 | 11 | import rx.Observable; 12 | 13 | 14 | /** 15 | * Created by qqq34 on 2016/11/24. 16 | */ 17 | 18 | public interface IPresenter { 19 | void onPresenterCreate(boolean isNewCreate); 20 | void attach(); 21 | void detach(); 22 | void showSnakbar(String s); 23 | void startActivity(Intent intent); 24 | void setCallback(BaseActivityCallback baseActivityCallback); 25 | void setListCallback(BaseListCallback baseListCallback); 26 | void showList(List list); 27 | Observable checkPermission(int resString, String... mPerms); 28 | } 29 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/fall/gank/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.fall.gank", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/adapter/ViewPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.adapter; 2 | 3 | import android.support.v4.app.Fragment; 4 | import android.support.v4.app.FragmentManager; 5 | import android.support.v4.app.FragmentPagerAdapter; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by 康颢曦 on 2016/11/27. 11 | */ 12 | 13 | public class ViewPagerAdapter extends FragmentPagerAdapter { 14 | private List fragmentList; 15 | 16 | public void setFragmentList(List fragmentList) { 17 | this.fragmentList = fragmentList; 18 | } 19 | 20 | public ViewPagerAdapter(FragmentManager fm) { 21 | super(fm); 22 | } 23 | 24 | @Override 25 | public Fragment getItem(int position) { 26 | return fragmentList.get(position); 27 | } 28 | 29 | @Override 30 | public int getCount() { 31 | return fragmentList.size(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/core/BaseListFragment.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.core; 2 | 3 | import android.util.Log; 4 | 5 | import com.fall.gank.callback.BaseListCallback; 6 | import com.github.markzhai.recyclerview.SingleTypeAdapter; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Created by qqq34 on 2016/12/1. 12 | */ 13 | 14 | public abstract class BaseListFragment extends BaseFragment implements BaseListCallback { 15 | 16 | @Override 17 | public void initListeners() { 18 | if (iPresenterList!=null&&iPresenterList.size()>0){ 19 | for (IPresenter iPresenter:iPresenterList){ 20 | if (iPresenter!=null){ 21 | iPresenter.setListCallback(this); 22 | } 23 | } 24 | } 25 | } 26 | 27 | public abstract SingleTypeAdapter getAdapter(); 28 | @Override 29 | public void onListLoadFinished(List list) { 30 | getAdapter().set(list); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/viewmodel/FuliViewModel.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.viewmodel; 2 | 3 | import android.databinding.BaseObservable; 4 | import android.databinding.Bindable; 5 | import android.databinding.ObservableBoolean; 6 | 7 | import com.fall.gank.BR; 8 | 9 | import java.io.Serializable; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | /** 14 | * Created by 康颢曦 on 2016/11/27. 15 | */ 16 | 17 | public class FuliViewModel extends BaseListViewModel implements Serializable { 18 | private int lastOffset; 19 | private int position ; 20 | 21 | public int getPosition() { 22 | return position; 23 | } 24 | 25 | public void setPosition(int position) { 26 | this.position = position; 27 | } 28 | 29 | public int getLastOffset() { 30 | return lastOffset; 31 | } 32 | 33 | public void setLastOffset(int lastOffset) { 34 | this.lastOffset = lastOffset; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_photo.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/Utils/TimeUtils.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.Utils; 2 | 3 | import java.text.ParseException; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Calendar; 6 | import java.util.HashMap; 7 | 8 | /** 9 | * Created by qqq34 on 2016/12/5. 10 | */ 11 | 12 | public class TimeUtils { 13 | public static final String YEAR = "TimeUtils.YEAR"; 14 | public static final String MONTH = "TimeUtils.MONTH"; 15 | public static final String DAY = "TimeUtils.DAY"; 16 | 17 | public static HashMap getTime(String s) throws ParseException { 18 | HashMap hashMap = new HashMap<>(); 19 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); 20 | Calendar calendar = Calendar.getInstance(); 21 | calendar.setTime(sdf.parse(s)); 22 | hashMap.put(YEAR, calendar.get(Calendar.YEAR) + ""); 23 | hashMap.put(MONTH, calendar.get(Calendar.MONTH) + 1 + ""); 24 | hashMap.put(DAY, calendar.get(Calendar.DAY_OF_MONTH) + ""); 25 | 26 | return hashMap; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/viewmodel/MainViewModel.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.viewmodel; 2 | 3 | import android.databinding.BaseObservable; 4 | import android.databinding.Bindable; 5 | import android.databinding.ObservableField; 6 | import android.databinding.ObservableInt; 7 | 8 | import com.fall.gank.BR; 9 | 10 | import java.io.Serializable; 11 | 12 | 13 | /** 14 | * Created by qqq34 on 2016/11/25. 15 | */ 16 | 17 | public class MainViewModel extends BaseObservable implements Serializable, ITestModel { 18 | private int currentSelecte = 0; 19 | 20 | public ObservableField title = new ObservableField<>("Gank.io"); 21 | 22 | public MainViewModel(int currentSelecte) { 23 | this.currentSelecte = currentSelecte; 24 | } 25 | 26 | @Bindable 27 | public int getCurrentSelecte() { 28 | return currentSelecte; 29 | } 30 | 31 | public void setCurrentSelecte(int currentSelecte) { 32 | this.currentSelecte = currentSelecte; 33 | notifyPropertyChanged(BR.currentSelecte); 34 | } 35 | 36 | @Override 37 | public void changeTitle(String s) { 38 | title.set(s); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/viewmodel/BaseListViewModel.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.viewmodel; 2 | 3 | import android.databinding.BaseObservable; 4 | import android.databinding.ObservableBoolean; 5 | 6 | import java.io.Serializable; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * Created by qqq34 on 2017/1/18. 12 | */ 13 | 14 | 15 | 16 | 17 | public abstract class BaseListViewModel extends BaseObservable implements Serializable { 18 | public final ObservableBoolean isRefresh = new ObservableBoolean(false); 19 | public final ObservableBoolean isDataEnable = new ObservableBoolean(false); 20 | public int page = 1; 21 | private List mIVMs; 22 | 23 | 24 | public int getPage() { 25 | return page; 26 | } 27 | 28 | public void setPage(int page) { 29 | this.page = page; 30 | } 31 | 32 | public BaseListViewModel() { 33 | mIVMs = new ArrayList<>(); 34 | } 35 | 36 | 37 | public List getIVMs() { 38 | return mIVMs; 39 | } 40 | 41 | public void setIVMs(List IVMs) { 42 | mIVMs = IVMs; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/viewmodel/ClassificationViewModel.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.viewmodel; 2 | 3 | import android.databinding.BaseObservable; 4 | import android.databinding.Bindable; 5 | import android.databinding.ObservableBoolean; 6 | import android.databinding.ObservableInt; 7 | 8 | import com.fall.gank.BR; 9 | 10 | import java.io.Serializable; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * Created by 康颢曦 on 2016/11/27. 16 | */ 17 | 18 | public class ClassificationViewModel extends BaseListViewModel implements Serializable{ 19 | private int lastOffset; 20 | private int position ; 21 | private String type; 22 | 23 | public String getType() { 24 | return type; 25 | } 26 | 27 | public void setType(String type) { 28 | this.type = type; 29 | } 30 | public int getPosition() { 31 | return position; 32 | } 33 | 34 | public void setPosition(int position) { 35 | this.position = position; 36 | } 37 | 38 | public int getLastOffset() { 39 | return lastOffset; 40 | } 41 | 42 | public void setLastOffset(int lastOffset) { 43 | this.lastOffset = lastOffset; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 13 | 14 | 20 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 15 | 21 | 24 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/viewmodel/ClassificationItemViewModel.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.viewmodel; 2 | 3 | import android.databinding.BaseObservable; 4 | import android.databinding.ObservableBoolean; 5 | import android.databinding.ObservableField; 6 | 7 | import java.io.Serializable; 8 | 9 | import rx.Observable; 10 | 11 | /** 12 | * Created by qqq34 on 2016/12/2. 13 | */ 14 | 15 | public class ClassificationItemViewModel extends BaseObservable implements Serializable { 16 | public final ObservableField classificationName = new ObservableField<>(); 17 | public final ObservableField dimension = new ObservableField<>(); 18 | public final ObservableField year = new ObservableField<>(); 19 | public final ObservableField monthAndDay = new ObservableField<>(); 20 | public final ObservableBoolean isLike = new ObservableBoolean(false); 21 | public final ObservableField url = new ObservableField<>(); 22 | 23 | public ClassificationItemViewModel(String classificationName, String dimension, String year, String monthAndDay, boolean isLike,String url) { 24 | this.classificationName.set(classificationName); 25 | this.dimension.set(dimension); 26 | this.year.set(year); 27 | this.monthAndDay.set(monthAndDay); 28 | this.isLike.set(isLike); 29 | this.url.set(url); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 12 | 15 | 18 | 19 | 20 | 26 | 27 | 33 | 34 | -------------------------------------------------------------------------------- /app/tinkerpatch.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'tinkerpatch-support' 2 | 3 | def bakPath = file("bakApk/") 4 | 5 | def appName = "app-1.0.0-1227-15-14-15" 6 | 7 | /** 8 | * 对于插件各参数的详细解析请参考 9 | * http://tinkerpatch.com/Docs/SDK 10 | */ 11 | tinkerpatchSupport { 12 | tinkerEnable = true 13 | appKey = "489ea6ba17a0d002" 14 | appVersion = "1.0.0" 15 | 16 | autoBackupApkPath = "${bakPath}" 17 | 18 | baseApkFile = "${bakPath}/${appName}/app-release.apk" 19 | baseProguardMappingFile = "" 20 | baseResourceRFile = "${bakPath}/${appName}/app-release-R.txt" 21 | } 22 | 23 | /** 24 | * 一般来说,我们无需对下面的参数做任何的修改 25 | * 对于各参数的详细介绍请参考: 26 | * https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97 27 | */ 28 | tinkerPatch { 29 | ignoreWarning = false 30 | useSign = true 31 | dex { 32 | dexMode = "jar" 33 | pattern = ["classes*.dex"] 34 | loader = [] 35 | } 36 | lib { 37 | pattern = ["lib/*/*.so"] 38 | } 39 | 40 | res { 41 | pattern = ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"] 42 | ignoreChange = [] 43 | largeModSize = 100 44 | } 45 | 46 | packageConfig { 47 | } 48 | sevenZip { 49 | zipArtifact = "com.tencent.mm:SevenZip:1.1.10" 50 | // path = "/usr/local/bin/7za" 51 | } 52 | buildConfig { 53 | keepDexApply = false 54 | } 55 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/presenter/SettingFragmentPresenter.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.presenter; 2 | 3 | import android.database.sqlite.SQLiteException; 4 | 5 | import com.anupcowkur.reservoir.Reservoir; 6 | import com.fall.gank.Utils.RxUtils; 7 | import com.fall.gank.core.BasePresenter; 8 | import com.fall.gank.entity.SettingData; 9 | import com.fall.gank.viewmodel.SettingViewModel; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * Created by qqq34 on 2016/11/29. 15 | */ 16 | 17 | public class SettingFragmentPresenter extends BasePresenter { 18 | public static final String SETTING_KEY = "setting_key"; 19 | private SettingData mSettingData; 20 | private SettingViewModel mSettingViewModel; 21 | 22 | public SettingFragmentPresenter(SettingViewModel settingViewModel) { 23 | mSettingViewModel = settingViewModel; 24 | } 25 | 26 | @Override 27 | public void onPresenterCreate(boolean isNewCreate) { 28 | } 29 | public void updateSetting(boolean isDarkTheme){ 30 | if (mSettingData==null){ 31 | mSettingData=new SettingData(); 32 | mSettingData.setDarkTheme(isDarkTheme); 33 | }else { 34 | mSettingData.setDarkTheme(isDarkTheme); 35 | } 36 | Reservoir.putUsingObservable(SETTING_KEY,mSettingData) 37 | .subscribe(aBoolean -> { 38 | },throwable -> {}); 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_collection.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 16 | 17 | 24 | 25 | 26 | 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/network/model/impl/GankModel.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.network.model.impl; 2 | 3 | import com.fall.gank.entity.ClassificationEntity; 4 | import com.fall.gank.entity.HomeEntity; 5 | import com.fall.gank.network.HttpMethods; 6 | import com.fall.gank.network.GankService; 7 | import com.fall.gank.network.model.IGankModel; 8 | 9 | import rx.Observable; 10 | 11 | /** 12 | * Created by qqq34 on 2016/11/25. 13 | */ 14 | 15 | public class GankModel implements IGankModel 16 | { 17 | 18 | public static final int PAGE_COUNT=10; 19 | 20 | private static GankModel ourInstance; 21 | private GankService mGankService = HttpMethods.getInstance().getGankService(); 22 | 23 | public static GankModel getInstance() 24 | { 25 | if (ourInstance == null); 26 | try 27 | { 28 | if (ourInstance == null) 29 | ourInstance = new GankModel(); 30 | return ourInstance; 31 | } 32 | finally 33 | { 34 | } 35 | } 36 | 37 | 38 | @Override 39 | public Observable getHomeData(int year, int mouth, int day) { 40 | return mGankService.getHomeData(year, mouth, day); 41 | } 42 | 43 | @Override 44 | public Observable getClassifiData(String section, int page) { 45 | 46 | return mGankService.getClassifiData(section, PAGE_COUNT, page); 47 | } 48 | 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/network/HttpMethods.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.network; 2 | 3 | import com.fall.gank.network.converter.GsonConverterFactory; 4 | 5 | import java.util.concurrent.TimeUnit; 6 | 7 | import okhttp3.OkHttpClient; 8 | import retrofit2.Retrofit; 9 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; 10 | 11 | /** 12 | * Created by qqq34 on 2016/11/25. 13 | */ 14 | 15 | public class HttpMethods 16 | { 17 | public static final String BASE_URL = "https://gank.io/api/"; 18 | private static final int DEFAULT_TIMEOUT = 5; 19 | private GankService gankService; 20 | private Retrofit retrofit; 21 | 22 | private HttpMethods() 23 | { 24 | OkHttpClient.Builder localBuilder = new OkHttpClient.Builder(); 25 | localBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS); 26 | retrofit = new Retrofit.Builder().client(localBuilder.build()).addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJavaCallAdapterFactory.create()).baseUrl(BASE_URL).build(); 27 | gankService = (retrofit.create(GankService.class)); 28 | } 29 | 30 | public static HttpMethods getInstance() 31 | { 32 | return SingletonHolder.INSTANCE; 33 | } 34 | 35 | public GankService getGankService() 36 | { 37 | return gankService; 38 | } 39 | 40 | private static class SingletonHolder 41 | { 42 | private static final HttpMethods INSTANCE = new HttpMethods(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_fuli.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 12 | 13 | 16 | 19 | 20 | 21 | 27 | 28 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_classification.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 15 | 16 | 19 | 22 | 23 | 24 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/view/widget/SlowlyScrollLinearLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.view.widget; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.LinearLayoutManager; 5 | import android.support.v7.widget.LinearSmoothScroller; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.util.AttributeSet; 8 | 9 | /** 10 | * Created by qqq34 on 2016/12/1. 11 | */ 12 | 13 | public class SlowlyScrollLinearLayoutManager extends LinearLayoutManager { 14 | public SlowlyScrollLinearLayoutManager(Context context) { 15 | super(context); 16 | } 17 | 18 | public SlowlyScrollLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { 19 | super(context, orientation, reverseLayout); 20 | } 21 | 22 | public SlowlyScrollLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 23 | super(context, attrs, defStyleAttr, defStyleRes); 24 | } 25 | 26 | @Override 27 | public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) { 28 | LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()){ 29 | @Override 30 | protected int calculateTimeForScrolling(int dx) { 31 | dx=2500; 32 | return super.calculateTimeForScrolling(dx); 33 | } 34 | 35 | }; 36 | linearSmoothScroller.setTargetPosition(position); 37 | startSmoothScroll(linearSmoothScroller); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/test/java/com/fall/gank/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank; 2 | 3 | import org.junit.AfterClass; 4 | import org.junit.Assert; 5 | import org.junit.Before; 6 | import org.junit.Rule; 7 | import org.junit.Test; 8 | import org.junit.rules.TemporaryFolder; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | 13 | /** 14 | * Example local unit test, which will execute on the development machine (host). 15 | * 16 | * @see Testing documentation 17 | */ 18 | public class ExampleUnitTest { 19 | private static File testFile = null; 20 | 21 | @Rule 22 | public TemporaryFolder mFolder = new TemporaryFolder(); 23 | 24 | @Before 25 | public void before(){ 26 | System.out.println("----------method before testFile is: "+ testFile + "----------"); 27 | Assert.assertNull(testFile); 28 | } 29 | 30 | @Test 31 | public void test(){ 32 | try { 33 | testFile = mFolder.newFile("myfile.txt"); 34 | boolean flag = testFile.exists(); 35 | System.out.println("----------method test testFile exists flag: "+ flag + "----------"); 36 | Assert.assertTrue(flag); 37 | } catch (IOException e) { 38 | Assert.fail("exception is:"+e.getMessage()); 39 | } 40 | } 41 | 42 | @AfterClass 43 | public static void after(){ 44 | boolean flag = testFile.exists(); 45 | System.out.println("----------method after testFile exists flag: "+ flag + "----------"); 46 | Assert.assertFalse(flag); 47 | } 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/viewmodel/HomeItemViewModel.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.viewmodel; 2 | 3 | import android.databinding.BaseObservable; 4 | import android.databinding.Bindable; 5 | 6 | import com.fall.gank.BR; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * Created by qqq34 on 2016/11/29. 12 | */ 13 | 14 | public class HomeItemViewModel extends BaseObservable implements Serializable{ 15 | private String imageUrl; 16 | private String name; 17 | private String subTitle; 18 | private String videoUrl; 19 | 20 | public HomeItemViewModel(String imageUrl, String subTitle, String name,String videoUrl) { 21 | this.imageUrl = imageUrl; 22 | this.subTitle = subTitle; 23 | this.name = name; 24 | this.videoUrl=videoUrl; 25 | } 26 | 27 | public String getVideoUrl() { 28 | return videoUrl; 29 | } 30 | 31 | public void setVideoUrl(String videoUrl) { 32 | this.videoUrl = videoUrl; 33 | } 34 | 35 | public String getImageUrl() { 36 | return imageUrl; 37 | } 38 | 39 | public void setImageUrl(String imageUrl) { 40 | this.imageUrl = imageUrl; 41 | } 42 | @Bindable 43 | public String getName() { 44 | return name; 45 | } 46 | 47 | public void setName(String name) { 48 | this.name = name; 49 | notifyPropertyChanged(BR.name); 50 | } 51 | @Bindable 52 | public String getSubTitle() { 53 | return subTitle; 54 | } 55 | 56 | public void setSubTitle(String subTitle) { 57 | this.subTitle = subTitle; 58 | notifyPropertyChanged(BR.subTitle); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/core/AttachPresenterHelper.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.core; 2 | 3 | import android.databinding.BaseObservable; 4 | 5 | import com.fall.gank.callback.BaseActivityCallback; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by qqq34 on 2017/1/17. 11 | */ 12 | 13 | public class AttachPresenterHelper { 14 | private List mIPresenterList; 15 | 16 | 17 | public AttachPresenterHelper(List IPresenterList) { 18 | mIPresenterList = IPresenterList; 19 | 20 | } 21 | 22 | public void initPresenter(boolean isFirstStart,BaseActivityCallback mBaseActivityCallback){ 23 | if (mIPresenterList != null && mIPresenterList.size() > 0) { 24 | for (IPresenter iPresenter :mIPresenterList) { 25 | if (iPresenter != null) { 26 | iPresenter.setCallback(mBaseActivityCallback); 27 | iPresenter.onPresenterCreate(isFirstStart); 28 | } 29 | } 30 | } 31 | } 32 | 33 | public void destroyPresenter(){ 34 | if (mIPresenterList != null && mIPresenterList.size() > 0) { 35 | for (IPresenter iPresenter : mIPresenterList) { 36 | if (iPresenter != null) { 37 | iPresenter.detach(); 38 | } 39 | } 40 | } 41 | } 42 | public void attachl() { 43 | if (mIPresenterList != null && mIPresenterList.size() > 0) { 44 | for (IPresenter iPresenter : mIPresenterList) { 45 | if (iPresenter != null ) { 46 | iPresenter.attach(); 47 | } 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/view/activity/CollectionActivity.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.view.activity; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.FragmentManager; 5 | import android.support.v4.app.FragmentTransaction; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.support.v7.widget.Toolbar; 8 | 9 | import com.fall.gank.R; 10 | import com.fall.gank.Utils.MDStatusBarCompat; 11 | import com.fall.gank.view.fragment.ClassificationFragment; 12 | 13 | import static com.fall.gank.view.fragment.ClassificationFragment.COLLECTION_TYPE; 14 | 15 | /** 16 | * Created by qqq34 on 2016/12/12. 17 | */ 18 | 19 | public class CollectionActivity extends AppCompatActivity { 20 | private ClassificationFragment mClassificationFragment; 21 | @Override 22 | public void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_collection); 25 | MDStatusBarCompat.setOrdinaryToolBar(this); 26 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 27 | toolbar.setTitle("收藏"); 28 | setSupportActionBar(toolbar); 29 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 30 | toolbar.setNavigationOnClickListener(view -> { 31 | finish(); 32 | }); 33 | FragmentManager fm = getSupportFragmentManager(); 34 | FragmentTransaction transaction = fm.beginTransaction(); 35 | if (mClassificationFragment==null){ 36 | mClassificationFragment = ClassificationFragment.newInstance(COLLECTION_TYPE); 37 | } 38 | transaction.replace(R.id.container, mClassificationFragment); 39 | transaction.commit(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/adapter/ClassificationAdapterDecorator.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.adapter; 2 | 3 | import android.util.Log; 4 | 5 | import com.fall.gank.BR; 6 | import com.fall.gank.callback.LikeClickListener; 7 | import com.fall.gank.database.Collection; 8 | import com.github.markzhai.recyclerview.BaseViewAdapter; 9 | import com.github.markzhai.recyclerview.BindingViewHolder; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * Created by qqq34 on 2016/12/8. 15 | */ 16 | 17 | public class ClassificationAdapterDecorator implements BaseViewAdapter.Decorator { 18 | @Override 19 | public void decorator(BindingViewHolder holder, int position, int viewType) { 20 | holder.getBinding().setVariable(BR.like, (LikeClickListener) classificationItemViewModel -> { 21 | 22 | if (!classificationItemViewModel.isLike.get()){ 23 | Collection collection = new Collection(classificationItemViewModel.classificationName.get(),classificationItemViewModel.url.get(),classificationItemViewModel.monthAndDay.get(),classificationItemViewModel.year.get(),classificationItemViewModel.dimension.get()); 24 | collection.save(); 25 | classificationItemViewModel.isLike.set(true) ; 26 | }else { 27 | List list = Collection.find(Collection.class,"url=?",classificationItemViewModel.url.get()); 28 | if (list!=null&&list.size()>0){ 29 | Collection collection = list.get(0); 30 | classificationItemViewModel.isLike.set(!collection.delete()); 31 | }else { 32 | classificationItemViewModel.isLike.set(false 33 | ); 34 | } 35 | 36 | } 37 | 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/database/Collection.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.database; 2 | 3 | import com.orm.SugarRecord; 4 | import com.orm.dsl.Unique; 5 | 6 | /** 7 | * Created by qqq34 on 2016/12/8. 8 | */ 9 | 10 | public class Collection extends SugarRecord { 11 | private String classificationName; 12 | private String dimension; 13 | private String year; 14 | private String monthAndDay; 15 | @Unique 16 | private String url; 17 | 18 | public Collection() { 19 | 20 | } 21 | 22 | public Collection(String classificationName, String url, String monthAndDay, String year, String dimension) { 23 | this.classificationName = classificationName; 24 | this.url = url; 25 | this.monthAndDay = monthAndDay; 26 | this.year = year; 27 | this.dimension = dimension; 28 | } 29 | 30 | public String getClassificationName() { 31 | return classificationName; 32 | } 33 | 34 | public void setClassificationName(String classificationName) { 35 | this.classificationName = classificationName; 36 | } 37 | 38 | public String getDimension() { 39 | return dimension; 40 | } 41 | 42 | public void setDimension(String dimension) { 43 | this.dimension = dimension; 44 | } 45 | 46 | public String getYear() { 47 | return year; 48 | } 49 | 50 | public void setYear(String year) { 51 | this.year = year; 52 | } 53 | 54 | public String getMonthAndDay() { 55 | return monthAndDay; 56 | } 57 | 58 | public void setMonthAndDay(String monthAndDay) { 59 | this.monthAndDay = monthAndDay; 60 | } 61 | 62 | public String getUrl() { 63 | return url; 64 | } 65 | 66 | public void setUrl(String url) { 67 | this.url = url; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/network/converter/GsonResponseBodyConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.fall.gank.network.converter; 17 | 18 | import android.util.Log; 19 | 20 | 21 | import com.fall.gank.entity.BaseEntity; 22 | import com.google.gson.Gson; 23 | import com.google.gson.TypeAdapter; 24 | 25 | import java.io.IOException; 26 | 27 | import okhttp3.ResponseBody; 28 | import retrofit2.Converter; 29 | 30 | 31 | final class GsonResponseBodyConverter implements Converter { 32 | private final Gson gson; 33 | private final TypeAdapter adapter; 34 | 35 | GsonResponseBodyConverter(Gson gson, TypeAdapter adapter) { 36 | this.gson = gson; 37 | this.adapter = adapter; 38 | } 39 | @Override 40 | public T convert(ResponseBody value) throws IOException { 41 | String body = value.string(); 42 | Log.d("GSON",body); 43 | BaseEntity baseEntity= gson.fromJson(body,BaseEntity.class); 44 | try { 45 | if (!baseEntity.isError()){ 46 | return adapter.fromJson(body); 47 | }else { 48 | throw new ResultException("1", "数据加载失败"); 49 | } 50 | } finally { 51 | value.close(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/network/converter/GsonRequestBodyConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.fall.gank.network.converter; 17 | 18 | import com.google.gson.Gson; 19 | import com.google.gson.TypeAdapter; 20 | import com.google.gson.stream.JsonWriter; 21 | 22 | import java.io.IOException; 23 | import java.io.OutputStreamWriter; 24 | import java.io.Writer; 25 | import java.nio.charset.Charset; 26 | 27 | import okhttp3.MediaType; 28 | import okhttp3.RequestBody; 29 | import okio.Buffer; 30 | import retrofit2.Converter; 31 | 32 | 33 | final class GsonRequestBodyConverter implements Converter { 34 | private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8"); 35 | private static final Charset UTF_8 = Charset.forName("UTF-8"); 36 | 37 | private final Gson gson; 38 | private final TypeAdapter adapter; 39 | 40 | GsonRequestBodyConverter(Gson gson, TypeAdapter adapter) { 41 | this.gson = gson; 42 | this.adapter = adapter; 43 | } 44 | @Override 45 | public RequestBody convert(T value) throws IOException { 46 | Buffer buffer = new Buffer(); 47 | Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8); 48 | JsonWriter jsonWriter = gson.newJsonWriter(writer); 49 | adapter.write(jsonWriter, value); 50 | jsonWriter.close(); 51 | return RequestBody.create(MEDIA_TYPE, buffer.readByteString()); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 21 | 24 | 27 | 30 | 31 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 43 | 44 | 47 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/res/layout/webview_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 19 | 20 | 24 | 25 | 32 | 33 | 34 | 40 | 50 | 51 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/presenter/FuliFragmentPresenter.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.presenter; 2 | 3 | import com.anupcowkur.reservoir.Reservoir; 4 | import com.fall.gank.Utils.RxUtils; 5 | import com.fall.gank.core.BaseListPresenter; 6 | import com.fall.gank.network.model.IGankModel; 7 | import com.fall.gank.network.model.impl.GankModel; 8 | import com.fall.gank.viewmodel.ClassificationItemViewModel; 9 | import com.fall.gank.viewmodel.FuliViewModel; 10 | import com.fall.gank.viewmodel.HomeItemViewModel; 11 | import com.fall.gank.viewmodel.ImageItemViewModel; 12 | import com.google.gson.reflect.TypeToken; 13 | 14 | import java.lang.reflect.Type; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | import rx.Observable; 19 | 20 | /** 21 | * Created by 康颢曦 on 2016/11/27. 22 | */ 23 | 24 | public class FuliFragmentPresenter extends BaseListPresenter { 25 | private String KEY = "FuliFragmentPresenter.Key"; 26 | private IGankModel mModel = GankModel.getInstance(); 27 | private List mImageItemViewModels = new ArrayList<>(); 28 | 29 | public FuliFragmentPresenter(FuliViewModel listViewModel) { 30 | super(listViewModel); 31 | } 32 | 33 | @Override 34 | public void onPresenterCreate(boolean isNewCreate) { 35 | 36 | 37 | Type collectionType = new TypeToken>() { 38 | }.getType(); 39 | loadLocalData(Reservoir.getUsingObservable(KEY, ImageItemViewModel.class, collectionType), isNewCreate); 40 | } 41 | 42 | @Override 43 | public void getData(int page) { 44 | super.getData(page); 45 | mCompositeSubscription.add(mModel.getClassifiData("福利", page) 46 | .compose(RxUtils.applyIOToMainThreadSchedulers()) 47 | .map(classificationEntity -> classificationEntity.getResults()) 48 | .flatMap(Observable::from) 49 | .subscribe(classificationResultsEntity -> { 50 | mImageItemViewModels.add(new ImageItemViewModel(classificationResultsEntity.getUrl())); 51 | }, throwable -> { 52 | listViewModel.isRefresh.set(false); 53 | loadError(throwable); 54 | } 55 | , () -> { 56 | 57 | loadDataComplete(mImageItemViewModels); 58 | })); 59 | } 60 | 61 | @Override 62 | protected String getKey() { 63 | return KEY; 64 | } 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/core/BasePresenter.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.core; 2 | 3 | import android.content.Intent; 4 | import android.databinding.BaseObservable; 5 | import android.util.Log; 6 | 7 | 8 | import com.fall.gank.callback.BaseActivityCallback; 9 | import com.fall.gank.callback.BaseListCallback; 10 | import com.fall.gank.network.converter.ResultException; 11 | 12 | import java.util.List; 13 | 14 | import rx.Observable; 15 | import rx.subscriptions.CompositeSubscription; 16 | 17 | /** 18 | * Created by qqq34 on 2016/11/24. 19 | */ 20 | 21 | public abstract class BasePresenter implements IPresenter { 22 | private BaseActivityCallback callback; 23 | private BaseListCallback mBaseListCallback; 24 | public CompositeSubscription mCompositeSubscription; 25 | 26 | @Override 27 | public void setCallback(BaseActivityCallback baseActivityCallback) { 28 | callback = baseActivityCallback; 29 | } 30 | 31 | 32 | 33 | @Override 34 | public void attach() { 35 | mCompositeSubscription = new CompositeSubscription(); 36 | } 37 | 38 | @Override 39 | public void detach() { 40 | mCompositeSubscription.unsubscribe(); 41 | mCompositeSubscription = null; 42 | callback = null; 43 | mBaseListCallback = null; 44 | Log.d("tag", "on detach executed"); 45 | } 46 | 47 | 48 | 49 | @Override 50 | public void showSnakbar(String s) { 51 | if (callback != null) { 52 | callback.onShowSnackBar(s); 53 | } 54 | } 55 | 56 | @Override 57 | public void startActivity(Intent intent) { 58 | if (callback != null) { 59 | callback.onStartActivity(intent); 60 | } 61 | } 62 | 63 | @Override 64 | public void showList(List list) { 65 | if (mBaseListCallback != null) { 66 | mBaseListCallback.onListLoadFinished(list); 67 | } 68 | } 69 | 70 | @Override 71 | public void setListCallback(BaseListCallback baseListCallback) { 72 | mBaseListCallback = baseListCallback; 73 | } 74 | 75 | @Override 76 | public Observable checkPermission(int resString, String... mPerms) { 77 | if (callback != null) { 78 | return callback.checkPermission(resString, mPerms); 79 | } 80 | return null; 81 | } 82 | 83 | public void loadError(Throwable throwable) { 84 | if (throwable instanceof ResultException) { 85 | showSnakbar("数据错误"); 86 | } else { 87 | showSnakbar("连接失败,请重试"); 88 | } 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/view/widget/RecyclerviewScrollHelper.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.view.widget; 2 | 3 | import android.support.v7.widget.LinearLayoutManager; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.View; 6 | 7 | import com.fall.gank.callback.OnScrollLastListener; 8 | 9 | /** 10 | * Created by qqq34 on 2016/12/1. 11 | */ 12 | 13 | public class RecyclerviewScrollHelper { 14 | 15 | private OnScrollLastListener mOnScrollLastListener; 16 | 17 | private RecyclerView mRecyclerView; 18 | 19 | public RecyclerviewScrollHelper(RecyclerView recyclerView) { 20 | mRecyclerView = recyclerView; 21 | recyclerView.addOnScrollListener(new MyOnScrollListener()); 22 | } 23 | 24 | public class MyOnScrollListener extends RecyclerView.OnScrollListener { 25 | @Override 26 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 27 | LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager(); 28 | if (newState == RecyclerView.SCROLL_STATE_IDLE) { 29 | View leftView = manager.getChildAt(0); 30 | if (leftView != null) { 31 | int offset = leftView.getLeft(); 32 | int position = manager.findFirstVisibleItemPosition(); 33 | int lastVisibleItem = manager.findLastCompletelyVisibleItemPosition(); 34 | int width = leftView.getWidth(); 35 | 36 | if (offset == 0) { 37 | if (lastVisibleItem == manager.getItemCount() - 1) { 38 | if (mOnScrollLastListener != null) { 39 | mOnScrollLastListener.onScrollLast(); 40 | } 41 | } 42 | return; 43 | } 44 | if (Math.abs(offset) > width / 2) { 45 | recyclerView.smoothScrollToPosition(position + 1); 46 | } else if (Math.abs(offset) < width / 2) { 47 | recyclerView.smoothScrollToPosition(position); 48 | } 49 | } 50 | 51 | 52 | } 53 | } 54 | 55 | @Override 56 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 57 | super.onScrolled(recyclerView, dx, dy); 58 | } 59 | } 60 | 61 | public void setOnScrollLastListener(OnScrollLastListener onScrollLastListener) { 62 | mOnScrollLastListener = onScrollLastListener; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/network/converter/GsonConverterFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.fall.gank.network.converter; 17 | 18 | import com.google.gson.Gson; 19 | import com.google.gson.TypeAdapter; 20 | import com.google.gson.reflect.TypeToken; 21 | 22 | import java.lang.annotation.Annotation; 23 | import java.lang.reflect.Type; 24 | 25 | import okhttp3.RequestBody; 26 | import okhttp3.ResponseBody; 27 | import retrofit2.Converter; 28 | import retrofit2.Retrofit; 29 | 30 | 31 | public final class GsonConverterFactory extends Converter.Factory { 32 | /** 33 | * Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and 34 | * decoding from JSON (when no charset is specified by a header) will use UTF-8. 35 | */ 36 | public static GsonConverterFactory create() { 37 | return create(new Gson()); 38 | } 39 | 40 | /** 41 | * Create an instance using {@code gson} for conversion. Encoding to JSON and 42 | * decoding from JSON (when no charset is specified by a header) will use UTF-8. 43 | */ 44 | public static GsonConverterFactory create(Gson gson) { 45 | return new GsonConverterFactory(gson); 46 | } 47 | 48 | private final Gson gson; 49 | 50 | private GsonConverterFactory(Gson gson) { 51 | if (gson == null) throw new NullPointerException("gson == null"); 52 | this.gson = gson; 53 | } 54 | 55 | @Override 56 | public Converter responseBodyConverter(Type type, Annotation[] annotations, 57 | Retrofit retrofit) { 58 | TypeAdapter adapter = gson.getAdapter(TypeToken.get(type)); 59 | return new GsonResponseBodyConverter<>(gson, adapter); 60 | } 61 | 62 | @Override 63 | public Converter requestBodyConverter(Type type, 64 | Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { 65 | TypeAdapter adapter = gson.getAdapter(TypeToken.get(type)); 66 | return new GsonRequestBodyConverter<>(gson, adapter); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 13 | 14 | 22 | 28 | 35 | 36 | 40 | 45 | 50 | 58 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/MyApplicationLike.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.support.multidex.MultiDex; 7 | import android.support.v7.app.AppCompatDelegate; 8 | 9 | import com.anupcowkur.reservoir.Reservoir; 10 | import com.facebook.drawee.backends.pipeline.Fresco; 11 | import com.fall.gank.entity.SettingData; 12 | import com.fall.gank.presenter.SettingFragmentPresenter; 13 | import com.orm.SugarContext; 14 | import com.tencent.tinker.anno.DefaultLifeCycle; 15 | import com.tencent.tinker.loader.app.ApplicationLike; 16 | import com.tencent.tinker.loader.shareutil.ShareConstants; 17 | import com.tinkerpatch.sdk.TinkerPatch; 18 | 19 | /** 20 | * Created by qqq34 on 2016/12/1. 21 | */ 22 | @DefaultLifeCycle( 23 | application = "com.fall.gank.Application", 24 | flags = ShareConstants.TINKER_ENABLE_ALL 25 | ) 26 | public class MyApplicationLike extends ApplicationLike { 27 | private static int count = 0; 28 | 29 | public MyApplicationLike(Application application, int i, boolean b, long l, long l1, Intent intent) { 30 | super(application, i, b, l, l1, intent); 31 | } 32 | 33 | 34 | @Override 35 | public void onBaseContextAttached(Context base) { 36 | super.onBaseContextAttached(base); 37 | MultiDex.install(base); 38 | TinkerPatch.init(this) 39 | .reflectPatchLibrary() 40 | .setPatchRollbackOnScreenOff(true) 41 | .setPatchRestartOnSrceenOff(true) 42 | .setFetchPatchIntervalByHours(3) 43 | .fetchPatchUpdate(false); 44 | } 45 | 46 | @Override 47 | public void onTerminate() { 48 | super.onTerminate(); 49 | SugarContext.terminate(); 50 | } 51 | 52 | @Override 53 | public void onCreate() { 54 | super.onCreate(); 55 | SugarContext.init(getApplication()); 56 | // AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); 57 | Fresco.initialize(getApplication()); 58 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); 59 | try { 60 | Reservoir.init(getApplication(), 20480); //in bytes 61 | SettingData settingData = Reservoir.get(SettingFragmentPresenter.SETTING_KEY,SettingData.class); 62 | if (settingData!=null) { 63 | if (settingData.isDarkTheme()) { 64 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); 65 | } else { 66 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); 67 | } 68 | } 69 | } catch (Exception e) { 70 | //failure 71 | } 72 | 73 | 74 | 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/Utils/ListLoadNextHelper.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.Utils; 2 | 3 | import android.support.v7.widget.LinearLayoutManager; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.util.Log; 6 | import android.view.View; 7 | 8 | /** 9 | * Created by qqq34 on 2017/1/17. 10 | */ 11 | 12 | public class ListLoadNextHelper { 13 | private RecyclerView mRecyclerView; 14 | 15 | private ListOffsetListener mListOffsetListener; 16 | private ScrollLastListener mScrollLastListener; 17 | 18 | public interface ScrollLastListener{ 19 | void onScrollLast(); 20 | } 21 | 22 | public interface ListOffsetListener { 23 | void onOffset(int lastOffset,int position); 24 | } 25 | 26 | public ListLoadNextHelper(RecyclerView recyclerView) { 27 | mRecyclerView = recyclerView; 28 | mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { 29 | boolean isSlidingToLast = false; 30 | @Override 31 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 32 | LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager(); 33 | if (newState == RecyclerView.SCROLL_STATE_IDLE) { 34 | View topView = manager.getChildAt(0); 35 | if (topView != null) { 36 | int lastOffset = topView.getTop(); 37 | int position = manager.getPosition(topView); 38 | 39 | if (mListOffsetListener!=null){ 40 | mListOffsetListener.onOffset(lastOffset,position); 41 | } 42 | 43 | 44 | int lastVisibleItem = manager.findLastCompletelyVisibleItemPosition(); 45 | if (lastVisibleItem ==( manager.getItemCount() - 1)&&isSlidingToLast) { 46 | if (mScrollLastListener!=null){ 47 | mScrollLastListener.onScrollLast(); 48 | } 49 | return; 50 | } 51 | } 52 | 53 | } 54 | } 55 | 56 | @Override 57 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 58 | super.onScrolled(recyclerView, dx, dy); 59 | if(dy> 0){ 60 | isSlidingToLast = true; 61 | }else{ 62 | isSlidingToLast = false; 63 | } 64 | } 65 | }); 66 | } 67 | 68 | 69 | public void setListOffsetListener(ListOffsetListener listOffsetListener) { 70 | mListOffsetListener = listOffsetListener; 71 | } 72 | 73 | public void setScrollLastListener(ScrollLastListener scrollLastListener) { 74 | mScrollLastListener = scrollLastListener; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Gank.io 3 | APP需要获取访问文件的权限 4 | 已收藏 5 | 收藏 6 | Settings 7 | 8 | 9 | 10 | 11 | General 12 | 13 | Enable social recommendations 14 | Recommendations for people to contact 15 | based on your message history 16 | 17 | 18 | Display name 19 | John Smith 20 | 21 | Add friends to messages 22 | 23 | Always 24 | When possible 25 | Never 26 | 27 | 28 | 1 29 | 0 30 | -1 31 | 32 | 33 | 34 | Data & sync 35 | 36 | Sync frequency 37 | 38 | 15 minutes 39 | 30 minutes 40 | 1 hour 41 | 3 hours 42 | 6 hours 43 | Never 44 | 45 | 46 | 15 47 | 30 48 | 60 49 | 180 50 | 360 51 | -1 52 | 53 | 54 | 55 | Entry 1 56 | Entry 2 57 | Entry 3 58 | 59 | 60 | 61 | 1 62 | 2 63 | 3 64 | 65 | 66 | 67 | 68 | System sync settings 69 | 70 | 71 | Notifications 72 | 73 | New message notifications 74 | 75 | Ringtone 76 | Silent 77 | 78 | Vibrate 79 | 80 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'me.tatarka.retrolambda' 3 | apply from: 'tinkerpatch.gradle' 4 | 5 | android { 6 | packagingOptions{ 7 | exclude 'META-INF/LICENSE.txt' 8 | exclude 'META-INF/NOTICE.txt' 9 | } 10 | lintOptions { 11 | abortOnError false 12 | } 13 | signingConfigs { 14 | config { 15 | keyAlias 'fall' 16 | keyPassword '612673' 17 | storeFile file('gankKey.jks') 18 | storePassword '612673' 19 | } 20 | } 21 | 22 | dataBinding { 23 | enabled true 24 | } 25 | compileSdkVersion 25 26 | buildToolsVersion "25.0.0" 27 | defaultConfig { 28 | applicationId "com.fall.gank" 29 | multiDexEnabled = true 30 | minSdkVersion 15 31 | targetSdkVersion 25 32 | versionCode 1 33 | versionName "1.0.0" 34 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 35 | vectorDrawables.useSupportLibrary = true 36 | } 37 | buildTypes { 38 | debug { 39 | signingConfig signingConfigs.config 40 | minifyEnabled false 41 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 42 | } 43 | release { 44 | signingConfig signingConfigs.config 45 | minifyEnabled false 46 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 47 | } 48 | } 49 | compileOptions { 50 | targetCompatibility 1.8 51 | sourceCompatibility 1.8 52 | } 53 | } 54 | 55 | dependencies { 56 | compile fileTree(include: ['*.jar'], dir: 'libs') 57 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 58 | exclude group: 'com.android.support', module: 'support-annotations' 59 | }) 60 | 61 | compile 'com.github.348476129:RxPermission:0.1.0' 62 | compile 'com.android.support:design:25.1.0' 63 | compile 'com.android.support:appcompat-v7:25.1.0' 64 | compile 'io.reactivex:rxandroid:1.2.1' 65 | compile 'io.reactivex:rxjava:1.2.3' 66 | compile 'com.google.code.gson:gson:2.8.0' 67 | compile 'com.squareup.retrofit2:retrofit:2.1.0' 68 | compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0' 69 | compile 'com.github.markzhai:databinding-rv-adapter:1.0.1' 70 | compile 'com.android.support:cardview-v7:25.1.0' 71 | compile 'com.facebook.fresco:fresco:0.12.0' 72 | compile 'com.anupcowkur:reservoir:3.1.0' 73 | provided("com.tencent.tinker:tinker-android-anno:1.7.7") 74 | compile("com.tinkerpatch.sdk:tinkerpatch-android-sdk:1.1.1") 75 | compile 'com.android.support:multidex:1.0.1' 76 | compile 'me.zhanghai.android.materialprogressbar:library:1.3.0' 77 | compile 'com.github.satyan:sugar:1.5' 78 | compile 'com.android.support:support-v4:25.1.0' 79 | compile 'com.android.support:support-vector-drawable:25.1.0' 80 | compile 'me.relex:photodraweeview:1.1.2' 81 | testCompile 'junit:junit:4.12' 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/Utils/RxUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * {EasyGank} Copyright (C) {2015} {CaMnter} 3 | * 4 | * This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 5 | * This is free software, and you are welcome to redistribute it 6 | * under certain conditions; type `show c' for details. 7 | * 8 | * The hypothetical commands `show w' and `show c' should show the appropriate 9 | * parts of the General Public License. Of course, your program's commands 10 | * might be different; for a GUI interface, you would use an "about box". 11 | * 12 | * You should also get your employer (if you work as a programmer) or school, 13 | * if any, to sign a "copyright disclaimer" for the program, if necessary. 14 | * For more information on this, and how to apply and follow the GNU GPL, see 15 | * . 16 | * 17 | * The GNU General Public License does not permit incorporating your program 18 | * into proprietary programs. If your program is a subroutine library, you 19 | * may consider it more useful to permit linking proprietary applications with 20 | * the library. If this is what you want to do, use the GNU Lesser General 21 | * Public License instead of this License. But first, please read 22 | * . 23 | */ 24 | 25 | package com.fall.gank.Utils; 26 | 27 | import rx.Observable; 28 | import rx.android.schedulers.AndroidSchedulers; 29 | import rx.schedulers.Schedulers; 30 | 31 | /** 32 | * Description:RxUtils 33 | * Created by:CaMnter 34 | * Time:2016-01-13 12:08 35 | */ 36 | public class RxUtils { 37 | /** 38 | * {@link Observable.Transformer} that transforms the source observable to subscribe in the 39 | * io thread and observe on the Android's UI thread. 40 | */ 41 | private static Observable.Transformer ioToMainThreadSchedulerTransformer; 42 | 43 | 44 | static { 45 | ioToMainThreadSchedulerTransformer = createIOToMainThreadScheduler(); 46 | } 47 | 48 | 49 | /** 50 | * Get {@link Observable.Transformer} that transforms the source observable to subscribe in 51 | * the io thread and observe on the Android's UI thread. 52 | *

53 | * Because it doesn't interact with the emitted items it's safe ignore the unchecked casts. 54 | * 55 | * @return {@link Observable.Transformer} 56 | */ 57 | @SuppressWarnings("unchecked") 58 | private static Observable.Transformer createIOToMainThreadScheduler() { 59 | return tObservable -> tObservable.subscribeOn(Schedulers.io()) 60 | .unsubscribeOn( 61 | Schedulers.computation()) // TODO: remove when https://github.com/square/okhttp/issues/1592 is fixed 62 | .observeOn(AndroidSchedulers.mainThread()); 63 | } 64 | 65 | 66 | @SuppressWarnings("unchecked") 67 | public static Observable.Transformer applyIOToMainThreadSchedulers() { 68 | return ioToMainThreadSchedulerTransformer; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/entity/ClassificationResultsEntity.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.entity; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by qqq34 on 2016/11/30. 7 | */ 8 | 9 | public class ClassificationResultsEntity { 10 | /** 11 | * _id : 583c0452421aa9710cf54c47 12 | * createdAt : 2016-11-28T18:17:54.556Z 13 | * desc : 六种二维码生成的样式 14 | * images : ["http://img.gank.io/2f0b6c5f-6de7-4ba3-94ad-98bf721ee447"] 15 | * publishedAt : 2016-11-29T11:38:58.378Z 16 | * source : web 17 | * type : Android 18 | * url : https://github.com/vivian8725118/ZXingDemo/ 19 | * used : true 20 | * who : Vivian 21 | */ 22 | 23 | private String _id; 24 | private String createdAt; 25 | private String desc; 26 | private String publishedAt; 27 | private String source; 28 | private String type; 29 | private String url; 30 | private boolean used; 31 | private String who; 32 | private List images; 33 | private String videoUrl; 34 | 35 | public String getVideoUrl() { 36 | return videoUrl; 37 | } 38 | 39 | public void setVideoUrl(String videoUrl) { 40 | this.videoUrl = videoUrl; 41 | } 42 | 43 | public String get_id() { 44 | return _id; 45 | } 46 | 47 | public void set_id(String _id) { 48 | this._id = _id; 49 | } 50 | 51 | public String getCreatedAt() { 52 | return createdAt; 53 | } 54 | 55 | public void setCreatedAt(String createdAt) { 56 | this.createdAt = createdAt; 57 | } 58 | 59 | public String getDesc() { 60 | return desc; 61 | } 62 | 63 | public void setDesc(String desc) { 64 | this.desc = desc; 65 | } 66 | 67 | public String getPublishedAt() { 68 | return publishedAt; 69 | } 70 | 71 | public void setPublishedAt(String publishedAt) { 72 | this.publishedAt = publishedAt; 73 | } 74 | 75 | public String getSource() { 76 | return source; 77 | } 78 | 79 | public void setSource(String source) { 80 | this.source = source; 81 | } 82 | 83 | public String getType() { 84 | return type; 85 | } 86 | 87 | public void setType(String type) { 88 | this.type = type; 89 | } 90 | 91 | public String getUrl() { 92 | return url; 93 | } 94 | 95 | public void setUrl(String url) { 96 | this.url = url; 97 | } 98 | 99 | public boolean isUsed() { 100 | return used; 101 | } 102 | 103 | public void setUsed(boolean used) { 104 | this.used = used; 105 | } 106 | 107 | public String getWho() { 108 | return who; 109 | } 110 | 111 | public void setWho(String who) { 112 | this.who = who; 113 | } 114 | 115 | public List getImages() { 116 | return images; 117 | } 118 | 119 | public void setImages(List images) { 120 | this.images = images; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/core/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.core; 2 | 3 | import android.content.Intent; 4 | import android.databinding.BaseObservable; 5 | import android.os.Bundle; 6 | import android.support.annotation.Nullable; 7 | import android.support.design.widget.Snackbar; 8 | import android.support.v7.app.AppCompatDelegate; 9 | import android.view.View; 10 | 11 | import com.example.rxpermisson.PermissionAppCompatActivity; 12 | import com.fall.gank.callback.BaseActivityCallback; 13 | 14 | import java.io.Serializable; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | 19 | /** 20 | * Created by qqq34 on 2016/11/24. 21 | */ 22 | 23 | public abstract class BaseActivity extends PermissionAppCompatActivity implements BaseActivityCallback, BaseView { 24 | public static final String KEY_VIEW_MODEL = "BaseActivity.viewmodel"; 25 | private View view; 26 | private BaseObservable baseObservable; 27 | private AttachPresenterHelper mAttachPresenterHelper; 28 | protected List iPresenterList ; //储存引用的所有presenter,一个界面可能会有多个Presenter的情况 29 | @Override 30 | protected void onCreate(@Nullable Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | iPresenterList = new ArrayList<>(); 33 | if (savedInstanceState != null) { 34 | baseObservable = (BaseObservable) savedInstanceState.get(KEY_VIEW_MODEL); 35 | } 36 | initBinding(); 37 | if (baseObservable != null) { 38 | initOldData(baseObservable); 39 | } else { 40 | initData(); 41 | } 42 | mAttachPresenterHelper = new AttachPresenterHelper(iPresenterList); 43 | attachViewModel(); 44 | initToolbar(savedInstanceState); 45 | initView(savedInstanceState); 46 | view = findViewById(android.R.id.content); 47 | initListeners(); 48 | mAttachPresenterHelper.initPresenter(baseObservable == null, this); 49 | 50 | } 51 | 52 | protected abstract void initBinding(); 53 | 54 | 55 | @Override 56 | protected void onDestroy() { 57 | mAttachPresenterHelper.destroyPresenter(); 58 | super.onDestroy(); 59 | } 60 | 61 | 62 | protected abstract void initToolbar(Bundle savedInstanceState); 63 | 64 | 65 | public void attachViewModel() { 66 | mAttachPresenterHelper.attachl(); 67 | } 68 | 69 | @Override 70 | public void onShowSnackBar(String s) { 71 | Snackbar.make(view, s, Snackbar.LENGTH_SHORT).show(); 72 | } 73 | 74 | @Override 75 | public void onStartActivity(Intent intent) { 76 | startActivity(intent); 77 | } 78 | 79 | @Override 80 | public void onSaveInstanceState(Bundle outState) { 81 | if (getViewModel() != null) { 82 | outState.putSerializable(KEY_VIEW_MODEL, (Serializable) getViewModel()); 83 | } 84 | super.onSaveInstanceState(outState); 85 | } 86 | 87 | public boolean isDarkTheme() { 88 | return AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/core/BaseListPresenter.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.core; 2 | 3 | import android.databinding.BaseObservable; 4 | import android.util.Log; 5 | 6 | import com.anupcowkur.reservoir.Reservoir; 7 | import com.fall.gank.Utils.RxUtils; 8 | import com.fall.gank.viewmodel.BaseListViewModel; 9 | 10 | import java.util.List; 11 | 12 | import rx.Observable; 13 | 14 | /** 15 | * Created by qqq34 on 2017/1/18. 16 | */ 17 | 18 | /** 19 | * @param List每一项 Item所对应的 ViewModel的类型 20 | * @param 整个List的界面所对应的ViewModel的类型,BaseListViewModel需要知道自己负责的ListItem的类型。 21 | */ 22 | 23 | public abstract class BaseListPresenter> extends BasePresenter { 24 | 25 | protected int page = 1; 26 | protected T listViewModel; 27 | 28 | public BaseListPresenter(T listViewModel) { 29 | this.listViewModel = listViewModel; 30 | } 31 | 32 | protected void loadLocalData(Observable observable, boolean isNewCreate) { 33 | if (isNewCreate) { 34 | mCompositeSubscription.add(observable.compose(RxUtils.applyIOToMainThreadSchedulers()) 35 | .toList() 36 | .subscribe(ivmList -> { 37 | if (ivmList.size() > 0) { 38 | listViewModel.setIVMs(ivmList); 39 | showList(listViewModel.getIVMs()); 40 | listViewModel.isDataEnable.set(true); 41 | } 42 | }, throwable -> { 43 | getData(page); 44 | }, () -> getData(page))); 45 | } else { 46 | page = listViewModel.getPage(); 47 | if (listViewModel.isRefresh.get()) { 48 | getData(page); 49 | } 50 | } 51 | } 52 | 53 | //获取到数据后,需要加载和储存Item的数据 54 | 55 | protected void loadDataComplete(List tempList) { 56 | if (page == 1) { 57 | listViewModel.getIVMs().clear(); 58 | } 59 | listViewModel.getIVMs().addAll(tempList); 60 | tempList.clear(); 61 | listViewModel.isRefresh.set(false); 62 | showList(listViewModel.getIVMs()); 63 | if (page == 1) { 64 | mCompositeSubscription.add(Reservoir.putUsingObservable(getKey(), listViewModel.getIVMs()) 65 | .compose(RxUtils.applyIOToMainThreadSchedulers()) 66 | .subscribe(aBoolean -> { 67 | }, throwable -> { 68 | })); 69 | } 70 | listViewModel.isDataEnable.set(true); 71 | page++; 72 | listViewModel.setPage(page); 73 | 74 | } 75 | 76 | 77 | public void getData(int page) { 78 | if (page == 1) { 79 | this.page = 1; 80 | } 81 | listViewModel.setPage(this.page); 82 | listViewModel.isRefresh.set(true); 83 | } 84 | 85 | 86 | public void loadNext() { 87 | Log.d("next"," "); 88 | if (page == 1) page++; 89 | if (!listViewModel.isRefresh.get()) { 90 | getData(page); 91 | } else return; 92 | } 93 | 94 | protected abstract String getKey(); 95 | 96 | } 97 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/Utils/BindingUtils.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.Utils; 2 | 3 | import android.databinding.BindingAdapter; 4 | import android.net.Uri; 5 | import android.support.v4.widget.SwipeRefreshLayout; 6 | import android.support.v7.widget.Toolbar; 7 | import android.view.View; 8 | import android.webkit.WebView; 9 | import android.widget.ImageView; 10 | 11 | import com.fall.gank.R; 12 | import com.fall.gank.callback.SwipeRefreshListener; 13 | import com.facebook.drawee.view.SimpleDraweeView; 14 | 15 | import me.zhanghai.android.materialprogressbar.MaterialProgressBar; 16 | 17 | /** 18 | * Created by qqq34 on 2016/11/30. 19 | */ 20 | 21 | public class BindingUtils { 22 | @BindingAdapter("webProgress") 23 | public static void setPregress(final MaterialProgressBar materialProgressBar,int progress){ 24 | materialProgressBar.setProgress(progress); 25 | if (progress==100){ 26 | materialProgressBar.setVisibility(View.GONE); 27 | }else { 28 | materialProgressBar.setVisibility(View.VISIBLE); 29 | } 30 | } 31 | @BindingAdapter("frescoImageUri") 32 | public static void showImageByUrl(final SimpleDraweeView simpleDraweeView, String url) { 33 | FrescoUtils.displayWithResize(600, 600, Uri.parse(url), simpleDraweeView); 34 | } 35 | 36 | @BindingAdapter("frescoImageUriWithSmallSize") 37 | public static void showImageByUrlWithSmallSize(final SimpleDraweeView simpleDraweeView, String url) { 38 | FrescoUtils.displayWithResize(350, 350, Uri.parse(url), simpleDraweeView); 39 | } 40 | 41 | @BindingAdapter("setRefreshing") 42 | public static void setRefreshing(final SwipeRefreshLayout swipeRefreshLayout, boolean isRefresh) { 43 | 44 | swipeRefreshLayout.post(() -> swipeRefreshLayout.setRefreshing(isRefresh)); 45 | } 46 | 47 | @BindingAdapter("onRefresh") 48 | public static void onRefresh(final SwipeRefreshLayout swipeRefreshLayout, SwipeRefreshListener swipeRefreshListener) { 49 | swipeRefreshLayout.setOnRefreshListener(swipeRefreshListener::onRefresh); 50 | } 51 | 52 | @BindingAdapter("homeTint") 53 | public static void setTint(final ImageView imageView, int current) { 54 | if (imageView.getTag().equals(current + "")) { 55 | imageView.setColorFilter(imageView.getResources().getColor(R.color.colorPrimary)); 56 | } else { 57 | imageView.setColorFilter(imageView.getResources().getColor(R.color.SecondaryText)); 58 | } 59 | 60 | } 61 | 62 | @BindingAdapter("classificationLikeTint") 63 | public static void setLikeTint(final ImageView imageView, boolean islike) { 64 | if (islike) { 65 | imageView.setColorFilter(imageView.getResources().getColor(R.color.favorite_color)); 66 | } else { 67 | imageView.setColorFilter(imageView.getResources().getColor(R.color.SecondaryText)); 68 | } 69 | 70 | } 71 | 72 | @BindingAdapter("webview_url") 73 | public static void setWebUrl(final WebView webView, String url) { 74 | webView.loadUrl(url); 75 | } 76 | 77 | @BindingAdapter("toolbar_title") 78 | public static void setTitle(final Toolbar toolbar, String s){ 79 | toolbar.setTitle(s); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/view/activity/PhotoActivity.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.view.activity; 2 | 3 | import android.Manifest; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.net.Uri; 7 | import android.os.Bundle; 8 | import android.support.design.widget.Snackbar; 9 | import android.support.v7.widget.Toolbar; 10 | import android.view.Menu; 11 | 12 | import com.example.rxpermisson.PermissionAppCompatActivity; 13 | import com.fall.gank.R; 14 | import com.fall.gank.Utils.FrescoUtils; 15 | import com.fall.gank.Utils.MDStatusBarCompat; 16 | 17 | import me.relex.photodraweeview.PhotoDraweeView; 18 | import rx.Subscription; 19 | 20 | /** 21 | * Created by qqq34 on 2016/12/12. 22 | */ 23 | 24 | public class PhotoActivity extends PermissionAppCompatActivity { 25 | private static final String EXTRA_URL = "PhotoActivity.url"; 26 | private Subscription mSubscription; 27 | 28 | @Override 29 | public void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | setContentView(R.layout.activity_photo); 32 | String url = getIntent().getStringExtra(EXTRA_URL); 33 | MDStatusBarCompat.setImageTranslucent(this); 34 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 35 | toolbar.setTitle("查看图片"); 36 | setSupportActionBar(toolbar); 37 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 38 | toolbar.setNavigationOnClickListener(view -> { 39 | finish(); 40 | }); 41 | PhotoDraweeView photoDraweeView = (PhotoDraweeView) findViewById(R.id.photo_drawee_view); 42 | photoDraweeView.setPhotoUri(Uri.parse(url)); 43 | toolbar.setOnMenuItemClickListener(item -> { 44 | switch (item.getItemId()) { 45 | case R.id.save_photo: 46 | mSubscription = checkPermission(R.string.base_permission, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE) 47 | .subscribe(aBoolean -> { 48 | if (aBoolean) { 49 | FrescoUtils.savePicture(url, PhotoActivity.this); 50 | Snackbar.make(toolbar, "保存成功!路径:" + FrescoUtils.IMAGE_PIC_CACHE_DIR, Snackbar.LENGTH_LONG).show(); 51 | }else { 52 | Snackbar.make(toolbar, "保存失败,app没有相应的权限", Snackbar.LENGTH_LONG).show(); 53 | 54 | } 55 | }, throwable -> { 56 | }); 57 | 58 | 59 | break; 60 | } 61 | return true; 62 | }); 63 | } 64 | 65 | public static void newIntent(Context context, String url) { 66 | Intent intent = new Intent(context, PhotoActivity.class); 67 | intent.putExtra(EXTRA_URL, url); 68 | context.startActivity(intent); 69 | } 70 | 71 | @Override 72 | public boolean onCreateOptionsMenu(Menu menu) { 73 | getMenuInflater().inflate(R.menu.menu_photo, menu); 74 | return true; 75 | } 76 | 77 | @Override 78 | protected void onDestroy() { 79 | super.onDestroy(); 80 | if (mSubscription != null) { 81 | mSubscription.unsubscribe(); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/view/fragment/SettingFragment.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.view.fragment; 2 | 3 | import android.content.Intent; 4 | import android.content.res.Configuration; 5 | import android.databinding.BaseObservable; 6 | import android.os.Bundle; 7 | import android.support.annotation.Nullable; 8 | import android.support.v7.app.AppCompatDelegate; 9 | import android.support.v7.widget.SwitchCompat; 10 | import android.util.Log; 11 | import android.view.LayoutInflater; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.widget.CompoundButton; 15 | 16 | import com.fall.gank.core.BaseFragment; 17 | import com.fall.gank.core.IPresenter; 18 | import com.fall.gank.databinding.FragmentSettingBinding; 19 | import com.fall.gank.presenter.SettingFragmentPresenter; 20 | import com.fall.gank.view.activity.CollectionActivity; 21 | import com.fall.gank.viewmodel.SettingViewModel; 22 | 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | 26 | /** 27 | * Created by qqq34 on 2016/11/29. 28 | */ 29 | 30 | public class SettingFragment extends BaseFragment { 31 | private FragmentSettingBinding binding; 32 | private SettingFragmentPresenter mSettingFragmentPresenter; 33 | private SettingViewModel mSettingViewModel; 34 | 35 | @Override 36 | protected View initBinding(LayoutInflater inflater, ViewGroup container) { 37 | binding = FragmentSettingBinding.inflate(inflater, container, false); 38 | return binding.getRoot(); 39 | } 40 | 41 | @Override 42 | public void initView(@Nullable Bundle savedInstanceState) { 43 | 44 | } 45 | 46 | @Override 47 | public void initListeners() { 48 | binding.switchbutton.setOnCheckedChangeListener((compoundButton, b) -> { 49 | if (b) { 50 | if (!isDarkTheme()) { 51 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); 52 | mSettingFragmentPresenter.updateSetting(true); 53 | getActivity().recreate(); 54 | } 55 | 56 | 57 | } else { 58 | if (isDarkTheme()) { 59 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); 60 | mSettingFragmentPresenter.updateSetting(false); 61 | getActivity().recreate(); 62 | } 63 | 64 | } 65 | }); 66 | binding.collectionLayout.setOnClickListener(view -> { 67 | startActivity(new Intent(getActivity(), CollectionActivity.class)); 68 | }); 69 | } 70 | 71 | @Override 72 | public void initOldData(@Nullable BaseObservable baseObservable) { 73 | mSettingViewModel = (SettingViewModel) baseObservable; 74 | mSettingViewModel.setDarkTheme(isDarkTheme()); 75 | binding.setViewModel(mSettingViewModel); 76 | mSettingFragmentPresenter = new SettingFragmentPresenter(mSettingViewModel); 77 | iPresenterList.add(mSettingFragmentPresenter); 78 | } 79 | 80 | @Override 81 | public void initData() { 82 | 83 | mSettingViewModel = new SettingViewModel(isDarkTheme()); 84 | binding.setViewModel(mSettingViewModel); 85 | mSettingFragmentPresenter = new SettingFragmentPresenter(mSettingViewModel); 86 | iPresenterList.add(mSettingFragmentPresenter); 87 | } 88 | 89 | 90 | @Override 91 | public BaseObservable getViewModel() { 92 | return mSettingViewModel; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/presenter/HomeFragmentPresenter.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.presenter; 2 | 3 | import android.Manifest; 4 | import android.util.Log; 5 | 6 | import com.anupcowkur.reservoir.Reservoir; 7 | import com.fall.gank.R; 8 | import com.fall.gank.Utils.RxUtils; 9 | import com.fall.gank.core.BaseListPresenter; 10 | import com.fall.gank.network.model.IDataManager; 11 | import com.fall.gank.network.model.impl.DataManager; 12 | import com.fall.gank.viewmodel.HomeItemViewModel; 13 | import com.fall.gank.viewmodel.HomeViewModel; 14 | import com.fall.gank.viewmodel.ImageItemViewModel; 15 | import com.google.gson.reflect.TypeToken; 16 | import com.tinkerpatch.sdk.TinkerPatch; 17 | 18 | import java.lang.reflect.Type; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | import rx.Observable; 23 | 24 | /** 25 | * Created by 康颢曦 on 2016/11/27. 26 | */ 27 | 28 | public class HomeFragmentPresenter extends BaseListPresenter { 29 | private String KEY = "HomeFragmentPresenter.homeData"; 30 | private IDataManager mManager = new DataManager(); 31 | // private SingleTypeAdapter mAdapter; 32 | private List mHomeItemViewModels = new ArrayList<>(); 33 | 34 | public HomeFragmentPresenter(HomeViewModel listViewModel) { 35 | super(listViewModel); 36 | } 37 | 38 | @Override 39 | public void onPresenterCreate(boolean isNewCreate) { 40 | if (isNewCreate) { 41 | mCompositeSubscription.add(checkPermission(R.string.base_permission, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE) 42 | .subscribe(aBoolean -> { 43 | if (aBoolean) { 44 | TinkerPatch.with().fetchPatchUpdate(true); 45 | } 46 | }, throwable -> { 47 | })); 48 | 49 | } 50 | Type collectionType = new TypeToken>() {}.getType(); 51 | loadLocalData(Reservoir.getUsingObservable(KEY, HomeItemViewModel.class, collectionType), isNewCreate); 52 | } 53 | 54 | @Override 55 | public void getData(int page) { 56 | super.getData(page); 57 | mCompositeSubscription.add(mManager.getHomeData(this.page) 58 | .compose(RxUtils.applyIOToMainThreadSchedulers()) 59 | .map(classificationEntity -> classificationEntity.getResults()) 60 | .flatMap(Observable::from) 61 | .subscribe(classificationResultsEntity -> { 62 | String[] strings = classificationResultsEntity.getDesc().split("######"); 63 | if (strings.length == 2) { 64 | mHomeItemViewModels.add(new HomeItemViewModel(classificationResultsEntity.getUrl(), strings[1], strings[0], classificationResultsEntity.getVideoUrl())); 65 | } else { 66 | mHomeItemViewModels.add(new HomeItemViewModel(classificationResultsEntity.getUrl(), classificationResultsEntity.getDesc(), "未知", classificationResultsEntity.getVideoUrl())); 67 | } 68 | 69 | }, throwable -> { 70 | listViewModel.isRefresh.set(false); 71 | loadError(throwable); 72 | }, () -> { 73 | loadDataComplete(mHomeItemViewModels); 74 | })); 75 | } 76 | 77 | @Override 78 | protected String getKey() { 79 | return KEY; 80 | } 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_home_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 13 | 14 | 17 | 18 | 21 | 22 | 23 | 32 | 33 | 43 | 44 | 48 | 49 | 56 | 57 | 64 | 65 | 72 | 73 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/entity/ClassificationEntity.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.entity; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by qqq34 on 2016/11/30. 7 | */ 8 | 9 | public class ClassificationEntity { 10 | 11 | /** 12 | * error : false 13 | * results : [{"_id":"583c0452421aa9710cf54c47","createdAt":"2016-11-28T18:17:54.556Z","desc":"六种二维码生成的样式","images":["http://img.gank.io/2f0b6c5f-6de7-4ba3-94ad-98bf721ee447"],"publishedAt":"2016-11-29T11:38:58.378Z","source":"web","type":"Android","url":"https://github.com/vivian8725118/ZXingDemo/","used":true,"who":"Vivian"},{"_id":"583c4dc6421aa9710cf54c4a","createdAt":"2016-11-28T23:31:18.761Z","desc":"Java设计模式之单例模式","publishedAt":"2016-11-29T11:38:58.378Z","source":"web","type":"Android","url":"http://www.haotianyi.win/2016/11/java%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8B%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F.html","used":true,"who":"HaoTianYi"},{"_id":"583ce4dd421aa939befafac7","createdAt":"2016-11-29T10:15:57.829Z","desc":"验证码图片 ImageView,相当实用!","images":["http://img.gank.io/940c9fd7-3c57-4152-b496-271bca9f20ae"],"publishedAt":"2016-11-29T11:38:58.378Z","source":"chrome","type":"Android","url":"https://github.com/jineshfrancs/CaptchaImageView","used":true,"who":"代码家"},{"_id":"583ce5df421aa939bb4637bc","createdAt":"2016-11-29T10:20:15.562Z","desc":"超漂亮的,支持展开菜单的 Fab 按钮。","images":["http://img.gank.io/76f6993b-d103-40e0-8c30-fb1d246e23a0"],"publishedAt":"2016-11-29T11:38:58.378Z","source":"chrome","type":"Android","url":"https://github.com/JoaquimLey/faboptions","used":true,"who":"嗲马甲"},{"_id":"583ce6d8421aa939bb4637bd","createdAt":"2016-11-29T10:24:24.521Z","desc":"类似 Google Inbox 的实现,做的不错","images":["http://img.gank.io/bc5b51d8-2974-4c87-a6ec-8ccc451aea0b"],"publishedAt":"2016-11-29T11:38:58.378Z","source":"chrome","type":"Android","url":"https://github.com/memfis19/Cadar","used":true,"who":"代码家"},{"_id":"583129bf421aa929ac960afc","createdAt":"2016-11-20T12:42:39.884Z","desc":"Android 实现视屏播放器、边播边缓存功能、外加铲屎(IJKPlayer)","images":["http://img.gank.io/8196d110-32cf-41bc-86c6-801af152a743"],"publishedAt":"2016-11-28T11:32:07.534Z","source":"chrome","type":"Android","url":"http://www.jianshu.com/p/9fe377dd9750","used":true,"who":"Jason"},{"_id":"583a2a98421aa91cb7afe7f4","createdAt":"2016-11-27T08:36:40.493Z","desc":"很赞的登录注册布局","images":["http://img.gank.io/dacc7f4c-3872-4c00-b669-3ab13b430e01"],"publishedAt":"2016-11-28T11:32:07.534Z","source":"chrome","type":"Android","url":"https://github.com/irfaan008/OnePageSigninSignup","used":true,"who":"蒋朋"},{"_id":"583b7e97421aa9711460f744","createdAt":"2016-11-28T08:47:19.286Z","desc":"清晰灵活简单易用的应用更新库","images":["http://img.gank.io/9d7deebb-3fa8-43dc-a36c-81a11044b394"],"publishedAt":"2016-11-28T11:32:07.534Z","source":"web","type":"Android","url":"https://github.com/czy1121/update","used":true,"who":"ezy"},{"_id":"583b99d1421aa9710cf54c3e","createdAt":"2016-11-28T10:43:29.756Z","desc":"目测是目前来看做 Blur 效果速度最快的库","images":["http://img.gank.io/f826f969-027d-43d6-bb00-a89684e37346"],"publishedAt":"2016-11-28T11:32:07.534Z","source":"chrome","type":"Android","url":"https://github.com/wonderkiln/blurkit-android","used":true,"who":"嗲马甲"},{"_id":"5836a7fc421aa91cb7afe7e0","createdAt":"2016-11-24T16:42:36.919Z","desc":"支持https的ijkplayer播放器","images":["http://img.gank.io/22aa7a50-de1f-4697-8eb8-7bcc247cce58"],"publishedAt":"2016-11-25T11:29:49.832Z","source":"web","type":"Android","url":"https://github.com/l123456789jy/ijkplayer","used":true,"who":"Lazy"}] 14 | */ 15 | 16 | private List results; 17 | 18 | public List getResults() { 19 | return results; 20 | } 21 | 22 | public void setResults(List results) { 23 | this.results = results; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/core/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.core; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.databinding.BaseObservable; 6 | import android.os.Bundle; 7 | import android.support.annotation.Nullable; 8 | import android.support.v4.app.Fragment; 9 | import android.support.v7.app.AppCompatDelegate; 10 | import android.util.Log; 11 | import android.view.LayoutInflater; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | 15 | import com.fall.gank.callback.BaseActivityCallback; 16 | 17 | import java.io.Serializable; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | /** 22 | * Created by 康颢曦 on 2016/11/27. 23 | */ 24 | 25 | public abstract class BaseFragment extends Fragment implements BaseView { 26 | public static final String KEY_VIEW_MODEL = "BaseFragment.viewmodel"; 27 | private BaseObservable baseObservable; 28 | private BaseActivityCallback baseActivityCallback; 29 | private AttachPresenterHelper mAttachPresenterHelper; 30 | protected List iPresenterList ; //储存引用的所有presenter,一个界面可能会有多个Presenter的情况 31 | @Override 32 | public void onCreate(@Nullable Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | setRetainInstance(true); 35 | } 36 | 37 | 38 | @Nullable 39 | @Override 40 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 41 | iPresenterList = new ArrayList<>(); 42 | if (savedInstanceState != null) { 43 | baseObservable = (BaseObservable) savedInstanceState.get(KEY_VIEW_MODEL); 44 | } 45 | View view = initBinding(inflater, container); 46 | if (baseObservable != null) { 47 | initOldData(baseObservable); 48 | } else { 49 | initData(); 50 | } 51 | mAttachPresenterHelper = new AttachPresenterHelper(iPresenterList); 52 | attachViewModel(); 53 | initView(savedInstanceState); 54 | mAttachPresenterHelper.initPresenter(baseObservable == null, baseActivityCallback); 55 | initListeners(); 56 | return view; 57 | } 58 | 59 | protected abstract View initBinding(LayoutInflater inflater, ViewGroup container); 60 | 61 | @Override 62 | public void onAttach(Context context) { 63 | Activity activity = (Activity) context; 64 | if (activity instanceof BaseActivity) { 65 | baseActivityCallback = (BaseActivity) activity; 66 | } 67 | super.onAttach(context); 68 | 69 | } 70 | 71 | @Override 72 | public void onDetach() { 73 | baseActivityCallback = null; 74 | super.onDetach(); 75 | } 76 | 77 | 78 | public void attachViewModel() { 79 | mAttachPresenterHelper.attachl(); 80 | } 81 | 82 | @Override 83 | public void onDestroy() { 84 | mAttachPresenterHelper.destroyPresenter(); 85 | super.onDestroy(); 86 | } 87 | 88 | @Override 89 | public void onSaveInstanceState(Bundle outState) { 90 | if (getViewModel() != null) { 91 | outState.putSerializable(KEY_VIEW_MODEL, (Serializable) getViewModel()); 92 | } 93 | super.onSaveInstanceState(outState); 94 | } 95 | 96 | @Override 97 | public void setUserVisibleHint(boolean isVisibleToUser) { 98 | super.setUserVisibleHint(isVisibleToUser); 99 | if (isVisibleToUser) { 100 | Log.d("tag", "可见"); 101 | } 102 | } 103 | 104 | public boolean isDarkTheme() { 105 | return AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/presenter/ClassificationPresenter.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.presenter; 2 | 3 | import android.util.Log; 4 | 5 | import com.anupcowkur.reservoir.Reservoir; 6 | import com.fall.gank.Utils.RxUtils; 7 | import com.fall.gank.core.BaseListPresenter; 8 | import com.fall.gank.database.Collection; 9 | import com.fall.gank.network.model.impl.DataManager; 10 | import com.fall.gank.network.model.IDataManager; 11 | import com.fall.gank.viewmodel.ClassificationViewModel; 12 | import com.fall.gank.viewmodel.ClassificationItemViewModel; 13 | import com.google.gson.reflect.TypeToken; 14 | 15 | import java.lang.reflect.Type; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | /** 20 | * Created by 康颢曦 on 2016/11/27. 21 | */ 22 | 23 | public class ClassificationPresenter extends BaseListPresenter { 24 | private String KEY = "ClassificationPresenter.Key"; 25 | private String type; 26 | private IDataManager mManager = new DataManager(); 27 | private List mList = new ArrayList<>(); 28 | 29 | public ClassificationPresenter(ClassificationViewModel listViewModel) { 30 | super(listViewModel); 31 | } 32 | 33 | @Override 34 | public void onPresenterCreate(boolean isNewCreate) { 35 | if (isNewCreate) { 36 | Type collectionType = new TypeToken>() { 37 | }.getType(); 38 | mCompositeSubscription.add(Reservoir.getUsingObservable(KEY + type, ClassificationItemViewModel.class, collectionType) 39 | .compose(RxUtils.applyIOToMainThreadSchedulers()) 40 | .map(classificationItemViewModel -> { 41 | List list = Collection.find(Collection.class, "url=?", classificationItemViewModel.url.get()); 42 | if (list.size() > 0) { 43 | classificationItemViewModel.isLike.set(true); 44 | } else { 45 | classificationItemViewModel.isLike.set(false); 46 | } 47 | return classificationItemViewModel; 48 | }) 49 | .toList() 50 | .subscribe(classificationItemViewModels -> { 51 | 52 | if (classificationItemViewModels.size() > 0) { 53 | listViewModel.setIVMs(classificationItemViewModels); 54 | // mAdapter.set(listViewModel.getHomeItemViewModelList()); 55 | showList(listViewModel.getIVMs()); 56 | listViewModel.isDataEnable.set(true); 57 | } 58 | }, throwable -> getData(page), () -> getData(page))); 59 | } else { 60 | page = listViewModel.getPage(); 61 | if (listViewModel.isRefresh.get()) { 62 | getData(page); 63 | } 64 | } 65 | } 66 | 67 | @Override 68 | public void getData(int page) { 69 | super.getData(page); 70 | Log.d("page",page+""); 71 | mCompositeSubscription.add(mManager.getClassificationData(type, page) 72 | .compose(RxUtils.applyIOToMainThreadSchedulers()) 73 | .subscribe(classificationItemViewModel -> { 74 | if (classificationItemViewModel != null) { 75 | mList.add(classificationItemViewModel); 76 | } 77 | 78 | }, throwable -> { 79 | listViewModel.isRefresh.set(false); 80 | loadError(throwable); 81 | } 82 | , () -> { 83 | loadDataComplete(mList); 84 | })); 85 | } 86 | 87 | @Override 88 | protected String getKey() { 89 | return KEY+type; 90 | } 91 | public void setType(String type) { 92 | this.type = type; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/view/fragment/FuliFragment.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.view.fragment; 2 | 3 | import android.databinding.BaseObservable; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.widget.GridLayoutManager; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | 11 | import com.fall.gank.R; 12 | import com.fall.gank.Utils.ListLoadNextHelper; 13 | import com.fall.gank.core.BaseListFragment; 14 | import com.fall.gank.core.IPresenter; 15 | import com.fall.gank.databinding.FragmentFuliBinding; 16 | import com.fall.gank.presenter.FuliFragmentPresenter; 17 | import com.fall.gank.view.activity.PhotoActivity; 18 | import com.fall.gank.viewmodel.FuliViewModel; 19 | import com.fall.gank.viewmodel.ImageItemViewModel; 20 | import com.github.markzhai.recyclerview.SingleTypeAdapter; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | /** 26 | * Created by 康颢曦 on 2016/11/27. 27 | */ 28 | 29 | public class FuliFragment extends BaseListFragment { 30 | private FragmentFuliBinding binding; 31 | private FuliFragmentPresenter fuliFragmentPresenter; 32 | private FuliViewModel fuliViewModel; 33 | private SingleTypeAdapter mAdapter; 34 | private GridLayoutManager mGridLayoutManager; 35 | private ListLoadNextHelper listLoadNextHelper; 36 | @Override 37 | protected View initBinding(LayoutInflater inflater, ViewGroup container) { 38 | binding = FragmentFuliBinding.inflate(inflater, container, false); 39 | return binding.getRoot(); 40 | } 41 | 42 | @Override 43 | public void initView(@Nullable Bundle savedInstanceState) { 44 | mGridLayoutManager.scrollToPositionWithOffset(fuliViewModel.getPosition(), fuliViewModel.getLastOffset()); 45 | } 46 | 47 | @Override 48 | public void initListeners() { 49 | super.initListeners(); 50 | binding.swipeContainer.setColorSchemeColors(getResources().getColor(R.color.colorPrimary)); 51 | binding.swipeContainer.setProgressBackgroundColorSchemeColor(getResources().getColor(R.color.swipeColor)); 52 | binding.setRefreshListener(() -> fuliFragmentPresenter.getData(1)); 53 | mAdapter.setPresenter((SingleTypeAdapter.Presenter) imageItemViewModel -> { 54 | PhotoActivity.newIntent(getContext(), imageItemViewModel.url.get()); 55 | }); 56 | listLoadNextHelper = new ListLoadNextHelper(binding.fuliList); 57 | listLoadNextHelper.setListOffsetListener((lastOffset, position) -> { 58 | fuliViewModel.setLastOffset(lastOffset); 59 | fuliViewModel.setPosition(position); 60 | }); 61 | listLoadNextHelper.setScrollLastListener(() -> { 62 | if (fuliViewModel.isDataEnable.get()) { 63 | fuliFragmentPresenter.loadNext(); 64 | } 65 | }); 66 | } 67 | 68 | @Override 69 | public SingleTypeAdapter getAdapter() { 70 | return mAdapter; 71 | } 72 | 73 | @Override 74 | public void initOldData(@Nullable BaseObservable baseObservable) { 75 | fuliViewModel = (FuliViewModel) baseObservable; 76 | initList(); 77 | fuliFragmentPresenter = new FuliFragmentPresenter(fuliViewModel); 78 | iPresenterList.add(fuliFragmentPresenter); 79 | } 80 | 81 | @Override 82 | public void initData() { 83 | fuliViewModel = new FuliViewModel(); 84 | initList(); 85 | fuliFragmentPresenter = new FuliFragmentPresenter(fuliViewModel); 86 | iPresenterList.add(fuliFragmentPresenter); 87 | } 88 | 89 | 90 | 91 | @Override 92 | public BaseObservable getViewModel() { 93 | return fuliViewModel; 94 | } 95 | 96 | private void initList() { 97 | mAdapter = new SingleTypeAdapter<>(getContext(), R.layout.view_image); 98 | if (fuliViewModel.getIVMs().size() > 0) { 99 | mAdapter.addAll(fuliViewModel.getIVMs()); 100 | } 101 | binding.setAdapter(mAdapter); 102 | mGridLayoutManager = new GridLayoutManager(getContext(), 2); 103 | binding.setLayoutmanager(mGridLayoutManager); 104 | binding.setViewmodel(fuliViewModel); 105 | 106 | } 107 | 108 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/network/model/impl/DataManager.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.network.model.impl; 2 | 3 | import android.util.Log; 4 | 5 | import com.fall.gank.Utils.TimeUtils; 6 | import com.fall.gank.database.Collection; 7 | import com.fall.gank.entity.ClassificationEntity; 8 | import com.fall.gank.entity.ClassificationResultsEntity; 9 | import com.fall.gank.network.model.IDataManager; 10 | import com.fall.gank.network.model.IGankModel; 11 | import com.fall.gank.network.model.impl.GankModel; 12 | import com.fall.gank.viewmodel.ClassificationItemViewModel; 13 | 14 | import java.text.ParseException; 15 | import java.util.HashMap; 16 | import java.util.List; 17 | 18 | import rx.Observable; 19 | 20 | import static com.fall.gank.view.fragment.ClassificationFragment.COLLECTION_TYPE; 21 | 22 | /** 23 | * Created by qqq34 on 2016/11/30. 24 | */ 25 | 26 | public class DataManager implements IDataManager { 27 | private IGankModel mIGankModel = GankModel.getInstance(); 28 | 29 | @Override 30 | public Observable getHomeData(int page) { 31 | Log.d("tag", page + ""); 32 | Observable observable = Observable.zip(mIGankModel.getClassifiData("福利", page), mIGankModel.getClassifiData("休息视频", page), this::createHomeData); 33 | return observable; 34 | } 35 | 36 | @Override 37 | public Observable getClassificationData(String section, int page) { 38 | Observable> observable; 39 | if (section.equals(COLLECTION_TYPE)) { 40 | observable = Observable.create(subscriber -> { 41 | try { 42 | if (page==1){ 43 | List collections = Collection.listAll(Collection.class); 44 | subscriber.onNext(collections); 45 | subscriber.onCompleted(); 46 | }else { 47 | subscriber.onNext(null); 48 | subscriber.onCompleted(); 49 | } 50 | 51 | } catch (Exception e) { 52 | subscriber.onError(e); 53 | } 54 | 55 | }); 56 | return observable.flatMap(Observable::from) 57 | .map(collection -> { 58 | ClassificationItemViewModel model = new ClassificationItemViewModel(collection.getClassificationName(), collection.getDimension(), collection.getYear(), collection.getMonthAndDay(), true, collection.getUrl()); 59 | return model; 60 | }); 61 | } else { 62 | return mIGankModel.getClassifiData(section, page) 63 | .map(classificationEntity -> classificationEntity.getResults()) 64 | .flatMap(Observable::from) 65 | .map(classificationResultsEntity -> { 66 | HashMap hashMap; 67 | try { 68 | hashMap = TimeUtils.getTime(classificationResultsEntity.getPublishedAt()); 69 | ClassificationItemViewModel model = new ClassificationItemViewModel(classificationResultsEntity.getType(), classificationResultsEntity.getDesc(), hashMap.get(TimeUtils.YEAR), hashMap.get(TimeUtils.MONTH) + "/" + hashMap.get(TimeUtils.DAY), false, classificationResultsEntity.getUrl()); 70 | List list = Collection.find(Collection.class, "url=?", model.url.get()); 71 | if (list.size() > 0) { 72 | model.isLike.set(true); 73 | } else { 74 | model.isLike.set(false); 75 | } 76 | return model; 77 | } catch (Exception e) { 78 | Observable.error(e); 79 | } 80 | return null; 81 | }); 82 | } 83 | 84 | 85 | } 86 | 87 | private ClassificationEntity createHomeData(ClassificationEntity data1, ClassificationEntity data2) { 88 | for (int i = 0; i < data1.getResults().size(); i++) { 89 | ClassificationResultsEntity fuli = data1.getResults().get(i); 90 | ClassificationResultsEntity reset = data2.getResults().get(i); 91 | try { 92 | HashMap hashMap = TimeUtils.getTime(fuli.getPublishedAt()); 93 | fuli.setDesc(hashMap.get(TimeUtils.YEAR) + "-" + hashMap.get(TimeUtils.MONTH) + "-" + hashMap.get(TimeUtils.DAY) + "######" + reset.getDesc()); 94 | fuli.setVideoUrl(reset.getUrl()); 95 | } catch (ParseException e) { 96 | 97 | fuli.setDesc(fuli.getDesc() + "######" + reset.getDesc()); 98 | } 99 | 100 | } 101 | return data1; 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/view/fragment/HomeFragment.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.view.fragment; 2 | 3 | import android.databinding.BaseObservable; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.widget.LinearLayoutManager; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | 11 | import com.fall.gank.R; 12 | import com.fall.gank.adapter.HomeAdapterDecorator; 13 | import com.fall.gank.core.BaseListFragment; 14 | import com.fall.gank.core.IPresenter; 15 | import com.fall.gank.databinding.FragmentHomeBinding; 16 | import com.fall.gank.presenter.HomeFragmentPresenter; 17 | import com.fall.gank.view.activity.WebViewActivity; 18 | import com.fall.gank.view.widget.RecyclerviewScrollHelper; 19 | import com.fall.gank.view.widget.SlowlyScrollLinearLayoutManager; 20 | import com.fall.gank.viewmodel.HomeItemViewModel; 21 | import com.fall.gank.viewmodel.HomeViewModel; 22 | import com.github.markzhai.recyclerview.SingleTypeAdapter; 23 | 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | /** 28 | * Created by 康颢曦 on 2016/11/27. 29 | */ 30 | 31 | public class HomeFragment extends BaseListFragment { 32 | private FragmentHomeBinding binding; 33 | private HomeFragmentPresenter homeFragmentPresenter; 34 | private HomeViewModel homeViewModel; 35 | private SlowlyScrollLinearLayoutManager mLinearLayoutManager; 36 | private SingleTypeAdapter mHomeItemViewModelSingleTypeAdapter; 37 | private RecyclerviewScrollHelper mRecyclerviewScrollHelper; 38 | 39 | @Override 40 | protected View initBinding(LayoutInflater inflater, ViewGroup container) { 41 | binding = FragmentHomeBinding.inflate(inflater, container, false); 42 | return binding.getRoot(); 43 | } 44 | 45 | @Override 46 | public void initView(@Nullable Bundle savedInstanceState) { 47 | binding.swipeContainer.setColorSchemeColors(getResources().getColor(R.color.colorPrimary)); 48 | binding.swipeContainer.setProgressBackgroundColorSchemeColor(getResources().getColor(R.color.swipeColor)); 49 | mLinearLayoutManager.scrollToPosition(homeViewModel.lastPosition.get()); 50 | mHomeItemViewModelSingleTypeAdapter.setDecorator(new HomeAdapterDecorator() { 51 | @Override 52 | public void onHomeClick(HomeItemViewModel homeItemViewModel) { 53 | WebViewActivity.newIntent(getContext(),homeItemViewModel.getVideoUrl()); 54 | } 55 | }); 56 | mRecyclerviewScrollHelper = new RecyclerviewScrollHelper(binding.homeRecyclerview); 57 | 58 | 59 | } 60 | 61 | @Override 62 | public void initListeners() { 63 | super.initListeners(); 64 | mRecyclerviewScrollHelper.setOnScrollLastListener(() -> { 65 | if (homeViewModel.isDataEnable.get()) { 66 | homeFragmentPresenter.loadNext(); 67 | } 68 | }); 69 | 70 | binding.setRefreshListener(() -> homeFragmentPresenter.getData(1)); 71 | mHomeItemViewModelSingleTypeAdapter.setPresenter((SingleTypeAdapter.Presenter) homeItemViewModel -> { 72 | 73 | }); 74 | 75 | } 76 | 77 | @Override 78 | public void initOldData(@Nullable BaseObservable baseObservable) { 79 | 80 | homeViewModel = (HomeViewModel) baseObservable; 81 | homeFragmentPresenter = new HomeFragmentPresenter(homeViewModel); 82 | initList(); 83 | iPresenterList.add(homeFragmentPresenter); 84 | } 85 | 86 | @Override 87 | public void initData() { 88 | 89 | homeViewModel = new HomeViewModel(); 90 | homeFragmentPresenter = new HomeFragmentPresenter(homeViewModel); 91 | initList(); 92 | iPresenterList.add(homeFragmentPresenter); 93 | } 94 | 95 | 96 | @Override 97 | public BaseObservable getViewModel() { 98 | return homeViewModel; 99 | } 100 | private void initList() { 101 | mHomeItemViewModelSingleTypeAdapter = new SingleTypeAdapter<>(getContext(), R.layout.view_home_item); 102 | if (homeViewModel.getIVMs().size() > 0) { 103 | mHomeItemViewModelSingleTypeAdapter.addAll(homeViewModel.getIVMs()); 104 | } 105 | binding.setAdapter(mHomeItemViewModelSingleTypeAdapter); 106 | mLinearLayoutManager = new SlowlyScrollLinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false); 107 | binding.setLayoutmanager(mLinearLayoutManager); 108 | binding.setViewmodel(homeViewModel); 109 | } 110 | 111 | @Override 112 | public void onSaveInstanceState(Bundle outState) { 113 | if (binding.homeRecyclerview.getLayoutManager() != null) { 114 | 115 | homeViewModel.lastPosition.set(mLinearLayoutManager.findLastVisibleItemPosition()); 116 | } 117 | super.onSaveInstanceState(outState); 118 | } 119 | 120 | @Override 121 | public SingleTypeAdapter getAdapter() { 122 | return mHomeItemViewModelSingleTypeAdapter; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/entity/GankDateData.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.entity; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by 康颢曦 on 2016/11/29. 7 | */ 8 | 9 | public class GankDateData { 10 | 11 | /** 12 | * error : false 13 | * results : ["2016-11-29","2016-11-28","2016-11-25","2016-11-24","2016-11-23","2016-11-22","2016-11-21","2016-11-18","2016-11-17","2016-11-16","2016-11-15","2016-11-14","2016-11-11","2016-11-10","2016-11-09","2016-11-08","2016-11-07","2016-11-04","2016-11-03","2016-11-02","2016-11-01","2016-10-31","2016-10-28","2016-10-27","2016-10-26","2016-10-25","2016-10-24","2016-10-21","2016-10-20","2016-10-19","2016-10-18","2016-10-17","2016-10-14","2016-10-13","2016-10-12","2016-10-11","2016-10-10","2016-10-09","2016-10-08","2016-09-30","2016-09-29","2016-09-28","2016-09-27","2016-09-26","2016-09-23","2016-09-22","2016-09-21","2016-09-20","2016-09-19","2016-09-18","2016-09-14","2016-09-13","2016-09-12","2016-09-10","2016-09-09","2016-09-08","2016-09-07","2016-09-06","2016-09-05","2016-09-02","2016-09-01","2016-08-31","2016-08-30","2016-08-29","2016-08-26","2016-08-25","2016-08-24","2016-08-23","2016-08-22","2016-08-19","2016-08-18","2016-08-17","2016-08-16","2016-08-15","2016-08-12","2016-08-11","2016-08-10","2016-08-09","2016-08-08","2016-08-05","2016-08-03","2016-08-02","2016-08-01","2016-07-29","2016-07-28","2016-07-27","2016-07-26","2016-07-25","2016-07-22","2016-07-20","2016-07-19","2016-07-18","2016-07-15","2016-07-14","2016-07-13","2016-07-12","2016-07-11","2016-07-08","2016-07-07","2016-07-06","2016-07-05","2016-07-04","2016-07-01","2016-06-30","2016-06-29","2016-06-28","2016-06-27","2016-06-24","2016-06-23","2016-06-22","2016-06-21","2016-06-20","2016-06-17","2016-06-16","2016-06-15","2016-06-14","2016-06-13","2016-06-12","2016-06-08","2016-06-07","2016-06-06","2016-06-03","2016-06-02","2016-06-01","2016-05-31","2016-05-30","2016-05-27","2016-05-26","2016-05-25","2016-05-24","2016-05-23","2016-05-20","2016-05-19","2016-05-18","2016-05-17","2016-05-16","2016-05-13","2016-05-12","2016-05-11","2016-05-10","2016-05-09","2016-05-06","2016-05-05","2016-05-04","2016-05-03","2016-04-29","2016-04-28","2016-04-27","2016-04-26","2016-04-25","2016-04-22","2016-04-21","2016-04-20","2016-04-19","2016-04-18","2016-04-15","2016-04-14","2016-04-13","2016-04-12","2016-04-11","2016-04-08","2016-04-07","2016-04-06","2016-04-05","2016-04-01","2016-03-31","2016-03-30","2016-03-29","2016-03-28","2016-03-25","2016-03-24","2016-03-23","2016-03-22","2016-03-21","2016-03-18","2016-03-17","2016-03-16","2016-03-15","2016-03-14","2016-03-11","2016-03-10","2016-03-09","2016-03-08","2016-03-07","2016-03-04","2016-03-03","2016-03-02","2016-03-01","2016-02-29","2016-02-26","2016-02-25","2016-02-24","2016-02-23","2016-02-22","2016-02-19","2016-02-18","2016-02-17","2016-02-16","2016-02-15","2016-02-04","2016-02-03","2016-02-02","2016-02-01","2016-01-29","2016-01-28","2016-01-27","2016-01-26","2016-01-25","2016-01-22","2016-01-21","2016-01-20","2016-01-19","2016-01-18","2016-01-15","2016-01-14","2016-01-13","2016-01-12","2016-01-11","2016-01-08","2016-01-07","2016-01-06","2016-01-05","2016-01-04","2015-12-31","2015-12-30","2015-12-29","2015-12-28","2015-12-25","2015-12-24","2015-12-23","2015-12-22","2015-12-21","2015-12-18","2015-12-17","2015-12-16","2015-12-15","2015-12-14","2015-12-11","2015-12-10","2015-12-09","2015-12-08","2015-12-07","2015-12-04","2015-12-03","2015-12-02","2015-12-01","2015-11-30","2015-11-27","2015-11-26","2015-11-25","2015-11-24","2015-11-23","2015-11-20","2015-11-19","2015-11-18","2015-11-17","2015-11-16","2015-11-13","2015-11-12","2015-11-11","2015-11-10","2015-11-09","2015-11-06","2015-11-05","2015-11-04","2015-11-03","2015-11-02","2015-10-30","2015-10-29","2015-10-28","2015-10-27","2015-10-26","2015-10-23","2015-10-22","2015-10-21","2015-10-20","2015-10-19","2015-10-16","2015-10-15","2015-10-14","2015-10-13","2015-10-12","2015-10-10","2015-10-09","2015-10-08","2015-09-30","2015-09-29","2015-09-28","2015-09-25","2015-09-24","2015-09-23","2015-09-22","2015-09-21","2015-09-18","2015-09-17","2015-09-16","2015-09-15","2015-09-14","2015-09-11","2015-09-10","2015-09-09","2015-09-08","2015-09-07","2015-09-06","2015-09-01","2015-08-31","2015-08-28","2015-08-27","2015-08-26","2015-08-25","2015-08-24","2015-08-21","2015-08-20","2015-08-19","2015-08-18","2015-08-17","2015-08-14","2015-08-13","2015-08-12","2015-08-11","2015-08-10","2015-08-07","2015-08-06","2015-08-05","2015-08-03","2015-07-31","2015-07-30","2015-07-29","2015-07-28","2015-07-27","2015-07-24","2015-07-23","2015-07-22","2015-07-21","2015-07-20","2015-07-17","2015-07-16","2015-07-15","2015-07-14","2015-07-13","2015-07-10","2015-07-09","2015-07-08","2015-07-07","2015-07-03","2015-07-02","2015-07-01","2015-06-30","2015-06-29","2015-06-26","2015-06-19","2015-06-18","2015-06-17","2015-06-16","2015-06-15","2015-06-12","2015-06-11","2015-06-10","2015-06-09","2015-06-05","2015-06-04","2015-06-03","2015-06-02","2015-05-29","2015-05-28","2015-05-27","2015-05-26","2015-05-25","2015-05-22","2015-05-21","2015-05-20","2015-05-19","2015-05-18"] 14 | */ 15 | 16 | private List results; 17 | 18 | public List getResults() { 19 | return results; 20 | } 21 | 22 | public void setResults(List results) { 23 | this.results = results; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_classification_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 15 | 16 | 19 | 20 | 21 | 28 | 29 | 39 | 40 | 45 | 46 | 52 | 53 | 59 | 60 | 65 | 66 | 74 | 75 | 81 | 82 | 90 | 91 | 92 | 98 | 99 | 106 | 107 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/view/fragment/ClassificationFragment.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.view.fragment; 2 | 3 | import android.databinding.BaseObservable; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.widget.LinearLayoutManager; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | 11 | import com.fall.gank.R; 12 | import com.fall.gank.Utils.ListLoadNextHelper; 13 | import com.fall.gank.adapter.ClassificationAdapterDecorator; 14 | import com.fall.gank.core.BaseListFragment; 15 | import com.fall.gank.core.IPresenter; 16 | import com.fall.gank.databinding.FragmentClassificationBinding; 17 | import com.fall.gank.presenter.ClassificationPresenter; 18 | import com.fall.gank.view.activity.WebViewActivity; 19 | import com.fall.gank.viewmodel.ClassificationViewModel; 20 | import com.fall.gank.viewmodel.ClassificationItemViewModel; 21 | import com.github.markzhai.recyclerview.SingleTypeAdapter; 22 | 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | 26 | /** 27 | * Created by 康颢曦 on 2016/11/27. 28 | */ 29 | 30 | public class ClassificationFragment extends BaseListFragment { 31 | private FragmentClassificationBinding binding; 32 | private ClassificationPresenter mClassificationPresenter; 33 | private ClassificationViewModel mClassificationViewModel; 34 | private SingleTypeAdapter mSingleTypeAdapter; 35 | private LinearLayoutManager mLinearLayoutManager; 36 | public static String KEY = "ClassificationFragment.key"; 37 | private ListLoadNextHelper listLoadNextHelper; 38 | public static final String COLLECTION_TYPE = "collection"; 39 | 40 | @Override 41 | protected View initBinding(LayoutInflater inflater, ViewGroup container) { 42 | binding = FragmentClassificationBinding.inflate(inflater, container, false); 43 | return binding.getRoot(); 44 | } 45 | 46 | @Override 47 | public void initView(@Nullable Bundle savedInstanceState) { 48 | mSingleTypeAdapter.setDecorator(new ClassificationAdapterDecorator()); 49 | mLinearLayoutManager.scrollToPositionWithOffset(mClassificationViewModel.getPosition(), mClassificationViewModel.getLastOffset()); 50 | } 51 | 52 | @Override 53 | public void initListeners() { 54 | super.initListeners(); 55 | binding.swipeContainer.setColorSchemeColors(getResources().getColor(R.color.colorPrimary)); 56 | binding.swipeContainer.setProgressBackgroundColorSchemeColor(getResources().getColor(R.color.swipeColor)); 57 | binding.setRefreshListener(() -> mClassificationPresenter.getData(1)); 58 | mSingleTypeAdapter.setPresenter((SingleTypeAdapter.Presenter) classificationItemViewModel -> { 59 | WebViewActivity.newIntent(getContext(), classificationItemViewModel.url.get()); 60 | }); 61 | 62 | listLoadNextHelper = new ListLoadNextHelper(binding.classificationList); 63 | listLoadNextHelper.setListOffsetListener((lastOffset, position) -> { 64 | mClassificationViewModel.setLastOffset(lastOffset); 65 | mClassificationViewModel.setPosition(position); 66 | }); 67 | listLoadNextHelper.setScrollLastListener(() -> { 68 | 69 | if (mClassificationViewModel.isDataEnable.get()) { 70 | mClassificationPresenter.loadNext(); 71 | } 72 | }); 73 | 74 | } 75 | 76 | @Override 77 | public SingleTypeAdapter getAdapter() { 78 | return mSingleTypeAdapter; 79 | } 80 | 81 | @Override 82 | public void initOldData(@Nullable BaseObservable baseObservable) { 83 | mClassificationViewModel = (ClassificationViewModel) baseObservable; 84 | initList(); 85 | mClassificationPresenter = new ClassificationPresenter(mClassificationViewModel); 86 | mClassificationPresenter.setType(mClassificationViewModel.getType()); 87 | 88 | 89 | iPresenterList.add(mClassificationPresenter); 90 | } 91 | 92 | @Override 93 | public void initData() { 94 | mClassificationViewModel = new ClassificationViewModel(); 95 | mClassificationViewModel.setType(getArguments().getString(KEY)); 96 | 97 | initList(); 98 | mClassificationPresenter = new ClassificationPresenter(mClassificationViewModel); 99 | mClassificationPresenter.setType(mClassificationViewModel.getType()); 100 | 101 | iPresenterList.add(mClassificationPresenter); 102 | } 103 | 104 | 105 | @Override 106 | public BaseObservable getViewModel() { 107 | return mClassificationViewModel; 108 | } 109 | 110 | private void initList() { 111 | mSingleTypeAdapter = new SingleTypeAdapter<>(getContext(), R.layout.view_classification_item); 112 | // mClassificationViewModel.getClassificationItemViewModelList().add(new ClassificationItemViewModel("a","a","a","a",false)); 113 | if (mClassificationViewModel.getIVMs().size() > 0) { 114 | mSingleTypeAdapter.addAll(mClassificationViewModel.getIVMs()); 115 | } 116 | binding.setAdapter(mSingleTypeAdapter); 117 | mLinearLayoutManager = new LinearLayoutManager(getContext()); 118 | binding.setLayoutmanager(mLinearLayoutManager); 119 | binding.setViewmodel(mClassificationViewModel); 120 | 121 | } 122 | 123 | public static ClassificationFragment newInstance(String type) { 124 | Bundle bundle = new Bundle(); 125 | bundle.putString(KEY, type); 126 | ClassificationFragment classificationFragment = new ClassificationFragment(); 127 | classificationFragment.setArguments(bundle); 128 | return classificationFragment; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/view/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.view.activity; 2 | 3 | import android.databinding.BaseObservable; 4 | import android.databinding.DataBindingUtil; 5 | import android.os.Bundle; 6 | import android.support.annotation.Nullable; 7 | import android.support.v4.app.Fragment; 8 | import android.support.v4.view.ViewPager; 9 | import android.support.v7.widget.Toolbar; 10 | import android.view.Menu; 11 | import com.fall.gank.R; 12 | import com.fall.gank.Utils.MDStatusBarCompat; 13 | import com.fall.gank.adapter.ViewPagerAdapter; 14 | import com.fall.gank.core.BaseActivity; 15 | import com.fall.gank.databinding.ActivityMainBinding; 16 | import com.fall.gank.presenter.MainActivityPresenter; 17 | import com.fall.gank.presenter.TestPresenter; 18 | import com.fall.gank.presenter.factory.PresenterFactory; 19 | import com.fall.gank.view.fragment.ClassificationFragment; 20 | import com.fall.gank.view.fragment.FuliFragment; 21 | import com.fall.gank.view.fragment.HomeFragment; 22 | import com.fall.gank.view.fragment.SettingFragment; 23 | import com.fall.gank.viewmodel.MainViewModel; 24 | 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | 29 | public class MainActivity extends BaseActivity { 30 | private ActivityMainBinding binding; 31 | private MainViewModel mViewModel; 32 | private MainActivityPresenter mMainActivityPresenter; 33 | private List fragmentList; 34 | private ViewPagerAdapter viewPagerAdapter; 35 | private TestPresenter mTestPresenter; 36 | 37 | 38 | @Override 39 | public void initBinding() { 40 | binding = DataBindingUtil.setContentView(MainActivity.this, R.layout.activity_main); 41 | } 42 | 43 | @Override 44 | public void initView(@Nullable Bundle savedInstanceState) { 45 | MDStatusBarCompat.setOrdinaryToolBar(this); 46 | fragmentList = new ArrayList<>(); 47 | fragmentList.add(new HomeFragment()); 48 | fragmentList.add(ClassificationFragment.newInstance("Android")); 49 | fragmentList.add(ClassificationFragment.newInstance("iOS")); 50 | fragmentList.add(ClassificationFragment.newInstance("前端")); 51 | fragmentList.add(new FuliFragment()); 52 | fragmentList.add(new SettingFragment()); 53 | viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager()); 54 | viewPagerAdapter.setFragmentList(fragmentList); 55 | binding.viewpager.setAdapter(viewPagerAdapter); 56 | binding.viewpager.setOffscreenPageLimit(6); 57 | } 58 | 59 | @Override 60 | protected void initToolbar(Bundle savedInstanceState) { 61 | Toolbar toolbar = binding.toolbar; 62 | setSupportActionBar(toolbar); 63 | } 64 | 65 | @Override 66 | public void initListeners() { 67 | 68 | 69 | binding.viewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { 70 | @Override 71 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 72 | 73 | } 74 | 75 | @Override 76 | public void onPageSelected(int position) { 77 | mViewModel.setCurrentSelecte(position); 78 | } 79 | 80 | @Override 81 | public void onPageScrollStateChanged(int state) { 82 | 83 | } 84 | }); 85 | binding.setViewClick(view -> { 86 | switch (view.getId()) { 87 | case R.id.image_home: { 88 | mViewModel.setCurrentSelecte(0); 89 | binding.viewpager.setCurrentItem(0, false); 90 | break; 91 | } 92 | case R.id.image_android: { 93 | mViewModel.setCurrentSelecte(1); 94 | binding.viewpager.setCurrentItem(1, false); 95 | break; 96 | } 97 | case R.id.image_ios: { 98 | mViewModel.setCurrentSelecte(2); 99 | binding.viewpager.setCurrentItem(2, false); 100 | break; 101 | } 102 | case R.id.image_web: { 103 | mViewModel.setCurrentSelecte(3); 104 | binding.viewpager.setCurrentItem(3, false); 105 | break; 106 | } 107 | case R.id.image_fuli: { 108 | mViewModel.setCurrentSelecte(4); 109 | binding.viewpager.setCurrentItem(4, false); 110 | break; 111 | } 112 | case R.id.image_setting: { 113 | mViewModel.setCurrentSelecte(5); 114 | binding.viewpager.setCurrentItem(5, false); 115 | break; 116 | } 117 | } 118 | }); 119 | binding.toolbar.setOnMenuItemClickListener(item -> { 120 | switch (item.getItemId()) { 121 | case R.id.go_github: 122 | WebViewActivity.newIntent(MainActivity.this, "https://github.com/348476129/MVVM-framework"); 123 | break; 124 | case R.id.test: 125 | mTestPresenter.onTextClick(); 126 | break; 127 | } 128 | return true; 129 | }); 130 | } 131 | 132 | @Override 133 | public void initOldData(BaseObservable baseObservable) { 134 | mViewModel = (MainViewModel) baseObservable; 135 | binding.setMainViewModel(mViewModel); 136 | mMainActivityPresenter = new MainActivityPresenter(mViewModel); 137 | mTestPresenter = new PresenterFactory().getTextPresenter(mViewModel); 138 | iPresenterList.add(mMainActivityPresenter); 139 | iPresenterList.add(mTestPresenter); 140 | } 141 | 142 | @Override 143 | public void initData() { 144 | mViewModel = new MainViewModel(0); 145 | binding.setMainViewModel(mViewModel); 146 | mMainActivityPresenter = new MainActivityPresenter(mViewModel); 147 | mTestPresenter = new PresenterFactory().getTextPresenter(mViewModel); 148 | iPresenterList.add(mMainActivityPresenter); 149 | iPresenterList.add(mTestPresenter); 150 | 151 | } 152 | 153 | 154 | @Override 155 | public BaseObservable getViewModel() { 156 | return mViewModel; 157 | } 158 | 159 | @Override 160 | public boolean onCreateOptionsMenu(Menu menu) { 161 | getMenuInflater().inflate(R.menu.menu_home, menu); 162 | return true; 163 | } 164 | 165 | } 166 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/Utils/MDStatusBarCompat.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.Utils; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.os.Build; 6 | import android.support.design.widget.AppBarLayout; 7 | import android.support.design.widget.CollapsingToolbarLayout; 8 | import android.support.design.widget.CoordinatorLayout; 9 | import android.support.v4.content.ContextCompat; 10 | import android.support.v4.view.ViewCompat; 11 | import android.support.v7.widget.Toolbar; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.widget.ImageView; 15 | 16 | import com.fall.gank.R; 17 | 18 | /** 19 | * @author 幸运Science-陈土燊 20 | * @description 21 | * @email chentushen.science@gmail.com,274240671@qq.com 22 | * @data 2016/4/14 23 | */ 24 | public class MDStatusBarCompat { 25 | 26 | private static View mStatusBarView; 27 | 28 | /** 29 | * 简单型状态栏(ToolBar) 30 | * 31 | * @param activity 32 | */ 33 | public static void setOrdinaryToolBar(Activity activity) { 34 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 35 | activity.getWindow().setStatusBarColor(ContextCompat.getColor(activity, R.color.colorPrimaryDark)); 36 | } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { 37 | setKKStatusBar(activity, R.color.colorPrimaryDark); 38 | } 39 | } 40 | 41 | /** 42 | * 图片全屏透明状态栏(图片位于状态栏下面) 43 | * 44 | * @param activity 45 | */ 46 | public static void setImageTransparent(Activity activity) { 47 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 48 | activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); 49 | } 50 | } 51 | 52 | /** 53 | * 图片全屏半透明状态栏(图片位于状态栏下面) 54 | * 55 | * @param activity 56 | */ 57 | public static void setImageTranslucent(Activity activity) { 58 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 59 | activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); 60 | activity.getWindow().setStatusBarColor(ContextCompat.getColor(activity, R.color.statusBar)); 61 | } else { 62 | setKKStatusBar(activity, R.color.statusBar); 63 | } 64 | } 65 | 66 | /** 67 | * ToolBar+TabLayout状态栏(ToolBar可伸缩) 68 | * 69 | * @param activity 70 | */ 71 | public static void setToolbarTabLayout(Activity activity) { 72 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 73 | activity.getWindow().setStatusBarColor(ContextCompat.getColor(activity, R.color.colorPrimaryDark)); 74 | } 75 | } 76 | 77 | /** 78 | * DrawerLayout+ToolBar+TabLayout状态栏(ToolBar可伸缩) 79 | * 80 | * @param activity 81 | * @param coordinatorLayout 82 | */ 83 | public static void setDrawerToolbarTabLayout(Activity activity, CoordinatorLayout coordinatorLayout) { 84 | if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { 85 | ViewGroup contentLayout = (ViewGroup) activity.findViewById(android.R.id.content); 86 | contentLayout.getChildAt(0).setFitsSystemWindows(false); 87 | coordinatorLayout.setFitsSystemWindows(true); 88 | setKKStatusBar(activity, R.color.statusBar); 89 | } 90 | } 91 | 92 | /** 93 | * DrawerLayout+ToolBar型状态栏 94 | * 95 | * @param activity 96 | */ 97 | public static void setDrawerToolbar(Activity activity) { 98 | if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { 99 | ViewGroup contentLayout = (ViewGroup) activity.findViewById(android.R.id.content); 100 | contentLayout.getChildAt(0).setFitsSystemWindows(false); 101 | setKKStatusBar(activity, R.color.statusBar); 102 | } 103 | } 104 | 105 | /** 106 | * CollapsingToolbarLayout状态栏(可折叠图片) 107 | * 108 | * @param activity 109 | * @param coordinatorLayout 110 | * @param appBarLayout 111 | * @param imageView 112 | * @param toolbar 113 | */ 114 | public static void setCollapsingToolbar(Activity activity, CoordinatorLayout coordinatorLayout, 115 | AppBarLayout appBarLayout, ImageView imageView, Toolbar toolbar) { 116 | if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { 117 | coordinatorLayout.setFitsSystemWindows(false); 118 | appBarLayout.setFitsSystemWindows(false); 119 | imageView.setFitsSystemWindows(false); 120 | toolbar.setFitsSystemWindows(true); 121 | CollapsingToolbarLayout.LayoutParams lp = (CollapsingToolbarLayout.LayoutParams) toolbar.getLayoutParams(); 122 | lp.height = (int) (getStatusBarHeight(activity) + 123 | activity.getResources().getDimension(R.dimen.abc_action_bar_default_height_material)); 124 | toolbar.setLayoutParams(lp); 125 | setKKStatusBar(activity, R.color.statusBar); 126 | setCollapsingToolbarStatus(appBarLayout); 127 | } 128 | } 129 | 130 | /** 131 | * Android4.4上CollapsingToolbar折叠时statusBar显示和隐藏 132 | * 133 | * @param appBarLayout 134 | */ 135 | private static void setCollapsingToolbarStatus(AppBarLayout appBarLayout) { 136 | ViewCompat.setAlpha(mStatusBarView, 1); 137 | appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() { 138 | @Override 139 | public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) { 140 | int maxScroll = appBarLayout.getTotalScrollRange(); 141 | float percentage = (float) Math.abs(verticalOffset) / (float) maxScroll; 142 | ViewCompat.setAlpha(mStatusBarView, percentage); 143 | } 144 | }); 145 | } 146 | 147 | private static void setKKStatusBar(Activity activity, int statusBarColor) { 148 | ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content); 149 | mStatusBarView = contentView.getChildAt(0); 150 | //改变颜色时避免重复添加statusBarView 151 | if (mStatusBarView != null && mStatusBarView.getMeasuredHeight() == getStatusBarHeight(activity)) { 152 | mStatusBarView.setBackgroundColor(ContextCompat.getColor(activity, statusBarColor)); 153 | return; 154 | } 155 | mStatusBarView = new View(activity); 156 | ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 157 | getStatusBarHeight(activity)); 158 | mStatusBarView.setBackgroundColor(ContextCompat.getColor(activity, statusBarColor)); 159 | contentView.addView(mStatusBarView, lp); 160 | } 161 | 162 | private static int getStatusBarHeight(Context context) { 163 | int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); 164 | return context.getResources().getDimensionPixelSize(resourceId); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /app/src/main/java/com/fall/gank/view/activity/WebViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.fall.gank.view.activity; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.databinding.BaseObservable; 6 | import android.databinding.DataBindingUtil; 7 | import android.net.Uri; 8 | import android.os.Bundle; 9 | import android.support.annotation.Nullable; 10 | import android.support.design.widget.Snackbar; 11 | import android.support.v7.widget.Toolbar; 12 | import android.view.KeyEvent; 13 | import android.view.Menu; 14 | import android.view.View; 15 | import android.webkit.WebChromeClient; 16 | import android.webkit.WebSettings; 17 | import android.webkit.WebView; 18 | import android.webkit.WebViewClient; 19 | 20 | import com.fall.gank.R; 21 | import com.fall.gank.Utils.MDStatusBarCompat; 22 | import com.fall.gank.Utils.Utils; 23 | import com.fall.gank.core.BaseActivity; 24 | import com.fall.gank.core.IPresenter; 25 | import com.fall.gank.databinding.WebviewActivityBinding; 26 | import com.fall.gank.presenter.WebViewActivityPresenter; 27 | import com.fall.gank.viewmodel.WebViewModel; 28 | 29 | import java.util.ArrayList; 30 | import java.util.List; 31 | 32 | /** 33 | * Created by qqq34 on 2016/12/8. 34 | */ 35 | 36 | public class WebViewActivity extends BaseActivity { 37 | 38 | private static final String EXTRA_URL = "WebViewActivity.url"; 39 | 40 | private WebviewActivityBinding mWebviewActivityBinding; 41 | private WebViewModel mModel; 42 | private WebViewActivityPresenter mPresenter; 43 | 44 | 45 | public static void newIntent(Context context, String url) { 46 | Intent intent = new Intent(context, WebViewActivity.class); 47 | intent.putExtra(EXTRA_URL, url); 48 | context.startActivity(intent); 49 | } 50 | 51 | @Override 52 | protected void initBinding() { 53 | mWebviewActivityBinding = DataBindingUtil.setContentView(WebViewActivity.this, R.layout.webview_activity); 54 | } 55 | 56 | @Override 57 | protected void initToolbar(Bundle savedInstanceState) { 58 | Toolbar toolbar = mWebviewActivityBinding.toolbar; 59 | toolbar.setTitle(getString(R.string.app_name)); 60 | setSupportActionBar(toolbar); 61 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 62 | } 63 | 64 | @Override 65 | public void initView(@Nullable Bundle savedInstanceState) { 66 | MDStatusBarCompat.setOrdinaryToolBar(this); 67 | WebView webView = mWebviewActivityBinding.webview; 68 | WebSettings settings = webView.getSettings(); 69 | settings.setJavaScriptEnabled(true); 70 | settings.setLoadWithOverviewMode(true); 71 | settings.setAppCacheEnabled(true); 72 | settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN); 73 | settings.setSupportZoom(true); 74 | webView.setWebChromeClient(new ChromeClient()); 75 | webView.setWebViewClient(new WebClient()); 76 | mModel.webViewUrl.set(getIntent().getStringExtra(EXTRA_URL)); 77 | } 78 | 79 | @Override 80 | public void initListeners() { 81 | mWebviewActivityBinding.toolbar.setNavigationOnClickListener(view -> { 82 | finish(); 83 | }); 84 | mWebviewActivityBinding.toolbar.setOnMenuItemClickListener(item -> { 85 | int id = item.getItemId(); 86 | switch (id) { 87 | case R.id.refresh: 88 | mWebviewActivityBinding.webview.reload(); 89 | return true; 90 | case R.id.copy: 91 | Utils.copyToClipBoard(this, mWebviewActivityBinding.webview.getUrl()); 92 | Snackbar.make(mWebviewActivityBinding.webview, "复制成功", Snackbar.LENGTH_SHORT).show(); 93 | 94 | return true; 95 | case R.id.open: 96 | Intent intent = new Intent(); 97 | intent.setAction(Intent.ACTION_VIEW); 98 | Uri uri = Uri.parse(mWebviewActivityBinding.webview.getUrl()); 99 | intent.setData(uri); 100 | if (intent.resolveActivity(getPackageManager()) != null) { 101 | startActivity(intent); 102 | } else { 103 | Snackbar.make(mWebviewActivityBinding.webview, "打开失败:没有安装能打开此链接的app", Snackbar.LENGTH_SHORT).show(); 104 | } 105 | return true; 106 | } 107 | return super.onOptionsItemSelected(item); 108 | }); 109 | } 110 | 111 | @Override 112 | public void initOldData(@Nullable BaseObservable baseObservable) { 113 | mModel = (WebViewModel) baseObservable; 114 | 115 | mWebviewActivityBinding.setWebViewModel(mModel); 116 | mPresenter = new WebViewActivityPresenter(mModel); 117 | iPresenterList.add(mPresenter); 118 | } 119 | 120 | @Override 121 | public void initData() { 122 | mModel = new WebViewModel(); 123 | mWebviewActivityBinding.setWebViewModel(mModel); 124 | mPresenter = new WebViewActivityPresenter(mModel); 125 | iPresenterList.add(mPresenter); 126 | } 127 | 128 | 129 | 130 | 131 | @Override 132 | public BaseObservable getViewModel() { 133 | return mModel; 134 | } 135 | 136 | private class ChromeClient extends WebChromeClient { 137 | 138 | @Override 139 | public void onProgressChanged(WebView view, int newProgress) { 140 | super.onProgressChanged(view, newProgress); 141 | mModel.progress.set(newProgress); 142 | } 143 | 144 | 145 | @Override 146 | public void onReceivedTitle(WebView view, String title) { 147 | super.onReceivedTitle(view, title); 148 | mWebviewActivityBinding.toolbar.setTitle(title); 149 | } 150 | } 151 | 152 | @Override 153 | public boolean onKeyDown(int keyCode, KeyEvent event) { 154 | if (event.getAction() == KeyEvent.ACTION_DOWN) { 155 | switch (keyCode) { 156 | case KeyEvent.KEYCODE_BACK: 157 | if (mWebviewActivityBinding.webview.canGoBack()) { 158 | mWebviewActivityBinding.webview.goBack(); 159 | } else { 160 | finish(); 161 | } 162 | return true; 163 | } 164 | } 165 | return super.onKeyDown(keyCode, event); 166 | } 167 | 168 | private class WebClient extends WebViewClient { 169 | 170 | public boolean shouldOverrideUrlLoading(WebView view, String url) { 171 | if (url != null) view.loadUrl(url); 172 | return true; 173 | } 174 | } 175 | 176 | @Override 177 | protected void onDestroy() { 178 | super.onDestroy(); 179 | mWebviewActivityBinding.webview.destroy(); 180 | } 181 | 182 | 183 | @Override 184 | protected void onPause() { 185 | mWebviewActivityBinding.webview.onPause(); 186 | super.onPause(); 187 | } 188 | 189 | 190 | @Override 191 | protected void onResume() { 192 | super.onResume(); 193 | mWebviewActivityBinding.webview.onResume(); 194 | } 195 | 196 | @Override 197 | public boolean onCreateOptionsMenu(Menu menu) { 198 | getMenuInflater().inflate(R.menu.menu_webview, menu); 199 | return true; 200 | } 201 | } 202 | --------------------------------------------------------------------------------