Repository: luqinmao/TomatoIt Branch: master Commit: 41d21d5adedc Files: 124 Total size: 274.0 KB Directory structure: gitextract_n0uuxjs9/ ├── .gitignore ├── .idea/ │ ├── gradle.xml │ ├── inspectionProfiles/ │ │ └── Project_Default.xml │ ├── misc.xml │ ├── modules.xml │ ├── runConfigurations.xml │ └── vcs.xml ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── lqm/ │ │ └── tomatoit/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── lqm/ │ │ │ └── tomatoit/ │ │ │ ├── api/ │ │ │ │ └── WanService.java │ │ │ ├── app/ │ │ │ │ ├── App.java │ │ │ │ └── AppConst.java │ │ │ ├── helper/ │ │ │ │ ├── Convert.java │ │ │ │ ├── JsonConvert.java │ │ │ │ ├── LzyResponse.java │ │ │ │ ├── SimpleResponse.java │ │ │ │ ├── interceptor/ │ │ │ │ │ └── TokenInterceptor.java │ │ │ │ └── rxjavahelper/ │ │ │ │ ├── RxObserver.java │ │ │ │ ├── RxResultHelper.java │ │ │ │ └── RxSchedulersHelper.java │ │ │ ├── manager/ │ │ │ │ └── ImageLoaderManager.java │ │ │ ├── model/ │ │ │ │ ├── ResponseData.java │ │ │ │ ├── pojo/ │ │ │ │ │ ├── ArticleBean.java │ │ │ │ │ ├── BannerBean.java │ │ │ │ │ ├── HotKeyBean.java │ │ │ │ │ ├── TypeChildrenBean.java │ │ │ │ │ └── UserBean.java │ │ │ │ └── pojoVO/ │ │ │ │ ├── ArticleListVO.java │ │ │ │ └── TypeTagVO.java │ │ │ ├── ui/ │ │ │ │ ├── activity/ │ │ │ │ │ ├── AboutActivity.java │ │ │ │ │ ├── CollectActivity.java │ │ │ │ │ ├── LoginActivity.java │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ ├── SearchActivity.java │ │ │ │ │ └── WebViewActivity.java │ │ │ │ ├── adapter/ │ │ │ │ │ ├── ArticleListAdapter.java │ │ │ │ │ ├── CollectArticleAdapter.java │ │ │ │ │ └── FragPagerAdapter.java │ │ │ │ ├── base/ │ │ │ │ │ ├── BaseActivity.java │ │ │ │ │ ├── BaseFragment.java │ │ │ │ │ └── BasePresenter.java │ │ │ │ ├── fragment/ │ │ │ │ │ ├── HomeFragment.java │ │ │ │ │ ├── TypeFragment.java │ │ │ │ │ └── UserFragment.java │ │ │ │ ├── presenter/ │ │ │ │ │ ├── CollectPresenter.java │ │ │ │ │ ├── HomePresenter.java │ │ │ │ │ ├── LoginRegistPresenter.java │ │ │ │ │ ├── SearchPresenter.java │ │ │ │ │ ├── TypePresenter.java │ │ │ │ │ └── WebViewPresenter.java │ │ │ │ └── view/ │ │ │ │ ├── CollectView.java │ │ │ │ ├── CommonWebView.java │ │ │ │ ├── HomeView.java │ │ │ │ ├── LoginRegistView.java │ │ │ │ ├── SearchView.java │ │ │ │ └── TypeView.java │ │ │ ├── util/ │ │ │ │ ├── ActivityUtils.java │ │ │ │ ├── L.java │ │ │ │ ├── PrefUtils.java │ │ │ │ ├── SharesUtils.java │ │ │ │ ├── T.java │ │ │ │ └── UIUtils.java │ │ │ └── widget/ │ │ │ ├── AutoLinefeedLayout.java │ │ │ ├── CustomDialog.java │ │ │ ├── CustomPopWindow.java │ │ │ ├── DynamicWave.java │ │ │ ├── IconFontTextView.java │ │ │ ├── RoundImageView.java │ │ │ └── WebViewFragment.java │ │ └── res/ │ │ ├── anim/ │ │ │ ├── slide_left_in.xml │ │ │ ├── slide_left_out.xml │ │ │ ├── slide_right_in.xml │ │ │ └── slide_right_out.xml │ │ ├── drawable/ │ │ │ ├── bg_round_frame_gray.xml │ │ │ ├── dialog_waiting.xml │ │ │ ├── progress_bar_status.xml │ │ │ ├── sel_menu_item.xml │ │ │ ├── shape_bg_round_white.xml │ │ │ ├── shape_tag_nor.xml │ │ │ └── shape_tag_sel.xml │ │ ├── layout/ │ │ │ ├── activity_about.xml │ │ │ ├── activity_collect.xml │ │ │ ├── activity_login.xml │ │ │ ├── activity_main.xml │ │ │ ├── activity_search.xml │ │ │ ├── activity_webview.xml │ │ │ ├── dialog_waiting.xml │ │ │ ├── frag_home.xml │ │ │ ├── frag_type.xml │ │ │ ├── frag_user.xml │ │ │ ├── item_article.xml │ │ │ ├── item_banner.xml │ │ │ ├── item_hot_key.xml │ │ │ ├── item_no_data.xml │ │ │ ├── item_tag_layout.xml │ │ │ ├── layout_banner.xml │ │ │ ├── layout_blank_tip.xml │ │ │ ├── layout_topbar_home.xml │ │ │ ├── layout_topbar_nor.xml │ │ │ └── popup_webview_more.xml │ │ └── values/ │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test/ │ └── java/ │ └── com/ │ └── lqm/ │ └── tomatoit/ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── iconfile/ │ ├── demo.css │ ├── demo_fontclass.html │ ├── demo_symbol.html │ ├── demo_unicode.html │ ├── iconfont.css │ ├── iconfont.js │ └── 用浏览器打开deni_unicode可以查看相关icon.txt └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.iml .gradle /local.properties /.idea/workspace.xml /.idea/libraries .DS_Store /build /captures .externalNativeBuild ================================================ FILE: .idea/gradle.xml ================================================ ================================================ FILE: .idea/inspectionProfiles/Project_Default.xml ================================================ ================================================ FILE: .idea/misc.xml ================================================ ================================================ FILE: .idea/modules.xml ================================================ ================================================ FILE: .idea/runConfigurations.xml ================================================ ================================================ FILE: .idea/vcs.xml ================================================ ================================================ FILE: README.md ================================================ # 用okgo、rxjava、mvp实现一个玩Android的客户端 # # 一、简述 # 现在Android开发,MVP+RxJava+Retrofit这安卓三剑客搭配真是主流,但这里我用了okgo+Rxjava+mvp来开发一个完整的APP,(okgo,比 Retrofit 更简单易用的网络请求框架,开发者的原话,真的不错)个人感觉此项目的框架很适合开发者在个人或者公司的项目中使用,相比retrofit和Google的MVP模式,挺简洁方便的,适合学习mvp模式在项目的应用。 # 二、备注 # - 此项目提供了一个完整的项目的开发解决方案,包括mvp模式Base类的封装,业务分层,iconfont图标适配方案,autoLayout全尺寸适配方案等 - okgo网络框架: https://github.com/jeasonlzy/okhttp-OkGo - 项目的接口:http://www.wanandroid.com/blog/show/2 ## 技术与框架 ## - okgo网络框架 - rxjava(增加适当的封装) - BRVAH - autolayout - 自适应图标IconFont - 具体看代码吧 ## 项目截图 ## ![](https://user-gold-cdn.xitu.io/2018/2/3/1615ae00b451c48b?w=320&h=568&f=jpeg&s=41339) ![](https://user-gold-cdn.xitu.io/2018/2/3/1615ae00ae746a05?w=320&h=568&f=jpeg&s=50929) ![](https://user-gold-cdn.xitu.io/2018/2/3/1615ae00ae26ce6c?w=320&h=568&f=jpeg&s=28894) ![](https://user-gold-cdn.xitu.io/2018/2/3/1615ae00aeb1419e?w=320&h=568&f=png&s=21729) ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 26 defaultConfig { applicationId "com.lqm.tomatoit" minSdkVersion 16 targetSdkVersion 26 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" //解决butterknife 在3.0.1问题 javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:26.0.2' implementation 'com.android.support.constraint:constraint-layout:1.0.2' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' //okrx2 compile 'com.lzy.net:okrx2:2.0.2' compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'com.readystatesoftware.chuck:library:1.0.4' compile 'com.android.support:design:26.0.2' compile 'com.android.support:recyclerview-v7:26.0.2' compile 'com.android.support:cardview-v7:26.0.2' compile 'com.jakewharton:butterknife:7.0.1' compile 'com.github.bumptech.glide:glide:3.7.0' compile 'com.google.code.gson:gson:2.8.0' compile 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.30' compile 'com.zhy:autolayout:1.4.5' //轮播图库 compile 'cn.bingoogolapple:bga-banner:2.1.7@aar' } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile ================================================ FILE: app/src/androidTest/java/com/lqm/tomatoit/ExampleInstrumentedTest.java ================================================ package com.lqm.tomatoit; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumented test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() throws Exception { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("com.lqm.tomatoit", appContext.getPackageName()); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/com/lqm/tomatoit/api/WanService.java ================================================ package com.lqm.tomatoit.api; import com.lqm.tomatoit.app.AppConst; import com.lqm.tomatoit.helper.JsonConvert; import com.lqm.tomatoit.model.ResponseData; import com.lqm.tomatoit.model.pojo.BannerBean; import com.lqm.tomatoit.model.pojo.HotKeyBean; import com.lqm.tomatoit.model.pojo.UserBean; import com.lqm.tomatoit.model.pojoVO.ArticleListVO; import com.lqm.tomatoit.model.pojoVO.TypeTagVO; import com.lzy.okgo.OkGo; import com.lzy.okgo.cache.CacheMode; import com.lzy.okrx2.adapter.ObservableBody; import java.util.List; import io.reactivex.Observable; /** * user:lqm * desc:玩Android提供的api接口 * www.wanandroid.com */ public class WanService { private static String homeDataList = AppConst.BASE_URL + "article/list/{page}/json"; private static String homeBannerData = AppConst.BASE_URL + "banner/json"; private static String hotKeyUrl = AppConst.BASE_URL + "hotkey/json"; private static String loginUrl = AppConst.BASE_URL + "user/login"; private static String registUrl = AppConst.BASE_URL + "user/register"; private static String getClollectData = AppConst.BASE_URL + "lg/collect/list/0/json"; /** * 首页Banner * * @GET("/banner/json") */ public static Observable>> getBannerData() { return OkGo.>>get(homeBannerData) .cacheMode(CacheMode.FIRST_CACHE_THEN_REQUEST) .converter(new JsonConvert>>() { }) .adapt(new ObservableBody>>()); } /** * 首页数据 * http://www.wanandroid.com/article/list/0/json * * @param page * @GET("/article/list/{page}/json") */ public static Observable> getHomeData(int page) { String url = AppConst.BASE_URL + "article/list/" + page + "/json"; return OkGo.>get(url) .cacheMode(CacheMode.FIRST_CACHE_THEN_REQUEST) //使用缓存 .converter(new JsonConvert>() { }) .adapt(new ObservableBody>()); } /** * 知识体系 (类别tag) * http://www.wanandroid.com/tree/json * * @GET("/tree/json") */ public static Observable>> getTypeTagData() { String url = AppConst.BASE_URL + "tree/json"; return OkGo.>>get(url) .converter(new JsonConvert>>() { }) .adapt(new ObservableBody>>()); } /** * 知识体系下的文章 * http://www.wanandroid.com/article/list/0/json?cid=168 * * @param page page * @param cid cid * @GET("/article/list/{page}/json") */ public static Observable> getTypeDataById(int page, int cid) { String url = AppConst.BASE_URL + "article/list/" + page + "/json"; return OkGo.>get(url) .params("cid", cid) .converter(new JsonConvert>() { }) .adapt(new ObservableBody>()); } /** * 大家都在搜 * http://www.wanandroid.com/hotkey/json * * @GET("/hotkey/json") */ public static Observable>> getHotKey() { return OkGo.>>get(hotKeyUrl) .converter(new JsonConvert>>() { }) .adapt(new ObservableBody>>()); } /** * 搜索 * http://www.wanandroid.com/article/query/0/json * @param page page * @param k POST search key * @POST("/article/query/{page}/json") */ public static Observable> searchArticle(int page,String key) { String url = AppConst.BASE_URL + "article/query/" + page + "/json"; return OkGo.>post(url) .params("k",key) .converter(new JsonConvert>() { }) .adapt(new ObservableBody>()); } /** * 登录 * @param username username * @param password password * @POST("/user/login") */ public static Observable> login(String username, String password) { return OkGo.>post(loginUrl) .params("username", username) .params("password", password) .converter(new JsonConvert>() { }) .adapt(new ObservableBody>()); } /** * 注册 * * @param username username * @param password password * @param repassword 确认密码 * @POST("/user/register") */ public static Observable> regist(String username, String password) { return OkGo.>post(registUrl) .params("username", username) .params("password", password) .params("repassword", password) .converter(new JsonConvert>() { }) .adapt(new ObservableBody>()); } /** * 获取自己收藏的文章列表 * * @param page page * @GET("/lg/collect/list/{page}/json") */ public static Observable> getCollectData(int page) { String url = AppConst.BASE_URL + "lg/collect/list/" + page + "/json"; return OkGo.> get(url) .converter(new JsonConvert>() { }) .adapt(new ObservableBody>()); } /** * 收藏文章 * * @param id id * @POST("/lg/collect/{id}/json") */ public static Observable> collectArticle(int id) { String url = AppConst.BASE_URL + "lg/collect/" + id + "/json"; return OkGo.> post(url) .converter(new JsonConvert>() { }) .adapt(new ObservableBody>()); } /** * 取消收藏文章 * * @param id id * POST("/lg/uncollect/{id}/json") * http://www.wanandroid.com/lg/uncollect/99/json * POST 参数:originId:1165,没有则写-1 * (注意获取首页接口的时候接口没有返回originId字段,获取我的收藏接口的时候有返回originId字段, * 收藏接口的id 跟 首页接口的id 是不一样的,收藏接口的originId 跟首页接口的id 一样, * 这个接口有点神奇啊,url也不一样) */ public static Observable> unCollectArticle(int id,int originId, boolean hasOriginId) { String url; if (hasOriginId){ url = AppConst.BASE_URL + "lg/uncollect/" + id + "/json"; }else{ url = AppConst.BASE_URL + "lg/uncollect_originId/" + id + "/json"; } if (originId == 0){ originId = -1; } return OkGo.> post(url) .params("originId",originId) .converter(new JsonConvert>() { }) .adapt(new ObservableBody>()); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/app/App.java ================================================ package com.lqm.tomatoit.app; import android.app.Activity; import android.app.Application; import android.content.Context; import com.lzy.okgo.OkGo; import com.lzy.okgo.cache.CacheEntity; import com.lzy.okgo.cache.CacheMode; import com.lzy.okgo.cookie.CookieJarImpl; import com.lzy.okgo.cookie.store.SPCookieStore; import com.lzy.okgo.interceptor.HttpLoggingInterceptor; import java.util.LinkedList; import java.util.List; import java.util.logging.Level; import okhttp3.OkHttpClient; /** * user:lqm * desc: */ public class App extends Application { public static List activities = new LinkedList<>(); private static Context mContext; @Override public void onCreate() { super.onCreate(); mContext = this.getApplicationContext(); initOkGo(); initAutoLayout(); } /** * 配置AutoLayout */ private void initAutoLayout() { //默认使用的高度是设备的可用高度,也就是不包括状态栏和底部的操作栏的,以下配置可以拿到设备的物理高度进行百分比 // AutoLayoutConifg.getInstance().useDeviceSize(); } /** * 初始化okgo */ private void initOkGo() { OkHttpClient.Builder builder = new OkHttpClient.Builder(); //使用sp保持cookie,如果cookie不过期,则一直有效 builder.cookieJar(new CookieJarImpl(new SPCookieStore(this))); HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor("OkGo"); loggingInterceptor.setPrintLevel(HttpLoggingInterceptor.Level.BODY); //log颜色级别,决定了log在控制台显示的颜色 loggingInterceptor.setColorLevel(Level.INFO); builder.addInterceptor(loggingInterceptor); // builder.addInterceptor(new TokenInterceptor()); OkGo.getInstance() .init(this) .setOkHttpClient(builder.build()) //设置OkHttpClient,不设置将使用默认的 .setCacheMode(CacheMode.NO_CACHE) .setCacheTime(CacheEntity.CACHE_NEVER_EXPIRE) .setRetryCount(3); } public static Context getmContext() { return mContext; } /** * 退出程序 */ public static void exit() { for (Activity activity : activities) { activity.finish(); } } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/app/AppConst.java ================================================ package com.lqm.tomatoit.app; /** * user:lqm * desc:App全局常量 */ public class AppConst { public static final String BASE_URL = "http://wanandroid.com/"; public static final String IS_LOGIN_KEY = "isLogin"; public static final String USERNAME_KEY = "userName"; } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/helper/Convert.java ================================================ /* * Copyright 2016 jeasonlzy(廖子尧) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.lqm.tomatoit.helper; import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonIOException; import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; import com.google.gson.stream.JsonReader; import com.readystatesoftware.chuck.internal.support.JsonConvertor; import java.io.Reader; import java.lang.reflect.Type; /** * ================================================ * 作 者:jeasonlzy(廖子尧)Github地址:https://github.com/jeasonlzy * 版 本:1.0 * 创建日期:16/9/28 * 描 述: Gson 数据转换工具类 * 修订历史: * ================================================ */ public class Convert { private static Gson create() { return Convert.GsonHolder.gson; } private static class GsonHolder { private static Gson gson = new Gson(); } public static T fromJson(String json, Class type) throws JsonIOException, JsonSyntaxException { return create().fromJson(json, type); } public static T fromJson(String json, Type type) { return create().fromJson(json, type); } public static T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException { return create().fromJson(reader, typeOfT); } public static T fromJson(Reader json, Class classOfT) throws JsonSyntaxException, JsonIOException { return create().fromJson(json, classOfT); } public static T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException { return create().fromJson(json, typeOfT); } public static String toJson(Object src) { return create().toJson(src); } public static String toJson(Object src, Type typeOfSrc) { return create().toJson(src, typeOfSrc); } public static String formatJson(String json) { try { JsonParser jp = new JsonParser(); JsonElement je = jp.parse(json); return JsonConvertor.getInstance().toJson(je); } catch (Exception e) { return json; } } public static String formatJson(Object src) { try { JsonParser jp = new JsonParser(); JsonElement je = jp.parse(toJson(src)); return JsonConvertor.getInstance().toJson(je); } catch (Exception e) { return e.getMessage(); } } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/helper/JsonConvert.java ================================================ /* * Copyright 2016 jeasonlzy(廖子尧) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.lqm.tomatoit.helper; import com.google.gson.stream.JsonReader; import com.lzy.okgo.convert.Converter; import org.json.JSONArray; import org.json.JSONObject; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import okhttp3.Response; import okhttp3.ResponseBody; /** * ================================================ * 作 者:jeasonlzy(廖子尧)Github地址:https://github.com/jeasonlzy * 版 本:1.0 * 创建日期:16/9/11 * 描 述: * 修订历史: * ================================================ */ public class JsonConvert implements Converter { private Type type; private Class clazz; public JsonConvert() { } public JsonConvert(Type type) { this.type = type; } public JsonConvert(Class clazz) { this.clazz = clazz; } /** * 该方法是子线程处理,不能做ui相关的工作 * 主要作用是解析网络返回的 response 对象,生成onSuccess回调中需要的数据对象 * 这里的解析工作不同的业务逻辑基本都不一样,所以需要自己实现,以下给出的时模板代码,实际使用根据需要修改 */ @Override public T convertResponse(Response response) throws Throwable { // 重要的事情说三遍,不同的业务,这里的代码逻辑都不一样,如果你不修改,那么基本不可用 // 重要的事情说三遍,不同的业务,这里的代码逻辑都不一样,如果你不修改,那么基本不可用 // 重要的事情说三遍,不同的业务,这里的代码逻辑都不一样,如果你不修改,那么基本不可用 // 如果你对这里的代码原理不清楚,可以看这里的详细原理说明: https://github.com/jeasonlzy/okhttp-OkGo/wiki/JsonCallback // 如果你对这里的代码原理不清楚,可以看这里的详细原理说明: https://github.com/jeasonlzy/okhttp-OkGo/wiki/JsonCallback // 如果你对这里的代码原理不清楚,可以看这里的详细原理说明: https://github.com/jeasonlzy/okhttp-OkGo/wiki/JsonCallback if (type == null) { if (clazz == null) { // 如果没有通过构造函数传进来,就自动解析父类泛型的真实类型(有局限性,继承后就无法解析到) Type genType = getClass().getGenericSuperclass(); type = ((ParameterizedType) genType).getActualTypeArguments()[0]; } else { return parseClass(response, clazz); } } if (type instanceof ParameterizedType) { return parseParameterizedType(response, (ParameterizedType) type); } else if (type instanceof Class) { return parseClass(response, (Class) type); } else { return parseType(response, type); } } private T parseClass(Response response, Class rawType) throws Exception { if (rawType == null) return null; ResponseBody body = response.body(); if (body == null) return null; JsonReader jsonReader = new JsonReader(body.charStream()); if (rawType == String.class) { //noinspection unchecked return (T) body.string(); } else if (rawType == JSONObject.class) { //noinspection unchecked return (T) new JSONObject(body.string()); } else if (rawType == JSONArray.class) { //noinspection unchecked return (T) new JSONArray(body.string()); } else { T t = Convert.fromJson(jsonReader, rawType); response.close(); return t; } } private T parseType(Response response, Type type) throws Exception { if (type == null) return null; ResponseBody body = response.body(); if (body == null) return null; JsonReader jsonReader = new JsonReader(body.charStream()); // 泛型格式如下: new JsonCallback<任意JavaBean>(this) T t = Convert.fromJson(jsonReader, type); response.close(); return t; } private T parseParameterizedType(Response response, ParameterizedType type) throws Exception { if (type == null) return null; ResponseBody body = response.body(); if (body == null) return null; JsonReader jsonReader = new JsonReader(body.charStream()); Type rawType = type.getRawType(); // 泛型的实际类型 Type typeArgument = type.getActualTypeArguments()[0]; // 泛型的参数 if (rawType != LzyResponse.class) { // 泛型格式如下: new JsonCallback<外层BaseBean<内层JavaBean>>(this) T t = Convert.fromJson(jsonReader, type); response.close(); return t; } else { if (typeArgument == Void.class) { // 泛型格式如下: new JsonCallback>(this) SimpleResponse simpleResponse = Convert.fromJson(jsonReader, SimpleResponse.class); response.close(); //noinspection unchecked return (T) simpleResponse.toLzyResponse(); } else { // 泛型格式如下: new JsonCallback>(this) LzyResponse lzyResponse = Convert.fromJson(jsonReader, type); response.close(); int code = lzyResponse.code; //这里的0是以下意思 //一般来说服务器会和客户端约定一个数表示成功,其余的表示失败,这里根据实际情况修改 if (code == 0) { //noinspection unchecked return (T) lzyResponse; } else if (code == 104) { throw new IllegalStateException("用户授权信息无效"); } else if (code == 105) { throw new IllegalStateException("用户收取信息已过期"); } else { //直接将服务端的错误信息抛出,onError中可以获取 throw new IllegalStateException("错误代码:" + code + ",错误信息:" + lzyResponse.msg); } } } } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/helper/LzyResponse.java ================================================ /* * Copyright 2016 jeasonlzy(廖子尧) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.lqm.tomatoit.helper; import java.io.Serializable; /** * ================================================ * 作 者:jeasonlzy(廖子尧)Github地址:https://github.com/jeasonlzy * 版 本:1.0 * 创建日期:16/9/28 * 描 述: * 修订历史: * ================================================ */ public class LzyResponse implements Serializable { private static final long serialVersionUID = 5213230387175987834L; public int code; public String msg; public T data; @Override public String toString() { return "LzyResponse{\n" +// "\tcode=" + code + "\n" +// "\tmsg='" + msg + "\'\n" +// "\tdata=" + data + "\n" +// '}'; } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/helper/SimpleResponse.java ================================================ /* * Copyright 2016 jeasonlzy(廖子尧) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.lqm.tomatoit.helper; import java.io.Serializable; /** * ================================================ * 作 者:jeasonlzy(廖子尧)Github地址:https://github.com/jeasonlzy * 版 本:1.0 * 创建日期:16/9/28 * 描 述: * 修订历史: * ================================================ */ public class SimpleResponse implements Serializable { private static final long serialVersionUID = -1477609349345966116L; public int code; public String msg; public LzyResponse toLzyResponse() { LzyResponse lzyResponse = new LzyResponse(); lzyResponse.code = code; lzyResponse.msg = msg; return lzyResponse; } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/helper/interceptor/TokenInterceptor.java ================================================ package com.lqm.tomatoit.helper.interceptor; import android.util.Log; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import java.nio.charset.Charset; import okhttp3.Interceptor; import okhttp3.MediaType; import okhttp3.Request; import okhttp3.Response; import okhttp3.ResponseBody; import okio.Buffer; import okio.BufferedSource; /** * user:lqm * desc:token验证令牌失效拦截器,token过期时刷新token或弹dialog跳转到登录界面 * src:https://www.jianshu.com/p/62ab11ddacc8 */ public class TokenInterceptor implements Interceptor { private static final Charset UTF8 = Charset.forName("UTF-8"); @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); // try the request Response originalResponse = chain.proceed(request); /**通过如下的办法曲线取到请求完成的数据 * * 原本想通过 originalResponse.body().string() * 去取到请求完成的数据,但是一直报错,不知道是okhttp的bug还是操作不当 * * 然后去看了okhttp的源码,找到了这个曲线方法,取到请求完成的数据后,根据特定的判断条件去判断token过期 */ ResponseBody responseBody = originalResponse.body(); BufferedSource source = responseBody.source(); source.request(Long.MAX_VALUE); // Buffer the entire body. Buffer buffer = source.buffer(); Charset charset = UTF8; MediaType contentType = responseBody.contentType(); if (contentType != null) { charset = contentType.charset(UTF8); } String bodyString = buffer.clone().readString(charset); Log.d("body---------->", bodyString); /***************************************/ JSONObject extrasJson = null; try { if (extrasJson == null){ extrasJson = new JSONObject(bodyString); } } catch (JSONException e) { e.printStackTrace(); } int code = Integer.parseInt(extrasJson.optString("errorCode")); //根据后台返回数据执行修改 // if (response shows expired token){//根据和服务端的约定判断token过期 if (code == 401){ //假设服务端返回码401为token过期 // TODO 弹出全局的dialog或者用以下代码刷新token (全局dialog可以用WindowManager相应实现) //取出本地的refreshToken String refreshToken = "sssgr122222222"; // 通过一个特定的接口获取新的token,此处要用到同步的retrofit请求 // ApiService service = ServiceManager.getService(ApiService.class); // Call call = service.refreshToken(refreshToken); //要用retrofit的同步方式 // String newToken = call.execute().body(); String newToken = "sssssssss ne wtoken"; // create a new request and modify it accordingly using the new token Request newRequest = request.newBuilder().header("token", newToken) .build(); // retry the request originalResponse.body().close(); return chain.proceed(newRequest); } // otherwise just pass the original response on return originalResponse; } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/helper/rxjavahelper/RxObserver.java ================================================ package com.lqm.tomatoit.helper.rxjavahelper; import io.reactivex.Observer; import io.reactivex.disposables.Disposable; /** * user:lqm * desc:自己的Observer,减少实现不必要的回调 */ public abstract class RxObserver implements Observer { @Override public void onSubscribe(Disposable d){ _onSubscribe(d); } @Override public void onNext(T t) { _onNext(t); } @Override public void onError(Throwable e) { _onError(e.getMessage()); } @Override public void onComplete() { _onComplete(); } public void _onSubscribe(Disposable d) { } public void _onComplete() { } //抽象方法,必须实现 public abstract void _onNext(T t); public abstract void _onError(String errorMessage); } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/helper/rxjavahelper/RxResultHelper.java ================================================ package com.lqm.tomatoit.helper.rxjavahelper; import com.lqm.tomatoit.model.ResponseData; import io.reactivex.Observable; import io.reactivex.ObservableSource; import io.reactivex.ObservableTransformer; import io.reactivex.functions.Function; /** * user:lqm * desc:Rx处理服务器返回, * 服务器的返回的数据格式一般都是一致的,所有我们每个网络请求都可以使 * 用compose(RxResultHelper.handleResult())来处理服务器返回,一般服务器返回成功码为200, * 相应改一下返回码的判断就行了 */ public class RxResultHelper { private static final int RESPONSE_SUCCESS_CODE = 0; //大部分为200 private static final int RESPONSE_ERROR_CODE = -1; public static ObservableTransformer, T> handleResult() { return new ObservableTransformer, T>() { @Override public ObservableSource apply(Observable> tObservable) { return tObservable.flatMap( new Function, Observable>() { @Override public Observable apply(ResponseData tResponseData) throws Exception { //可以相应更改 if (tResponseData.getErrorCode() == RESPONSE_SUCCESS_CODE) { return Observable.just(tResponseData.getData()); } else if (tResponseData.getErrorCode() == RESPONSE_ERROR_CODE) { return Observable.error(new Exception(tResponseData.getErrorMsg())); } else { return Observable.empty(); } } } ); } }; } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/helper/rxjavahelper/RxSchedulersHelper.java ================================================ package com.lqm.tomatoit.helper.rxjavahelper; import io.reactivex.Observable; import io.reactivex.ObservableSource; import io.reactivex.ObservableTransformer; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; /** * user:lqm * desc:compose()里接收一个Transformer对象,ObservableTransformer * 可以通过它将一种类型的Observable转换成另一种类型的Observable。 * 现在.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) * 的地方可以用.compose(RxSchedulersHelper.io_main())代替。 */ public class RxSchedulersHelper { public static ObservableTransformer io_main() { return new ObservableTransformer() { @Override public ObservableSource apply(Observable upstream) { return upstream .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } }; } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/manager/ImageLoaderManager.java ================================================ package com.lqm.tomatoit.manager; import android.content.Context; import android.widget.ImageView; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.request.FutureTarget; import com.lqm.tomatoit.R; import java.io.File; /** * @user lqm * @desc 图片加载管理 */ public class ImageLoaderManager { public static void LoadImage(Context context, String imgUrl, ImageView imageView) { Glide.with(context) .load(imgUrl) .placeholder(R.mipmap.default_img) .dontAnimate() //解决圆形图显示占位图问题 .error(R.mipmap.default_img) .diskCacheStrategy(DiskCacheStrategy.ALL) .into(imageView); } /** * 缓存图片到本地 */ public static File CacheFile(Context context, String imgUrl){ File cacheFile = null; FutureTarget future = Glide.with(context) .load(imgUrl) .downloadOnly(500, 500); try { cacheFile = future.get(); } catch (Exception e) { e.printStackTrace(); } return cacheFile; } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/model/ResponseData.java ================================================ package com.lqm.tomatoit.model; import java.io.Serializable; public class ResponseData implements Serializable { private static final long serialVersionUID = 5213230387175987834L; private int errorCode; private String errorMsg; private T data; public int getErrorCode() { return errorCode; } public void setErrorCode(int errorCode) { this.errorCode = errorCode; } public String getErrorMsg() { return errorMsg; } public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/model/pojo/ArticleBean.java ================================================ package com.lqm.tomatoit.model.pojo; import java.io.Serializable; /** * user:lqm * desc: */ public class ArticleBean implements Serializable { /** * id : 2207 * title : 慢啄的Xposed文章 * chapterId : 239 * chapterName : Xposed * envelopePic : null * link : https://www.wrbug.com/categories/xposed%E5%BC%80%E5%8F%91/ * author : 慢啄 * origin : null * publishTime : 1516332536000 * zan : null * desc : null * visible : 1 * niceDate : 2018-01-19 * courseId : 13 * collect : false */ private int id; private String title; private int chapterId; private String chapterName; private Object envelopePic; private String link; private String author; private Object origin; private int originId; private long publishTime; private Object zan; private Object desc; private int visible; private String niceDate; private int courseId; private boolean collect; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getChapterId() { return chapterId; } public void setChapterId(int chapterId) { this.chapterId = chapterId; } public String getChapterName() { return chapterName; } public void setChapterName(String chapterName) { this.chapterName = chapterName; } public Object getEnvelopePic() { return envelopePic; } public void setEnvelopePic(Object envelopePic) { this.envelopePic = envelopePic; } public String getLink() { return link; } public void setLink(String link) { this.link = link; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public Object getOrigin() { return origin; } public void setOrigin(Object origin) { this.origin = origin; } public int getOriginId() { return originId; } public void setOriginId(int originId) { this.originId = originId; } public long getPublishTime() { return publishTime; } public void setPublishTime(long publishTime) { this.publishTime = publishTime; } public Object getZan() { return zan; } public void setZan(Object zan) { this.zan = zan; } public Object getDesc() { return desc; } public void setDesc(Object desc) { this.desc = desc; } public int getVisible() { return visible; } public void setVisible(int visible) { this.visible = visible; } public String getNiceDate() { return niceDate; } public void setNiceDate(String niceDate) { this.niceDate = niceDate; } public int getCourseId() { return courseId; } public void setCourseId(int courseId) { this.courseId = courseId; } public boolean isCollect() { return collect; } public void setCollect(boolean collect) { this.collect = collect; } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/model/pojo/BannerBean.java ================================================ package com.lqm.tomatoit.model.pojo; import java.io.Serializable; /** * user:lqm * desc:首页轮播图 */ public class BannerBean implements Serializable { /** * id : 6 * url : http://www.wanandroid.com/navi * imagePath : http://www.wanandroid.com/blogimgs/62c1bd68-b5f3-4a3c-a649-7ca8c7dfabe6.png * title : 我们新增了一个常用导航Tab~ * desc : * isVisible : 1 * order : 1 * type : 0 */ private int id; private String url; private String imagePath; private String title; private String desc; private int isVisible; private int order; private int type; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getImagePath() { return imagePath; } public void setImagePath(String imagePath) { this.imagePath = imagePath; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public int getIsVisible() { return isVisible; } public void setIsVisible(int isVisible) { this.isVisible = isVisible; } public int getOrder() { return order; } public void setOrder(int order) { this.order = order; } public int getType() { return type; } public void setType(int type) { this.type = type; } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/model/pojo/HotKeyBean.java ================================================ package com.lqm.tomatoit.model.pojo; import java.io.Serializable; /** * user:lqm * desc:热词 */ public class HotKeyBean implements Serializable { /** * id : 6 * name : 面试 * link : null * visible : 1 * order : 1 */ private int id; private String name; private String link; private int visible; private int order; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getLink() { return link; } public void setLink(String link) { this.link = link; } public int getVisible() { return visible; } public void setVisible(int visible) { this.visible = visible; } public int getOrder() { return order; } public void setOrder(int order) { this.order = order; } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/model/pojo/TypeChildrenBean.java ================================================ package com.lqm.tomatoit.model.pojo; import java.io.Serializable; import java.util.List; /** * user:lqm * desc:Type Tag 二级标签 */ public class TypeChildrenBean implements Serializable { /** * id : 60 * name : Android Studio相关 * courseId : 13 * parentChapterId : 150 * order : 1000 * visible : 1 * children : [] */ private int id; private String name; private int courseId; private int parentChapterId; private int order; private int visible; private List children; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getCourseId() { return courseId; } public void setCourseId(int courseId) { this.courseId = courseId; } public int getParentChapterId() { return parentChapterId; } public void setParentChapterId(int parentChapterId) { this.parentChapterId = parentChapterId; } public int getOrder() { return order; } public void setOrder(int order) { this.order = order; } public int getVisible() { return visible; } public void setVisible(int visible) { this.visible = visible; } public List getChildren() { return children; } public void setChildren(List children) { this.children = children; } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/model/pojo/UserBean.java ================================================ package com.lqm.tomatoit.model.pojo; import java.io.Serializable; import java.util.List; /** * user:lqm * desc:登录注册成功 */ public class UserBean implements Serializable { /** * id : 1847 * username : 猥琐的豆腐6 * password : 123456 * icon : null * type : 0 * collectIds : [2239] */ private int id; private String username; private String password; private Object icon; private int type; private List collectIds; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Object getIcon() { return icon; } public void setIcon(Object icon) { this.icon = icon; } public int getType() { return type; } public void setType(int type) { this.type = type; } public List getCollectIds() { return collectIds; } public void setCollectIds(List collectIds) { this.collectIds = collectIds; } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/model/pojoVO/ArticleListVO.java ================================================ package com.lqm.tomatoit.model.pojoVO; import com.lqm.tomatoit.model.pojo.ArticleBean; import java.io.Serializable; import java.util.List; /** * user:lqm * desc:获取一个文章列表(首页,分类,收藏 等通用) */ public class ArticleListVO implements Serializable { /** * datas : [{"id":2207,"title":"慢啄的Xposed文章","chapterId":239,"chapterName":"Xposed","envelopePic":null,"link":"https://www.wrbug.com/categories/xposed%E5%BC%80%E5%8F%91/","author":"慢啄","origin":null,"publishTime":1516332536000,"zan":null,"desc":null,"visible":1,"niceDate":"2018-01-19","courseId":13,"collect":false},{"id":2206,"title":"QMUI Android","chapterId":301,"chapterName":"快速开发","envelopePic":null,"link":"http://qmuiteam.com/android/page/index.html","author":"小编","origin":null,"publishTime":1516328190000,"zan":null,"desc":null,"visible":1,"niceDate":"2018-01-19","courseId":13,"collect":false},{"id":2205,"title":"awesome-design","chapterId":299,"chapterName":"创意&素材","envelopePic":null,"link":"https://github.com/gztchan/awesome-design/","author":"小编","origin":null,"publishTime":1516289555000,"zan":null,"desc":null,"visible":1,"niceDate":"2018-01-18","courseId":13,"collect":false},{"id":2204,"title":"Material Design设计模板与素材","chapterId":299,"chapterName":"创意&素材","envelopePic":null,"link":"https://www.uplabs.com/android","author":"小编","origin":null,"publishTime":1516289539000,"zan":null,"desc":null,"visible":1,"niceDate":"2018-01-18","courseId":13,"collect":false},{"id":2203,"title":"iconstore","chapterId":299,"chapterName":"创意&素材","envelopePic":null,"link":"https://iconstore.co/","author":"小编","origin":null,"publishTime":1516289486000,"zan":null,"desc":null,"visible":1,"niceDate":"2018-01-18","courseId":13,"collect":false},{"id":2202,"title":"iconfont","chapterId":299,"chapterName":"创意&素材","envelopePic":null,"link":"http://www.iconfont.cn/","author":"小编","origin":null,"publishTime":1516289466000,"zan":null,"desc":null,"visible":1,"niceDate":"2018-01-18","courseId":13,"collect":false},{"id":2201,"title":"《广研Android卡顿监控系统》","chapterId":78,"chapterName":"性能优化","envelopePic":null,"link":"https://mp.weixin.qq.com/s/MthGj4AwFPL2JrZ0x1i4fw","author":"腾讯Bugly","origin":null,"publishTime":1516286424000,"zan":null,"desc":null,"visible":1,"niceDate":"2018-01-18","courseId":13,"collect":false},{"id":2200,"title":"WebView截长图解决方案","chapterId":98,"chapterName":"WebView","envelopePic":null,"link":"https://www.jianshu.com/p/0faa70e88441","author":"贝聊科技","origin":null,"publishTime":1516280223000,"zan":null,"desc":null,"visible":1,"niceDate":"2018-01-18","courseId":13,"collect":false},{"id":2199,"title":"【Xposed】Android-Hook初探","chapterId":239,"chapterName":"Xposed","envelopePic":null,"link":"http://liompei.com/2018/01/02/%E3%80%90Xposed%E3%80%91Android-Hook%E5%88%9D%E6%8E%A2/","author":"Liompei","origin":null,"publishTime":1516276750000,"zan":null,"desc":null,"visible":1,"niceDate":"2018-01-18","courseId":13,"collect":false},{"id":2198,"title":"Xposed开发初体验","chapterId":239,"chapterName":"Xposed","envelopePic":null,"link":"http://www.jowanxu.top/2017/09/21/Xposed%E5%BC%80%E5%8F%91%E5%88%9D%E4%BD%93%E9%AA%8C/","author":"叫我旺仔","origin":null,"publishTime":1516275347000,"zan":null,"desc":null,"visible":1,"niceDate":"2018-01-18","courseId":13,"collect":false},{"id":2197,"title":"叫我旺仔","chapterId":274,"chapterName":"个人博客","envelopePic":null,"link":"http://www.jowanxu.top/archives/","author":"小编","origin":null,"publishTime":1516275071000,"zan":null,"desc":null,"visible":1,"niceDate":"2018-01-18","courseId":13,"collect":false},{"id":2196,"title":"Android Studio 掌握这些调试技巧,Debug能力不能再高啦","chapterId":60,"chapterName":"Android Studio相关","envelopePic":null,"link":"https://www.jianshu.com/p/985f788fae2c","author":"亦枫","origin":null,"publishTime":1516270173000,"zan":null,"desc":null,"visible":1,"niceDate":"2018-01-18","courseId":13,"collect":false},{"id":2194,"title":"百度移动统计","chapterId":300,"chapterName":"互联网统计","envelopePic":null,"link":"https://mtj.baidu.com/data/mobile/device","author":"小编","origin":null,"publishTime":1516266460000,"zan":null,"desc":null,"visible":1,"niceDate":"2018-01-18","courseId":13,"collect":false},{"id":2193,"title":"猎豹大数据","chapterId":300,"chapterName":"互联网统计","envelopePic":null,"link":"http://cn.data.cmcm.com/rank","author":"小编","origin":null,"publishTime":1516266420000,"zan":null,"desc":null,"visible":1,"niceDate":"2018-01-18","courseId":13,"collect":false},{"id":2192,"title":"无版权素材站","chapterId":299,"chapterName":"创意&素材","envelopePic":null,"link":"https://unsplash.com/","author":"小编","origin":null,"publishTime":1516252267000,"zan":null,"desc":null,"visible":1,"niceDate":"2018-01-18","courseId":13,"collect":false},{"id":2189,"title":"Android 混淆查缺补漏","chapterId":295,"chapterName":"混淆","envelopePic":null,"link":"https://mp.weixin.qq.com/s/g7AxmLHvJTX-JyGqs4SaHg","author":"Othershe","origin":null,"publishTime":1516241162000,"zan":null,"desc":null,"visible":1,"niceDate":"2018-01-18","courseId":13,"collect":false},{"id":2188,"title":"Android实现修改状态栏背景 字体 图标颜色","chapterId":186,"chapterName":"沉浸式","envelopePic":null,"link":"https://juejin.im/post/5a30f1535188251c11409d77","author":"jlanglang","origin":null,"publishTime":1516203303000,"zan":null,"desc":null,"visible":1,"niceDate":"2018-01-17","courseId":13,"collect":false},{"id":2187,"title":"搭建Android上的服务器 实现隔空取物","chapterId":298,"chapterName":"我的博客","envelopePic":null,"link":"http://www.wanandroid.com/blog/show/2020","author":"鸿洋","origin":null,"publishTime":1516153307000,"zan":null,"desc":null,"visible":1,"niceDate":"2018-01-17","courseId":13,"collect":false},{"id":2088,"title":"Xposed 框架解析","chapterId":239,"chapterName":"Xposed","envelopePic":null,"link":"https://www.jianshu.com/p/2b8343c774df","author":"王永迪 ","origin":null,"publishTime":1516094972000,"zan":null,"desc":null,"visible":1,"niceDate":"2018-01-16","courseId":13,"collect":false},{"id":985,"title":"Android Studio 使用Gradle多渠道打包","chapterId":169,"chapterName":"gradle","envelopePic":"","link":"https://mp.weixin.qq.com/s/yKfesG8lodfhJVA-rPfyRg","author":"loonggg","origin":"非著名程序员","publishTime":1516003287000,"zan":0,"desc":"我们都知道国内应用市场非常多,为了统计各个应用市场的app下载量和使用情况,我们需要多渠道的打包。如果一个一个的手动去打包岂不烦死了,要多麻烦就有多麻烦。这就要求我们学会使用Gradle进行多渠道打包。","visible":1,"niceDate":"2018-01-15","courseId":13,"collect":false}] * offset : 20 * size : 20 * total : 1014 * pageCount : 51 * curPage : 2 * over : false */ private int offset; private int size; private int total; private int pageCount; private int curPage; private boolean over; private List datas; public int getOffset() { return offset; } public void setOffset(int offset) { this.offset = offset; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } public int getTotal() { return total; } public void setTotal(int total) { this.total = total; } public int getPageCount() { return pageCount; } public void setPageCount(int pageCount) { this.pageCount = pageCount; } public int getCurPage() { return curPage; } public void setCurPage(int curPage) { this.curPage = curPage; } public boolean isOver() { return over; } public void setOver(boolean over) { this.over = over; } public List getDatas() { return datas; } public void setDatas(List datas) { this.datas = datas; } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/model/pojoVO/TypeTagVO.java ================================================ package com.lqm.tomatoit.model.pojoVO; import com.lqm.tomatoit.model.pojo.TypeChildrenBean; import java.io.Serializable; import java.util.List; /** * user:lqm * desc:分类标签 */ public class TypeTagVO implements Serializable { /** * id : 150 * name : 开发环境 * courseId : 13 * parentChapterId : 0 * order : 1 * visible : 1 * children : [{"id":60,"name":"Android Studio相关","courseId":13,"parentChapterId":150,"order":1000,"visible":1,"children":[]},{"id":169,"name":"gradle","courseId":13,"parentChapterId":150,"order":1001,"visible":1,"children":[]},{"id":269,"name":"官方发布","courseId":13,"parentChapterId":150,"order":1002,"visible":1,"children":[]}] */ private int id; private String name; private int courseId; private int parentChapterId; private int order; private int visible; private List children; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getCourseId() { return courseId; } public void setCourseId(int courseId) { this.courseId = courseId; } public int getParentChapterId() { return parentChapterId; } public void setParentChapterId(int parentChapterId) { this.parentChapterId = parentChapterId; } public int getOrder() { return order; } public void setOrder(int order) { this.order = order; } public int getVisible() { return visible; } public void setVisible(int visible) { this.visible = visible; } public List getChildren() { return children; } public void setChildren(List children) { this.children = children; } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/activity/AboutActivity.java ================================================ package com.lqm.tomatoit.ui.activity; import android.widget.TextView; import com.lqm.tomatoit.R; import com.lqm.tomatoit.ui.base.BaseActivity; import com.lqm.tomatoit.ui.base.BasePresenter; import com.lqm.tomatoit.widget.IconFontTextView; import butterknife.Bind; import butterknife.OnClick; /** * user:lqm * desc:关于我们界面 */ public class AboutActivity extends BaseActivity { @Bind(R.id.tv_return) IconFontTextView tvReturn; @Bind(R.id.tv_title) TextView tvTitle; @Bind(R.id.tv_other) IconFontTextView tvOther; @Override protected BasePresenter createPresenter() { return null; } @Override protected int provideContentViewId() { return R.layout.activity_about; } @Override public void initView() { tvTitle.setText("关于我们"); } @OnClick(R.id.tv_return) public void onViewClicked() { finish(); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/activity/CollectActivity.java ================================================ package com.lqm.tomatoit.ui.activity; import android.support.design.widget.Snackbar; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.TextView; import com.chad.library.adapter.base.BaseQuickAdapter; import com.lqm.tomatoit.R; import com.lqm.tomatoit.model.pojo.ArticleBean; import com.lqm.tomatoit.ui.adapter.CollectArticleAdapter; import com.lqm.tomatoit.ui.base.BaseActivity; import com.lqm.tomatoit.ui.presenter.CollectPresenter; import com.lqm.tomatoit.ui.view.CollectView; import com.lqm.tomatoit.widget.IconFontTextView; import java.util.List; import butterknife.Bind; import butterknife.OnClick; /** * user:lqm * desc:我的收藏界面 */ public class CollectActivity extends BaseActivity implements CollectView, SwipeRefreshLayout.OnRefreshListener, BaseQuickAdapter.RequestLoadMoreListener { @Bind(R.id.tv_return) IconFontTextView tvReturn; @Bind(R.id.tv_title) TextView tvTitle; @Bind(R.id.tv_no_collect) TextView tvNoCollect; @Bind(R.id.tv_other) IconFontTextView tvOther; @Bind(R.id.rv_content) RecyclerView rvContent; @Bind(R.id.swipe_refresh) SwipeRefreshLayout swipeRefresh; private CollectArticleAdapter mAdapter; @Override protected CollectPresenter createPresenter() { return new CollectPresenter(); } @Override protected int provideContentViewId() { return R.layout.activity_collect; } @Override public void initView() { tvTitle.setText("我的收藏"); rvContent.setLayoutManager(new LinearLayoutManager(CollectActivity.this)); mAdapter = new CollectArticleAdapter(CollectActivity.this, null); rvContent.setAdapter(mAdapter); swipeRefresh.setOnRefreshListener(this); mAdapter.setOnLoadMoreListener(this,rvContent); onRefresh(); } @Override public void onRefresh() { mPresenter.getRefreshData(); } @Override public void onLoadMoreRequested() { mPresenter.getMoreData(); } @OnClick(R.id.tv_return) public void onViewClicked() { finish(); } @Override public void onRefreshSuccess(List data) { mAdapter.setNewData(data); tvNoCollect.setVisibility(data.size() ==0? View.VISIBLE:View.GONE); } @Override public void onRefreshFail(String errorString) { showSwipeRefresh(false); Snackbar.make(rvContent, errorString, Snackbar.LENGTH_SHORT).show(); } @Override public void onLoadMoreSuccess(List data) { if (data.size() == 0) { mAdapter.loadMoreEnd(); } else { mAdapter.addData(data); mAdapter.loadMoreComplete(); } } @Override public void onLoadMoreFail(String errorString) { showSwipeRefresh(false); mAdapter.loadMoreComplete(); Snackbar.make(rvContent, errorString, Snackbar.LENGTH_SHORT).show(); } @Override public void loadComplete() { showSwipeRefresh(false); } private void showSwipeRefresh(boolean isRefresh) { swipeRefresh.post(new Runnable() { @Override public void run() { swipeRefresh.setRefreshing(isRefresh); } }); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/activity/LoginActivity.java ================================================ package com.lqm.tomatoit.ui.activity; import android.content.Intent; import android.text.TextUtils; import android.view.View; import android.widget.Button; import android.widget.EditText; import com.lqm.tomatoit.R; import com.lqm.tomatoit.app.AppConst; import com.lqm.tomatoit.model.pojo.UserBean; import com.lqm.tomatoit.ui.base.BaseActivity; import com.lqm.tomatoit.ui.presenter.LoginRegistPresenter; import com.lqm.tomatoit.ui.view.LoginRegistView; import com.lqm.tomatoit.util.PrefUtils; import com.lqm.tomatoit.util.T; import com.lqm.tomatoit.widget.IconFontTextView; import butterknife.Bind; import butterknife.OnClick; /** * user:lqm * desc:登录注册界面 */ public class LoginActivity extends BaseActivity implements LoginRegistView { @Bind(R.id.ic_close) IconFontTextView icClose; @Bind(R.id.et_name) EditText etName; @Bind(R.id.et_password) EditText etPassword; @Bind(R.id.btn_regist) Button btnRegist; @Bind(R.id.btn_login) Button btnLogin; @Override protected LoginRegistPresenter createPresenter() { return new LoginRegistPresenter(); } @Override protected int provideContentViewId() { return R.layout.activity_login; } @OnClick({R.id.ic_close, R.id.btn_regist, R.id.btn_login}) public void onViewClicked(View view) { switch (view.getId()) { case R.id.ic_close: finish(); break; case R.id.btn_regist: if (TextUtils.isEmpty(etName.getText().toString()) || TextUtils.isEmpty(etPassword.getText().toString())){ T.showShort(LoginActivity.this,"用户名和密码不能为空"); }else if (etName.getText().toString().length() < 6 || etName.getText().toString().length() <6){ T.showShort(LoginActivity.this,"用户名和密码长度不能小于6位"); }else{ mPresenter.toRegist(etName.getText().toString(),etPassword.getText().toString()); } break; case R.id.btn_login: if (TextUtils.isEmpty(etName.getText().toString()) || TextUtils.isEmpty(etPassword.getText().toString())){ T.showShort(LoginActivity.this,"用户名和密码不能为空"); }else if (etName.getText().toString().length() < 6 || etName.getText().toString().length() <6){ T.showShort(LoginActivity.this,"用户名和密码长度不能小于6位"); }else{ mPresenter.toLogin(etName.getText().toString(),etPassword.getText().toString()); } break; } } @Override public void showProgress(String tipString) { showWaitingDialog(tipString); } @Override public void hideProgress() { hideWaitingDialog(); } @Override public void loginSuccess(UserBean user) { PrefUtils.setBoolean(LoginActivity.this,AppConst.IS_LOGIN_KEY,true); PrefUtils.setString(LoginActivity.this,AppConst.USERNAME_KEY,etName.getText().toString()); startActivity(new Intent(LoginActivity.this,MainActivity.class)); finish(); } @Override public void registerSuccess(UserBean user) { T.showShort(LoginActivity.this,"注册成功"); PrefUtils.setBoolean(LoginActivity.this, AppConst.IS_LOGIN_KEY,true); PrefUtils.setString(LoginActivity.this,AppConst.USERNAME_KEY,etName.getText().toString()); startActivity(new Intent(LoginActivity.this,MainActivity.class)); finish(); } @Override public void loginFail() { T.showShort(LoginActivity.this,"登录失败"); } @Override public void registerFail() { T.showShort(LoginActivity.this,"注册失败"); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/activity/MainActivity.java ================================================ package com.lqm.tomatoit.ui.activity; import android.content.Intent; import android.support.v4.app.Fragment; import android.support.v4.view.ViewPager; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; import com.lqm.tomatoit.R; import com.lqm.tomatoit.ui.adapter.FragPagerAdapter; import com.lqm.tomatoit.ui.base.BaseActivity; import com.lqm.tomatoit.ui.base.BasePresenter; import com.lqm.tomatoit.ui.fragment.HomeFragment; import com.lqm.tomatoit.ui.fragment.TypeFragment; import com.lqm.tomatoit.ui.fragment.UserFragment; import com.lqm.tomatoit.util.UIUtils; import com.lqm.tomatoit.widget.IconFontTextView; import java.util.ArrayList; import java.util.List; import butterknife.Bind; import butterknife.OnClick; public class MainActivity extends BaseActivity { @Bind(R.id.viewpager) ViewPager viewPager; @Bind(R.id.if_home) IconFontTextView ifHome; @Bind(R.id.tv_home) TextView tvHome; @Bind(R.id.ll_home) LinearLayout llHome; @Bind(R.id.if_type) IconFontTextView ifType; @Bind(R.id.tv_type) TextView tvType; @Bind(R.id.ll_type) LinearLayout llType; @Bind(R.id.if_user) IconFontTextView ifUser; @Bind(R.id.tv_user) TextView tvUser; @Bind(R.id.ll_user) LinearLayout llUser; @Bind(R.id.tv_hot) IconFontTextView tvHot; @Bind(R.id.tv_search) IconFontTextView tvSearch; private List mFragments = new ArrayList<>(); @Override protected BasePresenter createPresenter() { return null; } @Override protected int provideContentViewId() { return R.layout.activity_main; } @Override public void initView() { setTabColor(ifHome, tvHome); mFragments.add(HomeFragment.newInstance()); mFragments.add(TypeFragment.newInstance()); mFragments.add(UserFragment.newInstance()); viewPager.setAdapter(new FragPagerAdapter(getSupportFragmentManager(), mFragments)); viewPager.setCurrentItem(0, false); viewPager.setOffscreenPageLimit(3); viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { switch (position) { case 0: setTabColor(ifHome, tvHome); break; case 1: setTabColor(ifType, tvType); break; case 2: setTabColor(ifUser, tvUser); break; } } @Override public void onPageScrollStateChanged(int state) { } }); } @OnClick({R.id.ll_home, R.id.ll_type,R.id.ll_user, R.id.tv_hot, R.id.tv_search}) public void onViewClicked(View view) { switch (view.getId()) { case R.id.ll_home: viewPager.setCurrentItem(0); setTabColor(ifHome, tvHome); break; case R.id.ll_type: viewPager.setCurrentItem(1); setTabColor(ifType, tvType); break; case R.id.ll_user: viewPager.setCurrentItem(2); setTabColor(ifUser, tvUser); break; case R.id.tv_hot: break; case R.id.tv_search: startActivity(new Intent(MainActivity.this,SearchActivity.class)); break; } } private void setTabColor(IconFontTextView icon, TextView textView) { ifHome.setTextColor(UIUtils.getColor(R.color.tab_nor_color)); tvHome.setTextColor(UIUtils.getColor(R.color.tab_nor_color)); ifType.setTextColor(UIUtils.getColor(R.color.tab_nor_color)); tvType.setTextColor(UIUtils.getColor(R.color.tab_nor_color)); ifUser.setTextColor(UIUtils.getColor(R.color.tab_nor_color)); tvUser.setTextColor(UIUtils.getColor(R.color.tab_nor_color)); icon.setTextColor(UIUtils.getColor(R.color.tab_sel_color)); textView.setTextColor(UIUtils.getColor(R.color.tab_sel_color)); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/activity/SearchActivity.java ================================================ package com.lqm.tomatoit.ui.activity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.Editable; import android.text.Selection; import android.text.Spannable; import android.text.TextUtils; import android.text.TextWatcher; import android.view.View; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; import com.chad.library.adapter.base.BaseQuickAdapter; import com.lqm.tomatoit.R; import com.lqm.tomatoit.model.pojo.ArticleBean; import com.lqm.tomatoit.model.pojo.HotKeyBean; import com.lqm.tomatoit.ui.adapter.ArticleListAdapter; import com.lqm.tomatoit.ui.base.BaseActivity; import com.lqm.tomatoit.ui.presenter.SearchPresenter; import com.lqm.tomatoit.ui.view.SearchView; import com.lqm.tomatoit.util.T; import com.lqm.tomatoit.widget.AutoLinefeedLayout; import com.lqm.tomatoit.widget.IconFontTextView; import java.util.List; import butterknife.Bind; import butterknife.OnClick; /** * user:lqm * desc:搜索界面 */ public class SearchActivity extends BaseActivity implements SearchView, BaseQuickAdapter.RequestLoadMoreListener { @Bind(R.id.et_search) EditText etSearch; @Bind(R.id.rl_search) RelativeLayout rlSearch; @Bind(R.id.tv_return) TextView tvReturn; @Bind(R.id.tv_clean_input) IconFontTextView tvCleanInput; @Bind(R.id.layout_hot_key) AutoLinefeedLayout hotKeyLayout; @Bind(R.id.ll_hot_key) LinearLayout llHotKey; @Bind(R.id.rv_content) RecyclerView rvContent; private ArticleListAdapter mArticleAdapter; @Override protected SearchPresenter createPresenter() { return new SearchPresenter(); } @Override protected int provideContentViewId() { return R.layout.activity_search; } @Override public void initView() { rvContent.setLayoutManager(new LinearLayoutManager(SearchActivity.this)); mArticleAdapter = new ArticleListAdapter(SearchActivity.this, null); rvContent.setAdapter(mArticleAdapter); mArticleAdapter.setOnLoadMoreListener(this, rvContent); mPresenter.getHotKeyData(); } @Override public void initListener() { etSearch.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { mPresenter.searchData(etSearch.getText().toString()); } }); } @OnClick({R.id.tv_clean_input, R.id.tv_return}) public void onViewClicked(View view) { switch (view.getId()) { case R.id.tv_clean_input: etSearch.setText(""); break; case R.id.tv_return: finish(); break; } } @Override public void getHotKeySuccess(List data) { hotKeyLayout.removeAllViews(); for (int i = 0; i < data.size(); i++) { View view = LinearLayout.inflate(SearchActivity.this, R.layout.item_hot_key, null); TextView textView = (TextView) view.findViewById(R.id.textview); textView.setText(data.get(i).getName()); hotKeyLayout.addView(view); int finalI = i; view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { etSearch.setText(data.get(finalI).getName()); // 将光标移至字符串尾部 CharSequence charSequence = etSearch.getText(); if (charSequence instanceof Spannable) { Spannable spanText = (Spannable) charSequence; Selection.setSelection(spanText, charSequence.length()); } } }); } } @Override public void getHotKeyFail(String message) { T.showShort(SearchActivity.this, message); } @Override public void searchDataSuccess(List data) { if (data == null || data.size() == 0) { llHotKey.setVisibility(View.VISIBLE); rvContent.setVisibility(View.GONE); } else { llHotKey.setVisibility(View.GONE); rvContent.setVisibility(View.VISIBLE); } mArticleAdapter.setNewData(data); } @Override public void searchDataFail(String message) { T.showShort(SearchActivity.this, message); } @Override public void loadMoreDataSuccess(List data) { if (data.size() == 0) { mArticleAdapter.loadMoreEnd(); } else { mArticleAdapter.addData(data); mArticleAdapter.loadMoreComplete(); } } @Override public void loadMoreDataFail(String message) { T.showShort(SearchActivity.this, message); } @Override public void onLoadMoreRequested() { String keyWord = etSearch.getText().toString(); if (!TextUtils.isEmpty(keyWord)) { mPresenter.getMoreData(etSearch.getText().toString()); } } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/activity/WebViewActivity.java ================================================ package com.lqm.tomatoit.ui.activity; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.support.design.widget.Snackbar; import android.support.v4.widget.NestedScrollView; import android.view.KeyEvent; import android.view.View; import android.webkit.WebView; import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; import com.lqm.tomatoit.R; import com.lqm.tomatoit.ui.base.BaseActivity; import com.lqm.tomatoit.ui.presenter.WebViewPresenter; import com.lqm.tomatoit.ui.view.CommonWebView; import com.lqm.tomatoit.util.ActivityUtils; import com.lqm.tomatoit.util.SharesUtils; import com.lqm.tomatoit.util.UIUtils; import com.lqm.tomatoit.widget.CustomPopWindow; import com.lqm.tomatoit.widget.IconFontTextView; import com.lqm.tomatoit.widget.WebViewFragment; import butterknife.Bind; import butterknife.OnClick; /** * user:lqm * desc:WebView界面,加载文章详情等 * (tip:上滑隐藏topbar,因为webview焦点问题,这里在布局中用NestedScrollView,再动态添加webview) */ public class WebViewActivity extends BaseActivity implements CommonWebView { public static final String WEB_URL = "web_url"; @Bind(R.id.progress_bar) ProgressBar progressBar; @Bind(R.id.tv_return) IconFontTextView tvReturn; @Bind(R.id.tv_title) TextView tvTitle; @Bind(R.id.tv_other) IconFontTextView tvOther; @Bind(R.id.rl_topbar_layout) RelativeLayout rlTopbarLayout; @Bind(R.id.webview_container) NestedScrollView webViewContainer; private String mUrl; private CustomPopWindow mMorePopWindow; private WebViewFragment mWebViewFragment; private WebView webView; public static void runActivity(Context context, String url) { Intent intent = new Intent(context, WebViewActivity.class); intent.putExtra(WEB_URL, url); context.startActivity(intent); } @Override protected WebViewPresenter createPresenter() { return new WebViewPresenter(); } @Override protected int provideContentViewId() { return R.layout.activity_webview; } @Override public ProgressBar getProgressBar() { return progressBar; } @Override public void setTitle(String title) { tvTitle.setText(title); } @Override public void init() { mUrl = getIntent().getStringExtra(WEB_URL); } @Override public void initView() { tvOther.setText(UIUtils.getString(R.string.ic_more)); mWebViewFragment = new WebViewFragment(); ActivityUtils.addFragmentToActivity(getSupportFragmentManager(), mWebViewFragment, R.id.webview_container); } @Override protected void onStart() { super.onStart(); //获取webview webView = mWebViewFragment.getWebView(); mPresenter.setWebView(webView, mUrl); } @Override protected void onDestroy() { super.onDestroy(); webView.destroy(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if (webView.canGoBack()) { webView.goBack(); return true; } else { finish();//退出程序 return true; } } return super.onKeyDown(keyCode, event); } @OnClick({R.id.tv_return, R.id.tv_other}) public void onViewClicked(View view) { switch (view.getId()) { case R.id.tv_return: finish(); break; case R.id.tv_other: //更多按钮 View popView = View.inflate(WebViewActivity.this, R.layout.popup_webview_more, null); mMorePopWindow = new CustomPopWindow.PopupWindowBuilder(this) .setView(popView) .enableBackgroundDark(false) //弹出popWindow时,背景是否变暗 .create() .showAsDropDown(tvOther, -430, -10); //分享 popView.findViewById(R.id.tv_shape).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { SharesUtils.share(WebViewActivity.this, webView.getUrl()); mMorePopWindow.dissmiss(); } }); //复制链接 popView.findViewById(R.id.tv_copy_link).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ClipboardManager cmd = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); cmd.setPrimaryClip(ClipData.newPlainText(getString(R.string.copy_link), webView.getUrl())); Snackbar.make(getWindow().getDecorView(), R.string.copy_link_success, Snackbar.LENGTH_SHORT).show(); mMorePopWindow.dissmiss(); } }); //使用系统浏览器打开 popView.findViewById(R.id.tv_open_out).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(webView.getUrl())); startActivity(intent); mMorePopWindow.dissmiss(); } }); break; } } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/adapter/ArticleListAdapter.java ================================================ package com.lqm.tomatoit.ui.adapter; import android.content.Context; import android.support.annotation.Nullable; import android.text.Html; import android.view.View; import android.widget.TextView; import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import com.lqm.tomatoit.R; import com.lqm.tomatoit.api.WanService; import com.lqm.tomatoit.app.AppConst; import com.lqm.tomatoit.helper.rxjavahelper.RxObserver; import com.lqm.tomatoit.helper.rxjavahelper.RxResultHelper; import com.lqm.tomatoit.helper.rxjavahelper.RxSchedulersHelper; import com.lqm.tomatoit.model.ResponseData; import com.lqm.tomatoit.model.pojo.ArticleBean; import com.lqm.tomatoit.ui.activity.WebViewActivity; import com.lqm.tomatoit.util.PrefUtils; import com.lqm.tomatoit.util.T; import com.lqm.tomatoit.util.UIUtils; import java.util.List; import io.reactivex.Observer; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; /** * user:lqm * desc:文章列表适配器 */ public class ArticleListAdapter extends BaseQuickAdapter { private Context mContext; public ArticleListAdapter(Context context, @Nullable List data) { super(R.layout.item_article, data); mContext = context; } @Override protected void convert(final BaseViewHolder holder, final ArticleBean bean) { holder.setText(R.id.tv_title, Html.fromHtml(bean.getTitle())) .setText(R.id.tv_author, bean.getAuthor()) .setText(R.id.tv_time, bean.getNiceDate()) .setText(R.id.tv_type, bean.getChapterName()); TextView tvCollect = (TextView) holder.getView(R.id.tv_collect); if (bean.isCollect()) { tvCollect.setText(UIUtils.getString(R.string.ic_collect_sel)); tvCollect.setTextColor(UIUtils.getColor(R.color.main)); } else { tvCollect.setText(UIUtils.getString(R.string.ic_collect_nor)); tvCollect.setTextColor(UIUtils.getColor(R.color.text3)); } tvCollect.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { collectArticle(tvCollect, bean); } }); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { WebViewActivity.runActivity(mContext, bean.getLink()); } }); } //收藏文章 private void collectArticle(TextView tvCollect, ArticleBean bean) { if (PrefUtils.getBoolean(mContext, AppConst.IS_LOGIN_KEY, false) == false) { T.showShort(mContext, "请先登录"); return; } if (bean.isCollect()) { //已经收藏,点击取消收藏 unCollectArticler(bean, tvCollect); } else { collectArticler(bean, tvCollect); } } private void collectArticler(ArticleBean bean, TextView tvCollect) { WanService.collectArticle(bean.getId()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(ResponseData responseData) { if (responseData.getErrorCode() == 0) { T.showShort(mContext, "收藏成功"); bean.setCollect(true); tvCollect.setText(UIUtils.getString(R.string.ic_collect_sel)); tvCollect.setTextColor(UIUtils.getColor(R.color.main)); } else { T.showShort(mContext, responseData.getErrorMsg()); } } @Override public void onError(Throwable e) { T.showShort(mContext, "收藏失败"); } @Override public void onComplete() { } }); } private void unCollectArticler(ArticleBean bean, TextView tvCollect) { WanService.unCollectArticle(bean.getId(), bean.getOriginId(), false) .compose(RxSchedulersHelper.io_main()) .compose(RxResultHelper.handleResult()) .subscribe(new RxObserver() { @Override public void _onNext(String s) { T.showShort(mContext, "取消收藏"); bean.setCollect(false); tvCollect.setText(UIUtils.getString(R.string.ic_collect_nor)); tvCollect.setTextColor(UIUtils.getColor(R.color.text3)); } @Override public void _onError(String errorMessage) { T.showShort(mContext, "取消收藏失败"); } }); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/adapter/CollectArticleAdapter.java ================================================ package com.lqm.tomatoit.ui.adapter; import android.content.Context; import android.support.annotation.Nullable; import android.view.View; import android.widget.TextView; import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import com.lqm.tomatoit.R; import com.lqm.tomatoit.api.WanService; import com.lqm.tomatoit.helper.rxjavahelper.RxObserver; import com.lqm.tomatoit.helper.rxjavahelper.RxResultHelper; import com.lqm.tomatoit.helper.rxjavahelper.RxSchedulersHelper; import com.lqm.tomatoit.model.pojo.ArticleBean; import com.lqm.tomatoit.ui.activity.WebViewActivity; import com.lqm.tomatoit.util.T; import com.lqm.tomatoit.util.UIUtils; import java.util.List; /** * user:lqm * desc:我的收藏文章列表适配器 * (这个适配器跟ArticleListAdapter一样的,但是接口收藏列表接口返回的数据collect字段有问题,所以这里分开) */ public class CollectArticleAdapter extends BaseQuickAdapter { private Context mContext; public CollectArticleAdapter(Context context, @Nullable List data) { super(R.layout.item_article, data); mContext = context; } @Override protected void convert(final BaseViewHolder holder, final ArticleBean bean) { holder.setText(R.id.tv_title, bean.getTitle()) .setText(R.id.tv_author, bean.getAuthor()) .setText(R.id.tv_time, bean.getNiceDate()) .setText(R.id.tv_type, bean.getChapterName()); TextView tvCollect = (TextView) holder.getView(R.id.tv_collect); tvCollect.setText(UIUtils.getString(R.string.ic_collect_sel)); tvCollect.setTextColor(UIUtils.getColor(R.color.main)); tvCollect.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { unCollectArticler(holder.getLayoutPosition(), bean); } }); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { WebViewActivity.runActivity(mContext, bean.getLink()); } }); } private void unCollectArticler(int position, ArticleBean bean) { WanService.unCollectArticle(bean.getId(),bean.getOriginId(),true) .compose(RxSchedulersHelper.io_main()) .compose(RxResultHelper.handleResult()) .subscribe(new RxObserver() { @Override public void _onNext(String s) { T.showShort(mContext, "取消收藏"); getData().remove(position); if (position != 0){ notifyItemRemoved(position); }else{ notifyDataSetChanged(); } } @Override public void _onError(String errorMessage) { T.showShort(mContext, "取消收藏失败"); } }); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/adapter/FragPagerAdapter.java ================================================ package com.lqm.tomatoit.ui.adapter; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import java.util.List; /** * @user lqm * @desc ViewPager+Fragment 的适配器 */ public class FragPagerAdapter extends FragmentPagerAdapter { private List mFragments; public FragPagerAdapter(FragmentManager fm, List fragments) { super(fm); mFragments = fragments; } @Override public Fragment getItem(int position) { return mFragments.get(position); } @Override public int getCount() { return mFragments.size(); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/base/BaseActivity.java ================================================ package com.lqm.tomatoit.ui.base; import android.app.Dialog; import android.os.Bundle; import android.support.annotation.Nullable; import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.widget.TextView; import com.lqm.tomatoit.R; import com.lqm.tomatoit.app.App; import com.lqm.tomatoit.widget.CustomDialog; import com.zhy.autolayout.AutoLayoutActivity; import butterknife.ButterKnife; public abstract class BaseActivity> extends AutoLayoutActivity { protected T mPresenter; private CustomDialog mDialogWaiting; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); App.activities.add(this); init(); //判断是否使用MVP模式 mPresenter = createPresenter(); if (mPresenter != null) { mPresenter.attachView((V) this);//因为之后所有的子类都要实现对应的View接口 } //子类不再需要设置布局ID,也不再需要使用ButterKnife.bind() setContentView(provideContentViewId()); ButterKnife.bind(this); excuteStatesBar(); initView(); initData(); initListener(); } /** * 解决4.4设置状态栏颜色之后,布局内容嵌入状态栏位置问题 */ private void excuteStatesBar(){ ViewGroup mContentView = (ViewGroup) getWindow().findViewById(Window.ID_ANDROID_CONTENT); View mChildView = mContentView.getChildAt(0); if (mChildView != null) { //注意不是设置 ContentView 的 FitsSystemWindows, // 而是设置 ContentView 的第一个子 View ,预留出系统 View 的空间. mChildView.setFitsSystemWindows(true); } } @Override protected void onDestroy() { super.onDestroy(); if (mPresenter != null) { mPresenter.detachView(); } } //在setContentView()调用之前调用,可以设置WindowFeature(如:this.requestWindowFeature(Window.FEATURE_NO_TITLE);) public void init() { } public void initView() { } public void initData() { } public void initListener() { } //用于创建Presenter和判断是否使用MVP模式(由子类实现) protected abstract T createPresenter(); //得到当前界面的布局文件id(由子类实现) protected abstract int provideContentViewId(); /** * 显示等待提示框 */ public Dialog showWaitingDialog(String tip) { hideWaitingDialog(); View view = View.inflate(this, R.layout.dialog_waiting, null); if (!TextUtils.isEmpty(tip)) ((TextView) view.findViewById(R.id.tvTip)).setText(tip); mDialogWaiting = new CustomDialog(this, view, R.style.MyDialog); mDialogWaiting.show(); mDialogWaiting.setCancelable(false); return mDialogWaiting; } /** * 隐藏等待提示框 */ public void hideWaitingDialog() { if (mDialogWaiting != null) { mDialogWaiting.dismiss(); mDialogWaiting = null; } } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/base/BaseFragment.java ================================================ package com.lqm.tomatoit.ui.base; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import butterknife.ButterKnife; import io.reactivex.annotations.Nullable; /** * user:lqm * desc:BaseFragment */ public abstract class BaseFragment> extends Fragment { protected T mPresenter; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); init(); //判断是否使用MVP模式 mPresenter = createPresenter(); if (mPresenter != null) { mPresenter.attachView((V) this);//因为之后所有的子类都要实现对应的View接口 } } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View rootView = inflater.inflate(provideContentViewId(), container, false); ButterKnife.bind(this, rootView); initView(rootView); return rootView; } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); initData(); initListener(); } @Override public void onDestroy() { super.onDestroy(); if (mPresenter != null) { mPresenter.detachView(); } } public void init() { } public void initView(View rootView) { } public void initData() { } public void initListener() { } //用于创建Presenter和判断是否使用MVP模式(由子类实现) protected abstract T createPresenter(); //得到当前界面的布局文件id(由子类实现) protected abstract int provideContentViewId(); } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/base/BasePresenter.java ================================================ package com.lqm.tomatoit.ui.base; import java.lang.ref.Reference; import java.lang.ref.WeakReference; /** * @user lqm * @desc BasePresenter */ public abstract class BasePresenter { protected Reference mViewRef; public void attachView(V view){ mViewRef = new WeakReference(view); } protected V getView(){ return mViewRef.get(); } public boolean isViewAttached(){ return mViewRef != null&&mViewRef.get()!=null; } public void detachView(){ if(mViewRef!=null){ mViewRef.clear(); mViewRef = null; } } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/fragment/HomeFragment.java ================================================ package com.lqm.tomatoit.ui.fragment; import android.support.design.widget.Snackbar; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.ImageView; import com.chad.library.adapter.base.BaseQuickAdapter; import com.lqm.tomatoit.R; import com.lqm.tomatoit.manager.ImageLoaderManager; import com.lqm.tomatoit.model.pojo.ArticleBean; import com.lqm.tomatoit.model.pojo.BannerBean; import com.lqm.tomatoit.ui.activity.WebViewActivity; import com.lqm.tomatoit.ui.adapter.ArticleListAdapter; import com.lqm.tomatoit.ui.base.BaseFragment; import com.lqm.tomatoit.ui.presenter.HomePresenter; import com.lqm.tomatoit.ui.view.HomeView; import java.util.List; import butterknife.Bind; import cn.bingoogolapple.bgabanner.BGABanner; /** * user:lqm * desc:第一个模块,主页Fragment */ public class HomeFragment extends BaseFragment implements HomeView, SwipeRefreshLayout.OnRefreshListener, BaseQuickAdapter.RequestLoadMoreListener { @Bind(R.id.rv_content) RecyclerView rvContent; @Bind(R.id.swipe_refresh) SwipeRefreshLayout swipeRefresh; private ArticleListAdapter mAdapter; private BGABanner mBannerView; public static HomeFragment newInstance() { return new HomeFragment(); } @Override protected HomePresenter createPresenter() { return new HomePresenter(); } @Override protected int provideContentViewId() { return R.layout.frag_home; } @Override public void onRefresh() { mPresenter.getBannerData(); mPresenter.getRefreshData(); } @Override public void onLoadMoreRequested() { mPresenter.getMoreData(); } @Override public void showRefreshView(final Boolean refresh) { swipeRefresh.post(new Runnable() { @Override public void run() { swipeRefresh.setRefreshing(refresh); } }); } @Override public void getBannerDataSuccess(List bannerData) { //设置轮播图 mBannerView.setData(R.layout.item_banner, bannerData, null); mBannerView.setAdapter(new BGABanner.Adapter() { @Override public void fillBannerItem(BGABanner banner, View itemView, BannerBean model, int position) { ImageView imageView = (ImageView) itemView.findViewById(R.id.imageView); ImageLoaderManager.LoadImage(getContext(), model.getImagePath(), imageView); } }); mBannerView.setDelegate(new BGABanner.Delegate() { @Override public void onBannerItemClick(BGABanner banner, View itemView, BannerBean model, int position) { WebViewActivity.runActivity(getContext(), model.getUrl()); } }); } @Override public void initView(View rootView) { rvContent.setLayoutManager(new LinearLayoutManager(getContext())); mAdapter = new ArticleListAdapter(getContext(), null); rvContent.setAdapter(mAdapter); swipeRefresh.setOnRefreshListener(this); mAdapter.setOnLoadMoreListener(this, rvContent); //添加头部轮播图布局 View headView = View.inflate(getActivity(), R.layout.layout_banner, null); mBannerView = (BGABanner) headView.findViewById(R.id.banner); mAdapter.addHeaderView(headView); onRefresh(); } @Override public void initData() { mPresenter.getBannerData(); } @Override public void getDataError(String message) { showRefreshView(false); Snackbar.make(rvContent, message, Snackbar.LENGTH_SHORT).show(); } @Override public void getMoreDataSuccess(List data) { if (data.size() != 0) { mAdapter.addData(data); mAdapter.loadMoreComplete(); } else { mAdapter.loadMoreEnd(); } } @Override public void getRefreshDataSuccess(List data) { mAdapter.setNewData(data); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/fragment/TypeFragment.java ================================================ package com.lqm.tomatoit.ui.fragment; import android.support.design.widget.Snackbar; import android.support.design.widget.TabLayout; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; import com.chad.library.adapter.base.BaseQuickAdapter; import com.lqm.tomatoit.R; import com.lqm.tomatoit.model.pojo.ArticleBean; import com.lqm.tomatoit.ui.adapter.ArticleListAdapter; import com.lqm.tomatoit.ui.base.BaseFragment; import com.lqm.tomatoit.ui.presenter.TypePresenter; import com.lqm.tomatoit.ui.view.TypeView; import com.lqm.tomatoit.widget.AutoLinefeedLayout; import com.zhy.autolayout.AutoLinearLayout; import java.util.List; import butterknife.Bind; /** * user:lqm * desc:第二个模块,分类Fragment */ public class TypeFragment extends BaseFragment implements TypeView, BaseQuickAdapter.RequestLoadMoreListener { @Bind(R.id.tabLayout) TabLayout tabLayout; @Bind(R.id.rv_content) RecyclerView rvContent; @Bind(R.id.ll_tag) AutoLinefeedLayout llTag; @Bind(R.id.layout_blank) AutoLinearLayout layoutBlank; private ArticleListAdapter mAdapter; public static TypeFragment newInstance() { return new TypeFragment(); } @Override protected TypePresenter createPresenter() { return new TypePresenter(getActivity()); } @Override protected int provideContentViewId() { return R.layout.frag_type; } @Override public void initView(View rootView) { rvContent.setLayoutManager(new LinearLayoutManager(getContext())); mAdapter = new ArticleListAdapter(getContext(),null); rvContent.setAdapter(mAdapter); mAdapter.setOnLoadMoreListener(this,rvContent); mPresenter.getTagData(); } @Override public void onLoadMoreRequested() { mPresenter.getMoreData(); } @Override public TabLayout getTabLayout() { return tabLayout; } @Override public AutoLinefeedLayout getTagLayout() { return llTag; } @Override public ArticleListAdapter getAdapter() { return mAdapter; } @Override public void getDataError(String message) { Snackbar.make(rvContent, message, Snackbar.LENGTH_SHORT).show(); } @Override public void getRefreshDataSuccess(List data) { mAdapter.setNewData(data); } @Override public void getMoreDataSuccess(List data) { if (data.size() != 0) { mAdapter.addData(data); mAdapter.loadMoreComplete(); } else { mAdapter.loadMoreEnd(); } } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/fragment/UserFragment.java ================================================ package com.lqm.tomatoit.ui.fragment; import android.content.Intent; import android.support.v7.widget.CardView; import android.view.View; import android.widget.TextView; import com.lqm.tomatoit.R; import com.lqm.tomatoit.app.AppConst; import com.lqm.tomatoit.ui.activity.AboutActivity; import com.lqm.tomatoit.ui.activity.CollectActivity; import com.lqm.tomatoit.ui.activity.LoginActivity; import com.lqm.tomatoit.ui.base.BaseFragment; import com.lqm.tomatoit.ui.base.BasePresenter; import com.lqm.tomatoit.util.PrefUtils; import com.lqm.tomatoit.util.T; import butterknife.Bind; import butterknife.OnClick; /** * user:lqm * desc:第三个模块,用户模块 */ public class UserFragment extends BaseFragment{ @Bind(R.id.tv_name) TextView tvName; @Bind(R.id.cv_collect) CardView cvCollect; @Bind(R.id.cv_about) CardView cvAbout; @Bind(R.id.cv_logou) CardView cvLogou; @Bind(R.id.tv_logou) TextView tvLogou; public static UserFragment newInstance() { return new UserFragment(); } @Override protected BasePresenter createPresenter() { return null; } @Override protected int provideContentViewId() { return R.layout.frag_user; } @Override public void initView(View rootView) { if (PrefUtils.getBoolean(getContext(), AppConst.IS_LOGIN_KEY,false) == false){ tvLogou.setText("点击登录"); tvName.setText("暂未登录"); }else{ tvName.setText(PrefUtils.getString(getContext(),AppConst.USERNAME_KEY,"暂未登录")); tvLogou.setText("退出登录"); } } @OnClick({R.id.cv_collect, R.id.cv_about, R.id.cv_logou}) public void onViewClicked(View view) { switch (view.getId()) { case R.id.cv_collect: if (PrefUtils.getBoolean(getContext(),AppConst.IS_LOGIN_KEY,false) == false){ T.showShort(getContext(),"请先登录"); }else{ startActivity(new Intent(getActivity(),CollectActivity.class)); } break; case R.id.cv_about: startActivity(new Intent(getActivity(),AboutActivity.class)); break; case R.id.cv_logou: if (PrefUtils.getBoolean(getContext(),AppConst.IS_LOGIN_KEY,false) == false){ startActivity(new Intent(getActivity(),LoginActivity.class)); }else{ //注销 PrefUtils.setBoolean(getContext(),AppConst.IS_LOGIN_KEY,false); T.showShort(getContext(),"已注销"); tvLogou.setText("点击登录"); tvName.setText("暂未登录"); } break; } } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/presenter/CollectPresenter.java ================================================ package com.lqm.tomatoit.ui.presenter; import com.lqm.tomatoit.api.WanService; import com.lqm.tomatoit.helper.rxjavahelper.RxObserver; import com.lqm.tomatoit.helper.rxjavahelper.RxResultHelper; import com.lqm.tomatoit.helper.rxjavahelper.RxSchedulersHelper; import com.lqm.tomatoit.model.pojoVO.ArticleListVO; import com.lqm.tomatoit.ui.base.BasePresenter; import com.lqm.tomatoit.ui.view.CollectView; /** * user:lqm * desc:我的收藏 */ public class CollectPresenter extends BasePresenter { private int mCurrentPage; //刷新获取数据 public void getRefreshData() { mCurrentPage = 0; WanService.getCollectData(mCurrentPage) .compose(RxSchedulersHelper.io_main()) .compose(RxResultHelper.handleResult()) .subscribe(new RxObserver() { @Override public void _onNext(ArticleListVO articleListVO) { getView().onRefreshSuccess(articleListVO.getDatas()); } @Override public void _onError(String errorMessage) { getView().onRefreshFail(errorMessage); } @Override public void _onComplete() { getView().loadComplete(); } }); } //获取更多数据 public void getMoreData() { mCurrentPage += 1; WanService.getCollectData(mCurrentPage) .compose(RxSchedulersHelper.io_main()) .compose(RxResultHelper.handleResult()) .subscribe(new RxObserver() { @Override public void _onNext(ArticleListVO articleListVO) { getView().onLoadMoreSuccess(articleListVO.getDatas()); } @Override public void _onError(String errorMessage) { getView().onLoadMoreFail(errorMessage); } }); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/presenter/HomePresenter.java ================================================ package com.lqm.tomatoit.ui.presenter; import com.lqm.tomatoit.api.WanService; import com.lqm.tomatoit.helper.rxjavahelper.RxObserver; import com.lqm.tomatoit.helper.rxjavahelper.RxResultHelper; import com.lqm.tomatoit.helper.rxjavahelper.RxSchedulersHelper; import com.lqm.tomatoit.model.pojo.BannerBean; import com.lqm.tomatoit.model.pojoVO.ArticleListVO; import com.lqm.tomatoit.ui.base.BasePresenter; import com.lqm.tomatoit.ui.view.HomeView; import java.util.List; import io.reactivex.disposables.Disposable; /** * user:lqm * desc:首页 */ public class HomePresenter extends BasePresenter { private int mCurrentPage; //刷新 public void getRefreshData() { mCurrentPage = 0; WanService.getHomeData(mCurrentPage) .compose(RxSchedulersHelper.io_main()) .compose(RxResultHelper.handleResult()) .subscribe(new RxObserver() { @Override public void _onSubscribe(Disposable d) { getView().showRefreshView(true); } @Override public void _onNext(ArticleListVO articleListVO) { getView().getRefreshDataSuccess(articleListVO.getDatas()); } @Override public void _onError(String message) { getView().getDataError(message); } @Override public void _onComplete() { getView().showRefreshView(false); } }); } //加载更多 public void getMoreData() { mCurrentPage = mCurrentPage + 1; WanService.getHomeData(mCurrentPage) .compose(RxSchedulersHelper.io_main()) .compose(RxResultHelper.handleResult()) .subscribe(new RxObserver() { @Override public void _onNext(ArticleListVO articleListVO) { getView().getMoreDataSuccess(articleListVO.getDatas()); } @Override public void _onError(String errorMessage) { getView().getDataError(errorMessage); } }); } //获取轮播图数据 public void getBannerData() { WanService.getBannerData() .compose(RxSchedulersHelper.io_main()) .compose(RxResultHelper.handleResult()) .subscribe(new RxObserver>() { @Override public void _onNext(List bannerBeans) { getView().getBannerDataSuccess(bannerBeans); } @Override public void _onError(String errorMessage) { getView().getDataError(errorMessage); } }); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/presenter/LoginRegistPresenter.java ================================================ package com.lqm.tomatoit.ui.presenter; import com.lqm.tomatoit.api.WanService; import com.lqm.tomatoit.helper.rxjavahelper.RxObserver; import com.lqm.tomatoit.helper.rxjavahelper.RxResultHelper; import com.lqm.tomatoit.helper.rxjavahelper.RxSchedulersHelper; import com.lqm.tomatoit.model.pojo.UserBean; import com.lqm.tomatoit.ui.base.BasePresenter; import com.lqm.tomatoit.ui.view.LoginRegistView; import io.reactivex.disposables.Disposable; /** * user:lqm * desc:登录注册 */ public class LoginRegistPresenter extends BasePresenter { //登录 public void toLogin(String username, String password) { WanService.login(username, password) .compose(RxSchedulersHelper.io_main()) .compose(RxResultHelper.handleResult()) .subscribe(new RxObserver() { @Override public void _onSubscribe(Disposable d) { getView().showProgress("正在登陆..."); } @Override public void _onNext(UserBean userBean) { getView().loginSuccess(userBean); } @Override public void _onError(String errorMessage) { getView().loginFail(); } @Override public void _onComplete() { getView().hideProgress(); } }); } //注册 public void toRegist(String username, String password) { WanService.regist(username, password) .compose(RxSchedulersHelper.io_main()) .compose(RxResultHelper.handleResult()) .subscribe(new RxObserver() { @Override public void _onSubscribe(Disposable d) { getView().showProgress("正在注册..."); } @Override public void _onNext(UserBean userBean) { getView().registerSuccess(userBean); } @Override public void _onError(String errorMessage) { getView().registerFail(); } @Override public void _onComplete() { getView().hideProgress(); } }); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/presenter/SearchPresenter.java ================================================ package com.lqm.tomatoit.ui.presenter; import com.lqm.tomatoit.api.WanService; import com.lqm.tomatoit.helper.rxjavahelper.RxObserver; import com.lqm.tomatoit.helper.rxjavahelper.RxResultHelper; import com.lqm.tomatoit.helper.rxjavahelper.RxSchedulersHelper; import com.lqm.tomatoit.model.pojo.HotKeyBean; import com.lqm.tomatoit.model.pojoVO.ArticleListVO; import com.lqm.tomatoit.ui.base.BasePresenter; import com.lqm.tomatoit.ui.view.SearchView; import java.util.List; /** * user:lqm * desc:搜索 */ public class SearchPresenter extends BasePresenter { private int mCurrentPage; //热门搜索 public void getHotKeyData() { WanService.getHotKey() .compose(RxSchedulersHelper.io_main()) .compose(RxResultHelper.handleResult()) .subscribe(new RxObserver>() { @Override public void _onNext(List hotKeyBeans) { getView().getHotKeySuccess(hotKeyBeans); } @Override public void _onError(String errorMessage) { getView().getHotKeyFail(errorMessage); } }); } //搜索 public void searchData(String key) { mCurrentPage = 0; WanService.searchArticle(mCurrentPage, key) .compose(RxSchedulersHelper.io_main()) .compose(RxResultHelper.handleResult()) .subscribe(new RxObserver() { @Override public void _onNext(ArticleListVO articleListVO) { getView().searchDataSuccess(articleListVO.getDatas()); } @Override public void _onError(String errorMessage) { getView().searchDataFail(errorMessage); } }); } //加载更多 public void getMoreData(String key) { mCurrentPage += 1; WanService.searchArticle(mCurrentPage, key) .compose(RxSchedulersHelper.io_main()) .compose(RxResultHelper.handleResult()) .subscribe(new RxObserver() { @Override public void _onNext(ArticleListVO articleListVO) { getView().loadMoreDataSuccess(articleListVO.getDatas()); } @Override public void _onError(String errorMessage) { getView().loadMoreDataFail(errorMessage); } }); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/presenter/TypePresenter.java ================================================ package com.lqm.tomatoit.ui.presenter; import android.support.design.widget.TabLayout; import android.support.v4.app.FragmentActivity; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; import com.lqm.tomatoit.R; import com.lqm.tomatoit.api.WanService; import com.lqm.tomatoit.helper.rxjavahelper.RxObserver; import com.lqm.tomatoit.helper.rxjavahelper.RxResultHelper; import com.lqm.tomatoit.helper.rxjavahelper.RxSchedulersHelper; import com.lqm.tomatoit.model.pojoVO.ArticleListVO; import com.lqm.tomatoit.model.pojoVO.TypeTagVO; import com.lqm.tomatoit.ui.adapter.ArticleListAdapter; import com.lqm.tomatoit.ui.base.BasePresenter; import com.lqm.tomatoit.ui.view.TypeView; import com.lqm.tomatoit.util.UIUtils; import com.lqm.tomatoit.widget.AutoLinefeedLayout; import java.util.ArrayList; import java.util.List; /** * user:lqm * desc:分类 */ public class TypePresenter extends BasePresenter { private FragmentActivity mActivity; private TypeView mTypeView; private int mCurrentPage; private List mTagDatas; private ArticleListAdapter mAdapter; private int mId; private int mTabSelect; //标记选中的Tab标签 private int mTagSelect; //标记选中的Tag标签,用户设置背景色 private List tagTvs; private AutoLinefeedLayout llTag; public TypePresenter(FragmentActivity activity) { this.mActivity = activity; } //分类标签 public void getTagData() { mTypeView = getView(); WanService.getTypeTagData() .compose(RxSchedulersHelper.io_main()) .compose(RxResultHelper.handleResult()) .subscribe(new RxObserver>() { @Override public void _onNext(List typeTagVOS) { mTagDatas = typeTagVOS; setTabUI(); mTabSelect = 0; mTagSelect = 0; getServerData(mTagDatas.get(0).getChildren().get(0).getId()); } @Override public void _onError(String errorMessage) { } }); } //一级Tab private void setTabUI() { TabLayout tabLayout = mTypeView.getTabLayout(); tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE); for (TypeTagVO bean : mTagDatas) { tabLayout.addTab(tabLayout.newTab().setText(bean.getName())); } tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { @Override public void onTabSelected(TabLayout.Tab tab) { setTagUI(tab.getPosition()); } @Override public void onTabUnselected(TabLayout.Tab tab) { } @Override public void onTabReselected(TabLayout.Tab tab) { if (llTag != null && llTag.getVisibility() == View.VISIBLE) { llTag.setVisibility(View.GONE); } else { setTagUI(tab.getPosition()); } } }); } //二级Tag private void setTagUI(int position) { llTag = mTypeView.getTagLayout(); llTag.setVisibility(View.VISIBLE); llTag.removeAllViews(); if (tagTvs == null) { tagTvs = new ArrayList<>(); } else { tagTvs.clear(); } for (int i = 0; i < mTagDatas.get(position).getChildren().size(); i++) { View view = LinearLayout.inflate(mActivity, R.layout.item_tag_layout, null); TextView tvItem = (TextView) view.findViewById(R.id.tv_item); tvItem.setText(mTagDatas.get(position).getChildren().get(i).getName()); llTag.addView(view); tagTvs.add(tvItem); int finalI = i; view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //点击某个tag按钮 mTabSelect = position; mTagSelect = finalI; tvItem.setTextColor(UIUtils.getColor(R.color.white)); tvItem.setBackgroundResource(R.drawable.shape_tag_sel); mId = mTagDatas.get(position).getChildren().get(finalI).getId(); getServerData(mId); } }); } //设置选中的tag背景 for (int j = 0; j < tagTvs.size(); j++) { if (position == mTabSelect && j == mTagSelect) { tagTvs.get(j).setTextColor(UIUtils.getColor(R.color.white)); tagTvs.get(j).setBackgroundResource(R.drawable.shape_tag_sel); } else { tagTvs.get(j).setTextColor(UIUtils.getColor(R.color.text0)); tagTvs.get(j).setBackgroundResource(R.drawable.shape_tag_nor); } } } //根据点击的标签获取数据 public void getServerData(int cid) { mCurrentPage = 0; //从第0页开始 mAdapter = mTypeView.getAdapter(); WanService.getTypeDataById(mCurrentPage, cid) .compose(RxSchedulersHelper.io_main()) .compose(RxResultHelper.handleResult()) .subscribe(new RxObserver() { @Override public void _onNext(ArticleListVO articleListVO) { if (articleListVO.getDatas() != null) { getView().getRefreshDataSuccess(articleListVO.getDatas()); mTypeView.getTagLayout().setVisibility(View.GONE); } } @Override public void _onError(String errorMessage) { getView().getDataError(errorMessage); } }); } //加载下一页 public void getMoreData() { mCurrentPage = mCurrentPage + 1; WanService.getTypeDataById(mCurrentPage, mId) .compose(RxSchedulersHelper.io_main()) .compose(RxResultHelper.handleResult()) .subscribe(new RxObserver() { @Override public void _onNext(ArticleListVO articleListVO) { getView().getMoreDataSuccess(articleListVO.getDatas()); } @Override public void _onError(String errorMessage) { getView().getDataError(errorMessage); } }); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/presenter/WebViewPresenter.java ================================================ package com.lqm.tomatoit.ui.presenter; import android.graphics.Bitmap; import android.view.View; import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.ProgressBar; import com.lqm.tomatoit.ui.base.BasePresenter; import com.lqm.tomatoit.ui.view.CommonWebView; /** * user:lqm * desc:WebView Presenter */ public class WebViewPresenter extends BasePresenter { public void setWebView(WebView webView,String url) { CommonWebView urlView = getView(); ProgressBar progressBar = urlView.getProgressBar(); WebSettings settings = webView.getSettings(); settings.setJavaScriptEnabled(true);// 支持JS settings.setBuiltInZoomControls(true);// 显示放大缩小按钮 settings.setUseWideViewPort(true);// 支持双击放大缩小 settings.setSupportZoom(true); settings.setJavaScriptCanOpenWindowsAutomatically(true); settings.setLoadWithOverviewMode(true); settings.setUseWideViewPort(true); settings.setDefaultTextEncodingName("utf-8"); settings.setLoadsImagesAutomatically(true); settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN); settings.setAppCacheEnabled(true); settings.setDomStorageEnabled(true); webView.setWebViewClient(new WebViewClient() { @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); System.out.println("网页开始加载"); progressBar.setVisibility(View.VISIBLE); } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); System.out.println("网页加载结束"); progressBar.setVisibility(View.GONE); } /** * 所有跳转的链接都在此方法中回调 */ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return super.shouldOverrideUrlLoading(view, url); } }); webView.setWebChromeClient(new WebChromeClient() { @Override public void onProgressChanged(WebView view, int newProgress) { super.onProgressChanged(view, newProgress); progressBar.setProgress(newProgress); } @Override public void onReceivedTitle(WebView view, String title) { urlView.setTitle(title); super.onReceivedTitle(view, title); } }); webView.loadUrl(url); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/view/CollectView.java ================================================ package com.lqm.tomatoit.ui.view; import com.lqm.tomatoit.model.pojo.ArticleBean; import java.util.List; /** * user:lqm * desc:我的收藏 */ public interface CollectView { void onRefreshSuccess(List data); void onRefreshFail(String errorString); void onLoadMoreSuccess(List data); void onLoadMoreFail(String errorString); void loadComplete(); } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/view/CommonWebView.java ================================================ package com.lqm.tomatoit.ui.view; import android.widget.ProgressBar; /** * user:lqm * desc: WebView */ public interface CommonWebView { ProgressBar getProgressBar(); void setTitle(String title); } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/view/HomeView.java ================================================ package com.lqm.tomatoit.ui.view; import com.lqm.tomatoit.model.pojo.ArticleBean; import com.lqm.tomatoit.model.pojo.BannerBean; import java.util.List; /** * user:lqm * desc:首页 */ public interface HomeView { void showRefreshView(Boolean refresh); void getBannerDataSuccess(List data); void getDataError(String message); void getRefreshDataSuccess(List data); void getMoreDataSuccess(List data); } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/view/LoginRegistView.java ================================================ package com.lqm.tomatoit.ui.view; import com.lqm.tomatoit.model.pojo.UserBean; /** * user:lqm * desc:登录注册 */ public interface LoginRegistView { void showProgress(String tipString); void hideProgress(); void loginSuccess(UserBean user); void registerSuccess(UserBean user); void loginFail(); void registerFail(); } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/view/SearchView.java ================================================ package com.lqm.tomatoit.ui.view; import com.lqm.tomatoit.model.pojo.ArticleBean; import com.lqm.tomatoit.model.pojo.HotKeyBean; import java.util.List; /** * user:lqm * desc:搜索 */ public interface SearchView { void getHotKeySuccess(List data); void getHotKeyFail(String message); void searchDataSuccess(List data); void searchDataFail(String message); void loadMoreDataSuccess(List data); void loadMoreDataFail(String message); } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/ui/view/TypeView.java ================================================ package com.lqm.tomatoit.ui.view; import android.support.design.widget.TabLayout; import com.lqm.tomatoit.model.pojo.ArticleBean; import com.lqm.tomatoit.ui.adapter.ArticleListAdapter; import com.lqm.tomatoit.widget.AutoLinefeedLayout; import java.util.List; /** * user:lqm * desc:分类View */ public interface TypeView { TabLayout getTabLayout(); AutoLinefeedLayout getTagLayout(); ArticleListAdapter getAdapter(); void getDataError(String message); void getRefreshDataSuccess(List data); void getMoreDataSuccess(List data); } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/util/ActivityUtils.java ================================================ package com.lqm.tomatoit.util; import android.support.annotation.NonNull; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.util.Log; /** * autour: lqm * desc: */ public class ActivityUtils { private static final String TAG = "ActivityUtils"; public static void addFragmentToActivity(@NonNull FragmentManager fragmentManager, @NonNull Fragment fragment, int frameId) { addFragmentToActivity(fragmentManager, fragment, frameId, true); } public static void addFragmentToActivity(@NonNull FragmentManager fragmentManager, @NonNull Fragment fragment, int frameId, boolean addToBackStack) { if (fragment.isAdded()) { Log.w(TAG, "addFragmentToActivity: fragment is added:" + fragment.getClass().getName()); return; } FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); fragmentTransaction.add(frameId, fragment); if (addToBackStack) { fragmentTransaction.addToBackStack(null); } fragmentTransaction.commit(); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/util/L.java ================================================ package com.lqm.tomatoit.util; import android.util.Log; /** * Log统一管理类 */ public class L { private L() { /* cannot be instantiated */ throw new UnsupportedOperationException("cannot be instantiated"); } public static boolean isDebug = true;// 是否需要打印bug,可以在application的onCreate函数里面初始化 private static final String TAG = "way"; // 下面四个是默认tag的函数 public static void i(String msg) { if (isDebug) Log.i(TAG, msg); } public static void d(String msg) { if (isDebug) Log.d(TAG, msg); } public static void e(String msg) { if (isDebug) Log.e(TAG, msg); } public static void v(String msg) { if (isDebug) Log.v(TAG, msg); } // 下面是传入自定义tag的函数 public static void i(String tag, String msg) { if (isDebug) Log.i(tag, msg); } public static void d(String tag, String msg) { if (isDebug) Log.d(tag, msg); } public static void e(String tag, String msg) { if (isDebug) Log.e(tag, msg); } public static void v(String tag, String msg) { if (isDebug) Log.v(tag, msg); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/util/PrefUtils.java ================================================ package com.lqm.tomatoit.util; import android.content.Context; import android.content.SharedPreferences; /** * autour: lqm * desc: SharePreference的封装 */ public class PrefUtils { public static final String PREF_NAME = "config"; public static boolean getBoolean(Context ctx, String key, boolean defaultValue) { SharedPreferences sp = ctx.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); return sp.getBoolean(key, defaultValue); } public static void setBoolean(Context ctx, String key, boolean value) { SharedPreferences sp = ctx.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); sp.edit().putBoolean(key, value).commit(); } public static String getString(Context ctx, String key, String defaultValue) { SharedPreferences sp = ctx.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); return sp.getString(key, defaultValue); } public static void setString(Context ctx, String key, String value) { SharedPreferences sp = ctx.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); sp.edit().putString(key, value).commit(); } public static int getInt(Context ctx, String key, int defaultValue) { SharedPreferences sp = ctx.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); return sp.getInt(key, defaultValue); } public static void setInt(Context ctx, String key, int value) { SharedPreferences sp = ctx.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); sp.edit().putInt(key, value).commit(); } public static void clearData(Context ctx, String key) { SharedPreferences sp = ctx.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); sp.edit().remove(key).clear().commit(); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/util/SharesUtils.java ================================================ package com.lqm.tomatoit.util; import android.content.Context; import android.content.Intent; import android.net.Uri; import com.lqm.tomatoit.R; /** * autour: lqm * desc: 系统自带的分享 */ public class SharesUtils { public static void share(Context context, int stringRes) { share(context, context.getString(stringRes)); } public static void shareImage(Context context, Uri uri, String title) { Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.putExtra(Intent.EXTRA_STREAM, uri); shareIntent.setType("image/jpeg"); context.startActivity(Intent.createChooser(shareIntent, title)); } public static void share(Context context, String extraText) { Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("text/plain"); intent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.action_share)); intent.putExtra(Intent.EXTRA_TEXT, extraText); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity( Intent.createChooser(intent, context.getString(R.string.action_share))); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/util/T.java ================================================ package com.lqm.tomatoit.util; import android.content.Context; import android.widget.Toast; /** * Toast统一管理类 */ public class T { private T() { /* cannot be instantiated */ throw new UnsupportedOperationException("cannot be instantiated"); } public static boolean isShow = true; /** * 短时间显示Toast * * @param context * @param message */ public static void showShort(Context context, CharSequence message) { if (isShow) Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); } /** * 短时间显示Toast * * @param context * @param message */ public static void showShort(Context context, int message) { if (isShow) Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); } /** * 长时间显示Toast * * @param context * @param message */ public static void showLong(Context context, CharSequence message) { if (isShow) Toast.makeText(context, message, Toast.LENGTH_LONG).show(); } /** * 长时间显示Toast * * @param context * @param message */ public static void showLong(Context context, int message) { if (isShow) Toast.makeText(context, message, Toast.LENGTH_LONG).show(); } /** * 自定义显示Toast时间 * * @param context * @param message * @param duration */ public static void show(Context context, CharSequence message, int duration) { if (isShow) Toast.makeText(context, message, duration).show(); } /** * 自定义显示Toast时间 * * @param context * @param message * @param duration */ public static void show(Context context, int message, int duration) { if (isShow) Toast.makeText(context, message, duration).show(); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/util/UIUtils.java ================================================ package com.lqm.tomatoit.util; import android.content.Context; import android.content.res.Resources; import com.lqm.tomatoit.app.App; /** * @创建者 CSDN_LQR * @描述 和ui相关的工具类 */ public class UIUtils { /** * 得到上下文 * * @return */ public static Context getContext() { return App.getmContext(); } /** * 得到resources对象 * * @return */ public static Resources getResource() { return getContext().getResources(); } /** * 得到string.xml中的字符串 * * @param resId * @return */ public static String getString(int resId) { return getResource().getString(resId); } /** * 得到string.xml中的字符串,带点位符 * * @return */ public static String getString(int id, Object... formatArgs) { return getResource().getString(id, formatArgs); } /** * 得到string.xml中和字符串数组 * * @param resId * @return */ public static String[] getStringArr(int resId) { return getResource().getStringArray(resId); } /** * 得到colors.xml中的颜色 * @param colorId * @return */ public static int getColor(int colorId) { return getResource().getColor(colorId); } /** * dip-->px */ public static int dp2px(int dip) { // px/dip = density; // density = dpi/160 // 320*480 density = 1 1px = 1dp // 1280*720 density = 2 2px = 1dp float density = getResource().getDisplayMetrics().density; int px = (int) (dip * density + 0.5f); return px; } /** * px-->dip */ public static int px2dp(int px) { float density = getResource().getDisplayMetrics().density; int dip = (int) (px / density + 0.5f); return dip; } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/widget/AutoLinefeedLayout.java ================================================ package com.lqm.tomatoit.widget; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; /** * @user lqm * @desc 自动换行布局 */ public class AutoLinefeedLayout extends ViewGroup { public AutoLinefeedLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public AutoLinefeedLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public AutoLinefeedLayout(Context context) { this(context, null); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { layoutHorizontal(); } private void layoutHorizontal() { final int count = getChildCount(); final int lineWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight(); int paddingTop = getPaddingTop(); int childTop = 0; int childLeft = getPaddingLeft(); int availableLineWidth = lineWidth; int maxLineHight = 0; for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child == null) { continue; } else if (child.getVisibility() != GONE) { final int childWidth = child.getMeasuredWidth(); final int childHeight = child.getMeasuredHeight(); if (availableLineWidth < childWidth) { availableLineWidth = lineWidth; paddingTop = paddingTop + maxLineHight; childLeft = getPaddingLeft(); maxLineHight = 0; } childTop = paddingTop; setChildFrame(child, childLeft, childTop, childWidth, childHeight); childLeft += childWidth; availableLineWidth = availableLineWidth - childWidth; maxLineHight = Math.max(maxLineHight, childHeight); } } } private void setChildFrame(View child, int left, int top, int width, int height) { child.layout(left, top, left + width, top + height); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int heightMode = MeasureSpec.getMode(heightMeasureSpec); int count = getChildCount(); for (int i = 0; i < count; i++) { measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec); } if (heightMode == MeasureSpec.AT_MOST||heightMode == MeasureSpec.UNSPECIFIED) { final int width = MeasureSpec.getSize(widthMeasureSpec); super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec( getDesiredHeight(width), MeasureSpec.EXACTLY)); } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } private int getDesiredHeight(int width) { final int lineWidth = width - getPaddingLeft() - getPaddingRight(); int availableLineWidth = lineWidth; int totalHeight = getPaddingTop() + getPaddingBottom(); int lineHeight = 0; for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); final int childWidth = child.getMeasuredWidth(); final int childHeight = child.getMeasuredHeight(); if (availableLineWidth < childWidth) { availableLineWidth = lineWidth; totalHeight = totalHeight + lineHeight; lineHeight = 0; } availableLineWidth = availableLineWidth - childWidth; lineHeight = Math.max(childHeight, lineHeight); } totalHeight = totalHeight + lineHeight; return totalHeight; } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/widget/CustomDialog.java ================================================ package com.lqm.tomatoit.widget; import android.app.Dialog; import android.content.Context; import android.content.res.Resources; import android.util.DisplayMetrics; import android.view.Gravity; import android.view.View; import android.view.Window; import android.view.WindowManager; /** * autour: lqm * desc: 自定义圆角的dialog */ public class CustomDialog extends Dialog { /** * 宽高由布局文件中指定(但是最底层的宽度无效,可以多嵌套一层解决) */ public CustomDialog(Context context, View layout, int style) { super(context, style); setContentView(layout); Window window = getWindow(); WindowManager.LayoutParams params = window.getAttributes(); params.gravity = Gravity.CENTER; window.setAttributes(params); } /** * 宽高由该方法的参数设置 */ public CustomDialog(Context context, int width, int height, View layout, int style) { super(context, style); // 设置内容 setContentView(layout); // 设置窗口属性 Window window = getWindow(); WindowManager.LayoutParams params = window.getAttributes(); // 设置宽度、高度、密度、对齐方式 float density = getDensity(context); params.width = (int) (width * density); params.height = (int) (height * density); params.gravity = Gravity.CENTER; window.setAttributes(params); } /** * 获取显示密度 * * @param context * @return */ public float getDensity(Context context) { Resources res = context.getResources(); DisplayMetrics dm = res.getDisplayMetrics(); return dm.density; } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/widget/CustomPopWindow.java ================================================ package com.lqm.tomatoit.widget; import android.app.Activity; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.os.Build; import android.support.annotation.RequiresApi; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.PopupWindow; /** * user:lqm * desc:自定义PopWindow类,封装了PopWindow的一些常用属性,用Builder模式支持链式调用 * src:https://github.com/pinguo-zhouwei/CustomPopwindow */ public class CustomPopWindow implements PopupWindow.OnDismissListener{ private static final String TAG = "CustomPopWindow"; private static final float DEFAULT_ALPHA = 0.7f; private Context mContext; private int mWidth; private int mHeight; private boolean mIsFocusable = true; private boolean mIsOutside = true; private int mResLayoutId = -1; private View mContentView; private PopupWindow mPopupWindow; private int mAnimationStyle = -1; private boolean mClippEnable = true;//default is true private boolean mIgnoreCheekPress = false; private int mInputMode = -1; private PopupWindow.OnDismissListener mOnDismissListener; private int mSoftInputMode = -1; private boolean mTouchable = true;//default is ture private View.OnTouchListener mOnTouchListener; private Window mWindow;//当前Activity 的窗口 /** * 弹出PopWindow 背景是否变暗,默认不会变暗。 */ private boolean mIsBackgroundDark = false; private float mBackgroundDrakValue = 0;// 背景变暗的值,0 - 1 /** * 设置是否允许点击 PopupWindow之外的地方,关闭PopupWindow */ private boolean enableOutsideTouchDisMiss = true;// 默认点击pop之外的地方可以关闭 private CustomPopWindow(Context context){ mContext = context; } public int getWidth() { return mWidth; } public int getHeight() { return mHeight; } /** * * @param anchor * @param xOff * @param yOff * @return */ public CustomPopWindow showAsDropDown(View anchor, int xOff, int yOff){ if(mPopupWindow!=null){ mPopupWindow.showAsDropDown(anchor,xOff,yOff); } return this; } public CustomPopWindow showAsDropDown(View anchor){ if(mPopupWindow!=null){ mPopupWindow.showAsDropDown(anchor); } return this; } @RequiresApi(api = Build.VERSION_CODES.KITKAT) public CustomPopWindow showAsDropDown(View anchor, int xOff, int yOff, int gravity){ if(mPopupWindow!=null){ mPopupWindow.showAsDropDown(anchor,xOff,yOff,gravity); } return this; } /** * 相对于父控件的位置(通过设置Gravity.CENTER,下方Gravity.BOTTOM等 ),可以设置具体位置坐标 * @param parent 父控件 * @param gravity * @param x the popup's x location offset * @param y the popup's y location offset * @return */ public CustomPopWindow showAtLocation(View parent, int gravity, int x, int y){ if(mPopupWindow!=null){ mPopupWindow.showAtLocation(parent,gravity,x,y); } return this; } /** * 添加一些属性设置 * @param popupWindow */ private void apply(PopupWindow popupWindow){ popupWindow.setClippingEnabled(mClippEnable); if(mIgnoreCheekPress){ popupWindow.setIgnoreCheekPress(); } if(mInputMode!=-1){ popupWindow.setInputMethodMode(mInputMode); } if(mSoftInputMode!=-1){ popupWindow.setSoftInputMode(mSoftInputMode); } if(mOnDismissListener!=null){ popupWindow.setOnDismissListener(mOnDismissListener); } if(mOnTouchListener!=null){ popupWindow.setTouchInterceptor(mOnTouchListener); } popupWindow.setTouchable(mTouchable); } private PopupWindow build(){ if(mContentView == null){ mContentView = LayoutInflater.from(mContext).inflate(mResLayoutId,null); } // 2017.3.17 add // 获取当前Activity的window Activity activity = (Activity) mContentView.getContext(); if(activity!=null && mIsBackgroundDark){ //如果设置的值在0 - 1的范围内,则用设置的值,否则用默认值 final float alpha = (mBackgroundDrakValue > 0 && mBackgroundDrakValue < 1) ? mBackgroundDrakValue : DEFAULT_ALPHA; mWindow = activity.getWindow(); WindowManager.LayoutParams params = mWindow.getAttributes(); params.alpha = alpha; mWindow.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); mWindow.setAttributes(params); } if(mWidth != 0 && mHeight!=0 ){ mPopupWindow = new PopupWindow(mContentView,mWidth,mHeight); }else{ mPopupWindow = new PopupWindow(mContentView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); } if(mAnimationStyle!=-1){ mPopupWindow.setAnimationStyle(mAnimationStyle); } apply(mPopupWindow);//设置一些属性 if(mWidth == 0 || mHeight == 0){ mPopupWindow.getContentView().measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); //如果外面没有设置宽高的情况下,计算宽高并赋值 mWidth = mPopupWindow.getContentView().getMeasuredWidth(); mHeight = mPopupWindow.getContentView().getMeasuredHeight(); } // 添加dissmiss 监听 mPopupWindow.setOnDismissListener(this); //2017.6.27 add:fix 设置 setOutsideTouchable(false)点击外部取消的bug. // 判断是否点击PopupWindow之外的地方关闭 popWindow if(!enableOutsideTouchDisMiss){ //注意这三个属性必须同时设置,不然不能disMiss,以下三行代码在Android 4.4 上是可以,然后在Android 6.0以上,下面的三行代码就不起作用了,就得用下面的方法 mPopupWindow.setFocusable(true); mPopupWindow.setOutsideTouchable(false); mPopupWindow.setBackgroundDrawable(null); //注意下面这三个是contentView 不是PopupWindow mPopupWindow.getContentView().setFocusable(true); mPopupWindow.getContentView().setFocusableInTouchMode(true); mPopupWindow.getContentView().setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { mPopupWindow.dismiss(); return true; } return false; } }); //在Android 6.0以上 ,只能通过拦截事件来解决 mPopupWindow.setTouchInterceptor(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { final int x = (int) event.getX(); final int y = (int) event.getY(); if ((event.getAction() == MotionEvent.ACTION_DOWN) && ((x < 0) || (x >= mWidth) || (y < 0) || (y >= mHeight))) { Log.e(TAG,"out side "); Log.e(TAG,"width:"+mPopupWindow.getWidth()+"height:"+mPopupWindow.getHeight()+" x:"+x+" y :"+y); return true; } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { Log.e(TAG,"out side ..."); return true; } return false; } }); }else{ mPopupWindow.setFocusable(mIsFocusable); mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); mPopupWindow.setOutsideTouchable(mIsOutside); } // update mPopupWindow.update(); return mPopupWindow; } @Override public void onDismiss() { dissmiss(); } /** * 关闭popWindow */ public void dissmiss(){ if(mOnDismissListener!=null){ mOnDismissListener.onDismiss(); } //如果设置了背景变暗,那么在dissmiss的时候需要还原 if(mWindow!=null){ WindowManager.LayoutParams params = mWindow.getAttributes(); params.alpha = 1.0f; mWindow.setAttributes(params); } if(mPopupWindow!=null && mPopupWindow.isShowing()){ mPopupWindow.dismiss(); } } public PopupWindow getPopupWindow() { return mPopupWindow; } public static class PopupWindowBuilder{ private CustomPopWindow mCustomPopWindow; public PopupWindowBuilder(Context context){ mCustomPopWindow = new CustomPopWindow(context); } public PopupWindowBuilder size(int width,int height){ mCustomPopWindow.mWidth = width; mCustomPopWindow.mHeight = height; return this; } public PopupWindowBuilder setFocusable(boolean focusable){ mCustomPopWindow.mIsFocusable = focusable; return this; } public PopupWindowBuilder setView(int resLayoutId){ mCustomPopWindow.mResLayoutId = resLayoutId; mCustomPopWindow.mContentView = null; return this; } public PopupWindowBuilder setView(View view){ mCustomPopWindow.mContentView = view; mCustomPopWindow.mResLayoutId = -1; return this; } public PopupWindowBuilder setOutsideTouchable(boolean outsideTouchable){ mCustomPopWindow.mIsOutside = outsideTouchable; return this; } /** * 设置弹窗动画 * @param animationStyle * @return */ public PopupWindowBuilder setAnimationStyle(int animationStyle){ mCustomPopWindow.mAnimationStyle = animationStyle; return this; } public PopupWindowBuilder setClippingEnable(boolean enable){ mCustomPopWindow.mClippEnable =enable; return this; } public PopupWindowBuilder setIgnoreCheekPress(boolean ignoreCheekPress){ mCustomPopWindow.mIgnoreCheekPress = ignoreCheekPress; return this; } public PopupWindowBuilder setInputMethodMode(int mode){ mCustomPopWindow.mInputMode = mode; return this; } public PopupWindowBuilder setOnDissmissListener(PopupWindow.OnDismissListener onDissmissListener){ mCustomPopWindow.mOnDismissListener = onDissmissListener; return this; } public PopupWindowBuilder setSoftInputMode(int softInputMode){ mCustomPopWindow.mSoftInputMode = softInputMode; return this; } public PopupWindowBuilder setTouchable(boolean touchable){ mCustomPopWindow.mTouchable = touchable; return this; } public PopupWindowBuilder setTouchIntercepter(View.OnTouchListener touchIntercepter){ mCustomPopWindow.mOnTouchListener = touchIntercepter; return this; } /** * 设置背景变暗是否可用 * @param isDark * @return */ public PopupWindowBuilder enableBackgroundDark(boolean isDark){ mCustomPopWindow.mIsBackgroundDark = isDark; return this; } /** * 设置背景变暗的值 * @param darkValue * @return */ public PopupWindowBuilder setBgDarkAlpha(float darkValue){ mCustomPopWindow.mBackgroundDrakValue = darkValue; return this; } /** * 设置是否允许点击 PopupWindow之外的地方,关闭PopupWindow * @param disMiss * @return */ public PopupWindowBuilder enableOutsideTouchableDissmiss(boolean disMiss){ mCustomPopWindow.enableOutsideTouchDisMiss = disMiss; return this; } public CustomPopWindow create(){ //构建PopWindow mCustomPopWindow.build(); return mCustomPopWindow; } } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/widget/DynamicWave.java ================================================ package com.lqm.tomatoit.widget; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Canvas; import android.graphics.DrawFilter; import android.graphics.Paint; import android.graphics.PaintFlagsDrawFilter; import android.os.Build; import android.util.AttributeSet; import android.view.View; import com.lqm.tomatoit.R; import com.lqm.tomatoit.util.UIUtils; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * autour: lqm * desc: 水波纹效果View * y = Asin(wx+b)+h: * A:振幅两个山峰最大的高度.如果A越大两个山峰越高和越低 * h:你正弦曲线和y轴相交点.(影响正弦图初始高度的位置) * b:初相会让你图片向x轴平移 * src: http://blog.csdn.net/qfanmingyiq/article/details/53038262 *

* 修改者: Notzuonotdied */ public class DynamicWave extends View { /** * 第一条水波移动速度 */ private static final int TRANSLATE_X_SPEED_ONE = 7; /** * 第二条水波移动速度 */ private static final int TRANSLATE_X_SPEED_TWO = 5; /** * 第三条水波移动速度s */ private static final int TRANSLATE_X_SPEED_THREE = 3; private int mTotalWidth, mTotalHeight; private ScheduledExecutorService executorService; //用于延迟绘制 /** * 原始波纹的y值 */ private float[] mYPositions; /** * 第一个波纹移动速度的像素值 */ private int mXOffsetSpeedOne; private int mXOffsetSpeedTwo; private int mXOffsetSpeedThree; /** * 第一个波纹当前移动的距离 */ private int mXOneOffset; private int mXTwoOffset; private int mXThreeOffset; private Paint mWavePaint; private DrawFilter mDrawFilter; public DynamicWave(Context context, AttributeSet attrs) { super(context, attrs); // 将dp转化为px,用于控制不同分辨率上移动速度基本一致 mXOffsetSpeedOne = UIUtils.dp2px(TRANSLATE_X_SPEED_ONE); mXOffsetSpeedTwo = UIUtils.dp2px(TRANSLATE_X_SPEED_TWO); mXOffsetSpeedThree = UIUtils.dp2px(TRANSLATE_X_SPEED_THREE); // 初始绘制波纹的画笔 mWavePaint = new Paint(); // 去除画笔锯齿 mWavePaint.setAntiAlias(true); // 设置风格为实线 mWavePaint.setStyle(Paint.Style.FILL); // 设置画笔颜色 mWavePaint.setColor(getResources().getColor(R.color.white)); mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); // executorService = Executors .newScheduledThreadPool(Runtime.getRuntime().availableProcessors()); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 从canvas层面去除绘制时锯齿 canvas.setDrawFilter(mDrawFilter); // 控制波纹绘制的y在屏幕的位置,动态改变这个变量,可以形成波纹上升下降效果 for (int i = 0, j = 0, k = 0, f = 0; i < mTotalWidth; i++) { if (i + mXOneOffset < mTotalWidth) { canvas.drawLine(i, -mYPositions[mXOneOffset + i], i, mTotalHeight, mWavePaint); } else { canvas.drawLine(i, -mYPositions[j], i, mTotalHeight, mWavePaint); j++; } if (i + mXTwoOffset < mTotalWidth) { canvas.drawLine(i, -mYPositions[mXTwoOffset + i], i, mTotalHeight, mWavePaint); } else { canvas.drawLine(i, -mYPositions[k], i, mTotalHeight, mWavePaint); k++; } if (i + mXThreeOffset < mTotalWidth) { canvas.drawLine(i, -mYPositions[mXThreeOffset + i], i, mTotalHeight, mWavePaint); } else { canvas.drawLine(i, -mYPositions[f], i, mTotalHeight, mWavePaint); f++; } } // 改变两条波纹的移动点 mXOneOffset += mXOffsetSpeedOne; mXTwoOffset += mXOffsetSpeedTwo; mXThreeOffset += mXOffsetSpeedThree; // 如果已经移动到结尾处,则重头记录 if (mXOneOffset >= mTotalWidth) { mXOneOffset = 0; } if (mXTwoOffset > mTotalWidth) { mXTwoOffset = 0; } if (mXThreeOffset > mTotalWidth) { mXThreeOffset = 0; } // 引发view重绘,延迟233微秒绘制 executorService.schedule(this::invalidateWrap, 233, TimeUnit.MICROSECONDS); } /** * 获取控件的高度和宽度 */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // 记录下view的宽高 mTotalWidth = w; mTotalHeight = h; // 用于保存原始波纹的y值 mYPositions = new float[mTotalWidth]; // y = Asin(wx+b)+h // 将周期定为view总宽度 final float W = (float) (2 * Math.PI / mTotalWidth); final float A = mTotalHeight / 2; // 根据view总宽度得出所有对应的y值 for (int i = 0; i < mTotalWidth; i++) { mYPositions[i] = (float) (A * Math.sin(W * i) - A); } } @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private void invalidateWrap() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { postInvalidateOnAnimation(); } else { postInvalidate(); } } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/widget/IconFontTextView.java ================================================ package com.lqm.tomatoit.widget; import android.content.Context; import android.graphics.Typeface; import android.util.AttributeSet; /** * @user lqm * @desc 使用IConFont TextView */ public class IconFontTextView extends android.support.v7.widget.AppCompatTextView { private Context mContext; public IconFontTextView(Context context) { super(context); mContext = context; initView(); } public IconFontTextView(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; initView(); } private void initView() { Typeface iconfont = Typeface.createFromAsset(mContext.getAssets(), "iconfont.ttf"); setTypeface(iconfont); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/widget/RoundImageView.java ================================================ package com.lqm.tomatoit.widget; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.support.annotation.ColorRes; import android.util.AttributeSet; import android.util.TypedValue; import android.widget.ImageView; import com.lqm.tomatoit.R; /** * 自定义的圆角矩形ImageView */ @SuppressLint("AppCompatCustomView") public class RoundImageView extends ImageView { /** * 图片的类型,圆形or圆角 */ private int type; public static final int TYPE_CIRCLE = 0; public static final int TYPE_ROUND = 1; public static final int TYPE_OVAL = 2; /** * 描边的颜色、宽度 */ private int mBorderColor; private float mBorderWidth; private int mProgressColor = R.color.gray; /** * 圆角的大小 */ private float mCornerRadius; //左上角圆角大小 private float mLeftTopCornerRadius; //右上角圆角大小 private float mRightTopCornerRadius; //左下角圆角大小 private float mLeftBottomCornerRadius; //右下角圆角大小 private float mRightBottomCornerRadius; /** * 绘图的Paint */ private Paint mBitmapPaint; private Paint mBorderPaint; /** * 圆角的半径 */ private float mRadius; /** * 3x3 矩阵,主要用于缩小放大 */ private Matrix mMatrix; /** * 渲染图像,使用图像为绘制图形着色 */ private BitmapShader mBitmapShader; /** * view的宽度 */ private int mWidth; /** * 圆角图片区域 */ private RectF mRoundRect; private Path mRoundPath; private int mBorderProgress = 0; private static int num = 1; public RoundImageView(Context context) { this(context, null); } public RoundImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public RoundImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RoundImageView, defStyleAttr, 0); type = a.getInt(R.styleable.RoundImageView_type, TYPE_ROUND); mBorderColor = a.getColor(R.styleable.RoundImageView_border_color, Color.WHITE); mBorderWidth = a.getDimension(R.styleable.RoundImageView_border_width, 0); mCornerRadius = a.getDimension(R.styleable.RoundImageView_corner_radius, dp2px(10)); mLeftTopCornerRadius = a.getDimension(R.styleable.RoundImageView_leftTop_corner_radius, 0); mLeftBottomCornerRadius = a.getDimension(R.styleable.RoundImageView_leftBottom_corner_radius, 0); mRightTopCornerRadius = a.getDimension(R.styleable.RoundImageView_rightTop_corner_radius, 0); mRightBottomCornerRadius = a.getDimension(R.styleable.RoundImageView_rightBottom_corner_radius, 0); a.recycle(); init(); } private void init() { mRoundPath = new Path(); mMatrix = new Matrix(); mBitmapPaint = new Paint(); mBitmapPaint.setAntiAlias(true); mBorderPaint = new Paint(); mBorderPaint.setAntiAlias(true); mBorderPaint.setStyle(Paint.Style.STROKE); mBorderPaint.setStrokeCap(Paint.Cap.ROUND); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); /** * 如果类型是圆形,则强制改变view的宽高一致,以小值为准 */ if (type == TYPE_CIRCLE) { mWidth = Math.min(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec)); mRadius = mWidth / 2 - mBorderWidth / 2; setMeasuredDimension(mWidth, mWidth); } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // 圆角图片的范围 if (type == TYPE_ROUND || type == TYPE_OVAL) { mRoundRect = new RectF(mBorderWidth / 2, mBorderWidth / 2, w - mBorderWidth / 2, h - mBorderWidth / 2); } } @Override protected void onDraw(Canvas canvas) { mBorderPaint.setColor(mBorderColor); mBorderPaint.setStrokeWidth(mBorderWidth); if (getDrawable() == null) { return; } setUpShader(); if (type == TYPE_ROUND) { setRoundPath(); canvas.drawPath(mRoundPath, mBitmapPaint); //绘制描边 canvas.drawPath(mRoundPath, mBorderPaint); } else if (type == TYPE_CIRCLE) { canvas.drawCircle(mRadius + mBorderWidth / 2, mRadius + mBorderWidth / 2, mRadius, mBitmapPaint); //绘制描边 canvas.drawCircle(mRadius + mBorderWidth / 2, mRadius + mBorderWidth / 2, mRadius, mBorderPaint); //进度 mBorderPaint.setColor(getResources().getColor(mProgressColor)); RectF rect = new RectF(mBorderWidth/2,mBorderWidth/2,mRadius*2 + mBorderWidth/2,mRadius*2 + mBorderWidth/2); canvas.drawArc(rect,-90,mBorderProgress,false,mBorderPaint); } else { canvas.drawOval(mRoundRect, mBitmapPaint); canvas.drawOval(mRoundRect, mBorderPaint); } } private void setRoundPath() { mRoundPath.reset(); /** * 如果四个圆角大小都是默认值0, * 则将四个圆角大小设置为mCornerRadius的值 */ if (mLeftTopCornerRadius == 0 && mLeftBottomCornerRadius == 0 && mRightTopCornerRadius == 0 && mRightBottomCornerRadius == 0) { mRoundPath.addRoundRect(mRoundRect, new float[]{mCornerRadius, mCornerRadius, mCornerRadius, mCornerRadius, mCornerRadius, mCornerRadius, mCornerRadius, mCornerRadius}, Path.Direction.CW); } else { mRoundPath.addRoundRect(mRoundRect, new float[]{mLeftTopCornerRadius, mLeftTopCornerRadius, mRightTopCornerRadius, mRightTopCornerRadius, mRightBottomCornerRadius, mRightBottomCornerRadius, mLeftBottomCornerRadius, mLeftBottomCornerRadius}, Path.Direction.CW); } } /** * 初始化BitmapShader */ private void setUpShader() { Drawable drawable = getDrawable(); if (drawable == null) { return; } Bitmap bmp = drawableToBitamp(drawable); // 将bmp作为着色器,就是在指定区域内绘制bmp mBitmapShader = new BitmapShader(bmp, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); float scale = 1.0f; if (type == TYPE_CIRCLE) { // 拿到bitmap宽或高的小值 int bSize = Math.min(bmp.getWidth(), bmp.getHeight()); scale = mWidth * 1.0f / bSize; //使缩放后的图片居中 float dx = (bmp.getWidth() * scale - mWidth) / 2; float dy = (bmp.getHeight() * scale - mWidth) / 2; mMatrix.setTranslate(-dx, -dy); } else if (type == TYPE_ROUND || type == TYPE_OVAL) { if (!(bmp.getWidth() == getWidth() && bmp.getHeight() == getHeight())) { // 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,一定要大于我们view的宽高;所以我们这里取大值; scale = Math.max(getWidth() * 1.0f / bmp.getWidth(), getHeight() * 1.0f / bmp.getHeight()); //使缩放后的图片居中 float dx = (scale * bmp.getWidth() - getWidth()) / 2; float dy = (scale * bmp.getHeight() - getHeight()) / 2; mMatrix.setTranslate(-dx, -dy); } } // shader的变换矩阵,我们这里主要用于放大或者缩小 mMatrix.preScale(scale, scale); mBitmapShader.setLocalMatrix(mMatrix); // 设置变换矩阵 mBitmapShader.setLocalMatrix(mMatrix); // 设置shader mBitmapPaint.setShader(mBitmapShader); } /** * drawable转bitmap */ private Bitmap drawableToBitamp(Drawable drawable) { try { Bitmap bitmap; if (drawable instanceof BitmapDrawable) { BitmapDrawable bd = (BitmapDrawable) drawable; return bd.getBitmap(); } int w = drawable.getIntrinsicWidth(); int h = drawable.getIntrinsicHeight(); bitmap= Bitmap.createBitmap(w, h, drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_4444 : Bitmap.Config.RGB_565); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, w, h); drawable.draw(canvas); return bitmap; }catch (Exception e){ e.printStackTrace(); return null; } } /** * 设置图片类型: * imageType=0 圆形图片 * imageType=1 圆角图片 * 默认为圆形图片 */ public RoundImageView setType(int imageType) { if (this.type != imageType) { this.type = imageType; if (this.type != TYPE_ROUND && this.type != TYPE_CIRCLE && this.type != TYPE_OVAL) { this.type = TYPE_OVAL; } requestLayout(); } return this; } /** * 设置圆角图片的圆角大小 */ public RoundImageView setCornerRadius(int cornerRadius) { cornerRadius = dp2px(cornerRadius); if (mCornerRadius != cornerRadius) { mCornerRadius = cornerRadius; invalidate(); } return this; } /** * 设置圆角图片的左上圆角大小 */ public RoundImageView setLeftTopCornerRadius(int cornerRadius) { cornerRadius = dp2px(cornerRadius); if (mLeftTopCornerRadius != cornerRadius) { mLeftTopCornerRadius = cornerRadius; invalidate(); } return this; } /** * 设置圆角图片的右上圆角大小 */ public RoundImageView setRightTopCornerRadius(int cornerRadius) { cornerRadius = dp2px(cornerRadius); if (mRightTopCornerRadius != cornerRadius) { mRightTopCornerRadius = cornerRadius; invalidate(); } return this; } /** * 设置圆角图片的左下圆角大小 */ public RoundImageView setLeftBottomCornerRadius(int cornerRadius) { cornerRadius = dp2px(cornerRadius); if (mLeftBottomCornerRadius != cornerRadius) { mLeftBottomCornerRadius = cornerRadius; invalidate(); } return this; } /** * 设置圆角图片的右下圆角大小 */ public RoundImageView setRightBottomCornerRadius(int cornerRadius) { cornerRadius = dp2px(cornerRadius); if (mRightBottomCornerRadius != cornerRadius) { mRightBottomCornerRadius = cornerRadius; invalidate(); } return this; } /** * 设置描边宽度 */ public RoundImageView setBorderWidth(int borderWidth) { borderWidth = dp2px(borderWidth); if (mBorderWidth != borderWidth) { mBorderWidth = borderWidth; invalidate(); } return this; } /** * 设置描边颜色 */ public RoundImageView setBorderColor(int borderColor) { if (mBorderColor != borderColor) { mBorderColor = borderColor; invalidate(); } return this; } private int dp2px(int dpVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, getResources().getDisplayMetrics()); } public void setProgress(int progress,@ColorRes int color){ mBorderProgress = progress; mProgressColor = color; invalidate(); } } ================================================ FILE: app/src/main/java/com/lqm/tomatoit/widget/WebViewFragment.java ================================================ package com.lqm.tomatoit.widget; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.webkit.WebView; /** * 一个包含WebView的Fragment,来自Android Sdk */ public class WebViewFragment extends Fragment { private WebView mWebView; private boolean mIsWebViewAvailable; public WebViewFragment() { } /** * Called to instantiate the view. Creates and returns the WebView. */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (mWebView != null) { mWebView.destroy(); } mWebView = new WebView(getContext()); mIsWebViewAvailable = true; return mWebView; } /** * Called when the fragment is visible to the user and actively running. Resumes the WebView. */ @Override public void onPause() { super.onPause(); mWebView.onPause(); mWebView.pauseTimers(); } /** * Called when the fragment is no longer resumed. Pauses the WebView. */ @Override public void onResume() { mWebView.onResume(); mWebView.resumeTimers(); super.onResume(); } /** * Called when the WebView has been detached from the fragment. * The WebView is no longer available after this time. */ @Override public void onDestroyView() { mIsWebViewAvailable = false; super.onDestroyView(); } /** * Called when the fragment is no longer in use. Destroys the internal state of the WebView. */ @Override public void onDestroy() { if (mWebView != null) { mWebView.destroy(); mWebView = null; } super.onDestroy(); } /** * Gets the WebView. */ public WebView getWebView() { return mIsWebViewAvailable ? mWebView : null; } } ================================================ FILE: app/src/main/res/anim/slide_left_in.xml ================================================ ================================================ FILE: app/src/main/res/anim/slide_left_out.xml ================================================ ================================================ FILE: app/src/main/res/anim/slide_right_in.xml ================================================ ================================================ FILE: app/src/main/res/anim/slide_right_out.xml ================================================ ================================================ FILE: app/src/main/res/drawable/bg_round_frame_gray.xml ================================================ ================================================ FILE: app/src/main/res/drawable/dialog_waiting.xml ================================================ ================================================ FILE: app/src/main/res/drawable/progress_bar_status.xml ================================================ ================================================ FILE: app/src/main/res/drawable/sel_menu_item.xml ================================================ ================================================ FILE: app/src/main/res/drawable/shape_bg_round_white.xml ================================================ ================================================ FILE: app/src/main/res/drawable/shape_tag_nor.xml ================================================ ================================================ FILE: app/src/main/res/drawable/shape_tag_sel.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_about.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_collect.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_login.xml ================================================