1 #3

Closed
pbxef79cv wants to merge 12 commits from 张冉 into main

@ -1,17 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 这是一个项目配置文件的根元素version 属性表示该项目配置文件的版本号,此处为 4用于标识配置文件遵循的规范版本等信息 -->
<project version="4">
<!-- GradleMigrationSettings 组件元素,用于配置与 Gradle 迁移相关的设置migrationVersion 属性指定了 Gradle 迁移的版本号,此处为 1表明当前迁移的相关配置状态 -->
<component name="GradleMigrationSettings" migrationVersion="1" />
<!-- GradleSettings 组件元素,主要包含了与 Gradle 构建工具相关的各种具体设置选项 -->
<component name="GradleSettings">
<!-- option 元素,名为 "linkedExternalProjectsSettings",用于配置与外部项目链接相关的设置,其内部会包含更具体的 Gradle 项目相关设置 -->
<option name="linkedExternalProjectsSettings">
<!-- GradleProjectSettings 元素,用于详细定义 Gradle 项目的各项设置内容 -->
<GradleProjectSettings>
<!-- option 元素,名为 "distributionType",用于指定 Gradle 发行版的类型,"DEFAULT_WRAPPED" 表示默认的包装类型发行版,可能对应着特定的 Gradle 分发和使用方式 -->
<option name="distributionType" value="DEFAULT_WRAPPED" />
<!-- option 元素,名为 "externalProjectPath",用于设置外部项目的路径,此处使用了变量 "$PROJECT_DIR$",通常表示当前项目所在的目录路径,意味着外部项目的路径指向当前项目的根目录 -->
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<!-- option 元素,名为 "modules"用于配置项目的模块相关信息内部包含一个集合set来列举具体的模块路径 -->
<option name="modules">
<set>
<!-- option 元素,在模块集合中表示一个具体的模块路径,这里的值同样使用了变量 "$PROJECT_DIR$",表示项目根目录作为一个模块路径 -->
<option value="$PROJECT_DIR$" />
<!-- option 元素,指定了项目中 "app" 子目录作为一个模块路径,表明 "app" 是项目中的一个重要模块,可能包含了应用的主要代码、资源等内容 -->
<option value="$PROJECT_DIR$/app" />
<!-- option 元素,指定了项目中 "basemvplib" 子目录作为一个模块路径,说明 "basemvplib" 也是项目中的一个模块,可能存放着基础的 MVP 相关库代码等内容 -->
<option value="$PROJECT_DIR$/basemvplib" />
</set>
</option>
<!-- option 元素,名为 "resolveModulePerSourceSet"用于设置是否按照源集Source Set来解析模块值为 "false" 表示不按照这种方式进行模块解析,具体的解析方式会根据项目构建需求和 Gradle 的相关机制来确定 -->
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>

@ -1,11 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 这是 XML 配置文件的头部声明,表明该文件遵循 XML 1.0 版本规范,并且使用 UTF-8 编码格式 -->
<project version="4">
<!-- NullableNotNullManager 组件元素用于管理与可空Nullable和非空NotNull注解相关的配置可帮助开发工具等识别代码中不同来源的可空和非空标注情况 -->
<component name="NullableNotNullManager">
<!-- option 元素,名为 "myDefaultNullable",用于指定默认的可空注解类型,其值为 "android.support.annotation.Nullable",意味着在没有特别指定的情况下,开发工具等可能会将以此为默认的可空标识注解 -->
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<!-- option 元素,名为 "myDefaultNotNull",用于指定默认的非空注解类型,这里的值是 "android.support.annotation.NonNull",表示默认情况下将以此作为非空标识注解 -->
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<!-- option 元素,名为 "myNullables",用于配置一系列被认定为可空注解的相关信息,内部包含具体的可空注解列表 -->
<option name="myNullables">
<value>
<!-- list 元素表示一个列表结构用于罗列多个可空注解相关的配置项size 属性表示列表中元素的数量,此处为 7即包含 7 个可空注解相关配置 -->
<list size="7">
<!-- item 元素代表列表中的一个具体条目index 属性表示该条目的索引位置class 属性指定了条目的类型(这里都是 "java.lang.String" 类型表示以字符串形式记录相关信息itemvalue 属性则记录了具体的可空注解的全限定名 -->
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
@ -16,6 +23,7 @@
</list>
</value>
</option>
<!-- option 元素,名为 "myNotNulls",用于配置一系列被认定为非空注解的相关信息,与 "myNullables" 相对应,内部同样包含具体的非空注解列表 -->
<option name="myNotNulls">
<value>
<list size="6">
@ -29,5 +37,7 @@
</value>
</option>
</component>
<!-- ProjectRootManager 组件元素,用于管理项目根目录相关的配置以及项目所使用的 JDK 等关键信息 -->
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
<!-- 此元素的 version 属性值为 2 可能表示其配置版本情况languageLevel 属性指定了项目默认的语言级别为 JDK 1.7 相关语法特性等(尽管实际使用的 JDK 版本为 1.8project-jdk-name 属性记录了项目使用的 JDK 名称(这里是版本号 1.8project-jdk-type 属性明确了 JDK 的类型为 JavaSDK -->
</project>

@ -1,7 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- XML 声明部分,表明此 XML 文件遵循 1.0 版本规范且采用 UTF-8 编码格式 -->
<project version="4">
<!-- ProjectModuleManager 组件元素,主要用于管理项目中的模块相关信息,比如模块的配置文件路径等,以便开发环境能够准确识别和加载项目包含的各个模块 -->
<component name="ProjectModuleManager">
<!-- modules 元素,作为一个容器,用于罗列项目所包含的各个具体模块的相关配置信息 -->
<modules>
<!-- module 元素,代表项目中的一个具体模块,通过 fileurl 和 filepath 属性来指定该模块对应的配置文件的 URL 地址和本地文件路径 -->
<!-- 这里的 fileurl 属性值采用 "file://" 协议开头后面跟着模块配置文件的完整路径表示方式filepath 属性则是对应的本地文件系统中的实际路径表示,二者指向同一个模块配置文件 -->
<module fileurl="file://$PROJECT_DIR$/.idea/MONKOVEL.iml" filepath="$PROJECT_DIR$/.idea/MONKOVEL.iml" />
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
<module fileurl="file://$PROJECT_DIR$/basemvplib/basemvplib.iml" filepath="$PROJECT_DIR$/basemvplib/basemvplib.iml" />

@ -1,10 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- XML 声明部分,表明该 XML 文件遵循 1.0 版本规范,并且采用 UTF-8 编码格式 -->
<project version="4">
<!-- RunConfigurationProducerService 组件元素它通常与运行配置的生成相关服务有关用于管理哪些运行配置生成器Producers被忽略等相关设置 -->
<component name="RunConfigurationProducerService">
<!-- option 元素,名为 "ignoredProducers"用于配置需要被忽略的运行配置生成器相关信息其内部包含一个集合set来罗列具体要忽略的生成器 -->
<option name="ignoredProducers">
<set>
<!-- option 元素,在集合中代表一个具体要被忽略的运行配置生成器,通过 value 属性指定生成器的全限定名 -->
<!-- 这里第一个被忽略的生成器是 "org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer",意味着在涉及 Gradle 相关测试运行配置生成时,这个特定的生成器所提供的配置生成逻辑将不会被使用 -->
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<!-- 此生成器 "org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" 同样在被忽略的范围内,可能是基于项目特定需求或者为了避免某些冲突等原因,不采用它来生成相应的运行配置 -->
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<!-- 类似地,"org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" 这个运行配置生成器也被配置为忽略状态,即不会参与生成对应的运行配置 -->
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

@ -4,15 +4,34 @@ package com.monke.monkeybook;
import java.util.HashMap;
import java.util.Map;
/**
* BitIntentDataManagerIntent
* 便ActivityFragment
*/
public class BitIntentDataManager {
public static Map<String,Object> bigData;
/**
* 使bigDataMapStringObject
* 便
*/
public static Map<String, Object> bigData;
/**
* instanceBitIntentDataManagernull
*/
private static BitIntentDataManager instance = null;
public static BitIntentDataManager getInstance(){
if(instance == null){
synchronized (BitIntentDataManager.class){
if(instance == null){
/**
* BitIntentDataManager
* instancenull使synchronized线线
* instancenullnullBitIntentDataManagerinstance
* BitIntentDataManager
* @return BitIntentDataManager
*/
public static BitIntentDataManager getInstance() {
if (instance == null) {
synchronized (BitIntentDataManager.class) {
if (instance == null) {
instance = new BitIntentDataManager();
}
}
@ -20,16 +39,40 @@ public class BitIntentDataManager {
return instance;
}
private BitIntentDataManager(){
/**
* BitIntentDataManagerbigDataMap
* 使getInstance
*/
private BitIntentDataManager() {
bigData = new HashMap<>();
}
public Object getData(String key){
/**
* keybigDataMapObject
* null
* @param key 使便
* @return bigDataObjectnull
*/
public Object getData(String key) {
return bigData.get(key);
}
public void putData(String key,Object data){
bigData.put(key,data);
/**
* keyObjectbigDataMap便
*
* @param key 便
* @param data Object
*/
public void putData(String key, Object data) {
bigData.put(key, data);
}
public void cleanData(String key){
/**
* keybigDataMap
*
* @param key 使便
*/
public void cleanData(String key) {
bigData.remove(key);
}
}
}

@ -12,16 +12,36 @@ import io.reactivex.ObservableOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
/**
* ErrorAnalyContentManagerURL
* 便
*/
public class ErrorAnalyContentManager {
private ErrorAnalyContentManager(){
/**
*
* 使 `getInstance`
*/
private ErrorAnalyContentManager() {
}
/**
* ErrorAnalyContentManagernull
*/
private static ErrorAnalyContentManager instance;
public static ErrorAnalyContentManager getInstance(){
if(instance == null){
synchronized (ErrorAnalyContentManager.class){
if(instance == null){
/**
* ErrorAnalyContentManager
* `instance` null使 `synchronized` 线线
* `instance` nullnullErrorAnalyContentManager `instance`
* ErrorAnalyContentManager
* @return ErrorAnalyContentManager
*/
public static ErrorAnalyContentManager getInstance() {
if (instance == null) {
synchronized (ErrorAnalyContentManager.class) {
if (instance == null) {
instance = new ErrorAnalyContentManager();
}
}
@ -29,97 +49,149 @@ public class ErrorAnalyContentManager {
return instance;
}
public void writeNewErrorUrl(final String url){
/**
* URLURL
* RxJava `Observable` 线线线
* @param url URL
*/
public void writeNewErrorUrl(final String url) {
// 创建一个Observable对象用于定义一个异步可观察的操作序列这里传入一个实现了ObservableOnSubscribe接口的匿名内部类用于定义具体的操作逻辑即文件写入等操作
Observable.create(new ObservableOnSubscribe<Boolean>() {
@Override
public void subscribe(ObservableEmitter<Boolean> e) throws Exception {
// 获取应用程序的外部文件目录路径,这个路径是应用在外部存储中可以进行读写操作的目录,用于存放相关的错误记录文件
String filePath = MApplication.getInstance().getExternalFilesDir("").getPath();
// 根据获取到的文件路径创建一个File对象表示文件所在的目录后续用于判断目录是否存在以及创建目录等操作
File dir = new File(filePath);
if(!dir.exists()){
// 判断目录是否不存在,如果不存在则创建该目录,确保后续要写入的文件所在目录是存在的,避免出现文件写入失败的情况
if (!dir.exists()) {
dir.mkdirs();
}
File file2 = new File(filePath,"ErrorAnalyUrlsDetail.txt");
if(!file2.exists()) {
// 创建一个表示具体文件的File对象这里的文件名为 "ErrorAnalyUrlsDetail.txt"位于前面获取到的文件路径下用于详细记录每个出现错误的完整URL信息
File file2 = new File(filePath, "ErrorAnalyUrlsDetail.txt");
// 判断这个文件是否不存在如果不存在则创建一个新的空文件为后续写入错误URL信息做准备
if (!file2.exists()) {
file2.createNewFile();
}
FileOutputStream fileOutputStream2 = new FileOutputStream(file2,true);
fileOutputStream2.write((url+" \r\n").getBytes());
// 创建一个文件输出流对象,用于向 "ErrorAnalyUrlsDetail.txt" 文件写入数据第二个参数true表示以追加模式打开文件即新写入的数据会添加在文件末尾而不会覆盖原有内容
FileOutputStream fileOutputStream2 = new FileOutputStream(file2, true);
// 将传入的URL字符串加上特定的格式后面添加几个空格和换行符转换为字节数组后写入到文件中实现将错误URL信息记录到文件的操作
fileOutputStream2.write((url + " \r\n").getBytes());
// 刷新输出流缓冲区,确保数据真正写入到文件中,而不是仅仅停留在缓冲区
fileOutputStream2.flush();
// 关闭文件输出流,释放相关资源,避免资源泄漏
fileOutputStream2.close();
///////////////////////////////////////////////////////////////////////
File file1 = new File(filePath,"ErrorAnalyUrls.txt");
if(!file1.exists()) {
// 创建另一个表示文件的File对象这里的文件名为 "ErrorAnalyUrls.txt"同样位于前面获取到的文件路径下这个文件可能用于记录一些经过处理后的错误URL相关信息比如截取部分URL内容等情况
File file1 = new File(filePath, "ErrorAnalyUrls.txt");
// 判断这个文件是否不存在,如果不存在则创建一个新的空文件
if (!file1.exists()) {
file1.createNewFile();
}
// 创建一个文件输入流对象,用于读取 "ErrorAnalyUrls.txt" 文件中的内容后续会根据读取到的内容进行相应处理比如判断是否已存在相同的部分URL等情况
FileInputStream inputStream = new FileInputStream(file1);
// 创建一个字节数组用于临时存储每次从文件中读取到的数据这里指定了数组大小为1024字节可根据实际情况调整合适的大小
byte[] bytes = new byte[1024];
// 创建一个字节数组输出流对象,用于将从文件输入流中读取到的字节数据进行整合,方便后续将其转换为字符串进行处理
ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
while (inputStream.read(bytes) != -1) {
// 通过循环不断从文件输入流中读取数据每次读取的数据存储到bytes字节数组中直到读取到文件末尾返回值为 -1 表示文件末尾),将读取到的数据写入到字节数组输出流中
while (inputStream.read(bytes)!= -1) {
arrayOutputStream.write(bytes, 0, bytes.length);
}
// 关闭文件输入流,释放相关资源
inputStream.close();
// 关闭字节数组输出流,释放相关资源,注意在关闭之前,其内部已经整合好了从文件中读取到的所有字节数据
arrayOutputStream.close();
// 将字节数组输出流中的字节数据转换为字符串,这样就获取到了 "ErrorAnalyUrls.txt" 文件中的文本内容,方便后续进行字符串相关的判断操作
String content = new String(arrayOutputStream.toByteArray());
if(!content.contains(url.substring(0,url.indexOf('/',8)))){
FileOutputStream fileOutputStream1 = new FileOutputStream(file1,true);
fileOutputStream1.write((url.substring(0,url.indexOf('/',8))+" \r\n").getBytes());
// 判断转换后的文件内容字符串中是否不包含当前要写入的URL的部分内容这里截取了URL从开头到第8个字符后出现的第一个'/'之前的部分内容具体截取逻辑根据业务需求而定如果不包含则表示这个部分URL还未记录过
if (!content.contains(url.substring(0, url.indexOf('/', 8)))) {
// 创建一个文件输出流对象,用于向 "ErrorAnalyUrls.txt" 文件写入数据,同样以追加模式打开文件
FileOutputStream fileOutputStream1 = new FileOutputStream(file1, true);
// 将截取后的部分URL字符串加上特定的格式后面添加几个空格和换行符转换为字节数组后写入到文件中实现将部分错误URL信息记录到文件的操作
fileOutputStream1.write((url.substring(0, url.indexOf('/', 8)) + " \r\n").getBytes());
// 刷新输出流缓冲区,确保数据真正写入到文件中
fileOutputStream1.flush();
// 关闭文件输出流,释放相关资源
fileOutputStream1.close();
}
// 发送一个布尔值为true的数据给下游观察者表示当前的异步操作文件写入等操作已经成功完成了这一步骤触发下游的相关逻辑比如 `onNext` 方法的调用等)
e.onNext(true);
// 发送一个完成信号给下游观察者,表示整个异步操作已经全部完成,触发下游的 `onComplete` 方法调用等相关逻辑
e.onComplete();
}
})
// 指定这个Observable所代表的异步操作在IO线程用于处理输入输出相关的耗时操作如文件读写等上执行通过线程调度器来实现合适的线程切换提高应用的性能和响应性
.subscribeOn(Schedulers.io())
// 指定下游观察者即处理异步操作结果的相关逻辑代码所在的地方在Android的主线程上执行这样可以方便在主线程更新UI等操作符合Android开发中关于UI操作必须在主线程的要求
.observeOn(AndroidSchedulers.mainThread())
// 订阅这个Observable传入一个SimpleObserver对象用于处理异步操作过程中产生的数据通过 `onNext` 方法)、错误(通过 `onError` 方法)以及操作完成(通过 `onComplete` 方法)等不同情况的逻辑
.subscribe(new SimpleObserver<Boolean>() {
@Override
public void onNext(Boolean value) {
// 当上游的Observable发送数据这里是发送了布尔值为true的数据会调用这个方法当前方法体为空可以根据业务需求在这里添加相应的逻辑比如进行一些简单的日志记录等操作表示文件写入操作的某一步骤成功完成了
}
@Override
public void onError(Throwable e) {
// 当在异步操作过程中出现错误(比如文件读写出现异常等情况)时,会调用这个方法,当前方法体为空,可以在这里添加相应的错误处理逻辑,比如记录错误日志、提示用户等操作
}
});
}
public void writeMayByNetError(final String url){
/**
* URLRxJavaURL "ErrorNetUrl.txt" 便
* @param url URL
*/
public void writeMayByNetError(final String url) {
Observable.create(new ObservableOnSubscribe<Boolean>() {
@Override
public void subscribe(ObservableEmitter<Boolean> e) throws Exception {
// 获取应用程序的外部文件目录路径,用于确定要写入的文件所在的目录位置
String filePath = MApplication.getInstance().getExternalFilesDir("").getPath();
// 根据获取到的文件路径创建一个File对象表示文件所在的目录后续用于判断目录是否存在以及创建目录等操作
File dir = new File(filePath);
if(!dir.exists()){
// 判断目录是否不存在,如果不存在则创建该目录,确保后续要写入的文件所在目录是存在的,避免出现文件写入失败的情况
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(filePath,"ErrorNetUrl.txt");
if(!file.exists()) {
// 创建一个表示具体文件的File对象文件名为 "ErrorNetUrl.txt"位于前面获取到的文件路径下用于记录可能出现网络错误的URL信息
File file = new File(filePath, "ErrorNetUrl.txt");
// 判断这个文件是否不存在如果不存在则创建一个新的空文件为后续写入错误URL信息做准备
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream fileOutputStream2 = new FileOutputStream(file,true);
fileOutputStream2.write((url+" \r\n").getBytes());
// 创建一个文件输出流对象,用于向 "ErrorNetUrl.txt" 文件写入数据,以追加模式打开文件,使得新写入的数据添加在文件末尾,不会覆盖原有内容
FileOutputStream fileOutputStream2 = new FileOutputStream(file, true);
// 将传入的URL字符串加上特定的格式后面添加几个空格和换行符转换为字节数组后写入到文件中实现将网络错误相关的URL信息记录到文件的操作
fileOutputStream2.write((url + " \r\n").getBytes());
// 刷新输出流缓冲区,确保数据真正写入到文件中,而不是仅仅停留在缓冲区
fileOutputStream2.flush();
// 关闭文件输出流,释放相关资源,避免资源泄漏
fileOutputStream2.close();
// 发送一个布尔值为true的数据给下游观察者表示当前的异步操作文件写入等操作已经成功完成了这一步骤触发下游的相关逻辑比如 `onNext` 方法的调用等)
e.onNext(true);
// 发送一个完成信号给下游观察者,表示整个异步操作已经全部完成,触发下游的 `onComplete` 方法调用等相关逻辑
e.onComplete();
}
})
// 指定这个Observable所代表的异步操作在IO线程上执行通过线程调度器实现合适的线程切换让文件读写等耗时操作在后台线程进行提高应用性能和响应性
.subscribeOn(Schedulers.io())
// 指定下游观察者即处理异步操作结果的相关逻辑代码所在的地方在Android的主线程上执行方便在主线程进行UI更新等操作符合Android开发中关于UI操作必须在主线程的要求
.observeOn(AndroidSchedulers.mainThread())
// 订阅这个Observable传入一个SimpleObserver对象用于处理异步操作过程中产生的数据通过 `onNext` 方法)、错误(通过 `onError` 方法)以及操作完成(通过 `onComplete` 方法)等不同情况的逻辑
.subscribe(new SimpleObserver<Boolean>() {
@Override
public void onNext(Boolean value) {
// 当上游的Observable发送数据这里是发送了布尔值为true的数据会调用这个方法当前方法体为空可以根据业务需求在这里添加相应的逻辑比如进行一些简单的日志记录等操作表示文件写入操作的某一步骤成功完成了
}
@Override
public void onError(Throwable e) {
// 当在异步操作过程中出现错误(比如文件读写出现异常等情况)时,会调用这个方法,当前方法体为空,可以在这里添加相应的错误处理逻辑,比如记录错误日志、提示用户等操作
}
});
}
}
}

@ -8,31 +8,48 @@ import android.content.pm.PackageManager;
import com.monke.monkeybook.service.DownloadService;
import com.umeng.analytics.MobclickAgent;
// MApplication类继承自Android的Application类是整个应用程序的全局基础类用于在应用启动时进行一些初始化操作例如配置统计分析工具、启动相关服务等
public class MApplication extends Application {
// 定义一个静态变量instance用于保存MApplication类的唯一实例方便在整个应用的其他地方可以获取到这个唯一的Application实例对象实现全局访问
private static MApplication instance;
// 重写Application类的onCreate方法这个方法会在应用程序启动时被系统自动调用用于执行应用的初始化相关逻辑
@Override
public void onCreate() {
super.onCreate();
// 判断当前应用是否是发布版本BuildConfig.IS_RELEASE通常是一个根据构建配置生成的布尔值用于区分开发环境和发布环境如果是发布版本则执行以下初始化操作
if (BuildConfig.IS_RELEASE) {
// 初始化一个默认的渠道名为"debug",后续会尝试从应用的元数据中获取真实的渠道名来替换它,如果获取失败则保留这个默认值,这里的渠道名通常用于统计分析等工具区分应用的不同发布渠道来源
String channel = "debug";
try {
// 通过应用的包管理器PackageManager获取当前应用的ApplicationInfo信息GET_META_DATA标志表示要获取应用的元数据信息这一步是为了后续能从元数据中查找特定的渠道相关配置信息
ApplicationInfo appInfo = getPackageManager()
.getApplicationInfo(getPackageName(),
PackageManager.GET_META_DATA);
// 从获取到的ApplicationInfo的元数据中尝试获取名为"UMENG_CHANNEL_VALUE"的字符串值这个值通常在应用的配置文件如AndroidManifest.xml或相关的构建配置中设置中定义代表友盟统计分析工具所使用的渠道值
channel = appInfo.metaData.getString("UMENG_CHANNEL_VALUE");
} catch (PackageManager.NameNotFoundException e) {
// 如果在获取元数据过程中出现NameNotFoundException异常通常是因为指定的元数据键不存在等原因则打印异常堆栈信息方便排查问题同时保持前面设置的默认渠道名"debug"不变
e.printStackTrace();
}
// 使用获取到的渠道名以及其他相关配置信息如友盟的App Key等来初始化友盟统计分析工具MobclickAgent配置应用的统计相关行为例如指定应用的上下文this代表当前的MApplication实例、友盟的App Key通过getString(R.string.umeng_key)获取,通常在资源文件中定义)、渠道名、场景类型(这里是普通场景)以及是否自动统计页面停留时间等参数
MobclickAgent.startWithConfigure(new MobclickAgent.UMAnalyticsConfig(this, getString(R.string.umeng_key), channel, MobclickAgent.EScenarioType.E_UM_NORMAL, true));
}
// 将当前的MApplication实例赋值给静态变量instance使得在应用的其他地方可以通过getInstance方法获取到这个唯一的Application实例对象实现全局访问
instance = this;
// 调用ProxyManager类的initProxy方法具体功能需看ProxyManager类的实现可能是初始化一些代理相关设置等进行相关初始化操作这一步可能是应用自定义的初始化逻辑的一部分用于处理网络代理等相关功能的初始化
ProxyManager.initProxy();
// 创建一个启动DownloadService服务的意图Intent并通过startService方法启动这个服务DownloadService可能是用于处理应用中文件下载等相关功能的服务启动它可以让相关下载逻辑在后台运行保证下载任务等的持续执行
startService(new Intent(this, DownloadService.class));
}
// 定义一个静态方法用于获取MApplication类的唯一实例方便在应用的其他地方获取到这个全局的Application实例对象进而可以访问在Application中定义的全局变量、调用相关方法等
public static MApplication getInstance() {
return instance;
}
}
}

@ -1,67 +1,136 @@
package com.monke.monkeybook;
import android.content.SharedPreferences;
import org.jsoup.helper.StringUtil;
import java.util.regex.Pattern;
// ProxyManager类主要用于管理应用程序中的代理相关设置包括代理的状态是否启用代理以及代理的HTTP地址等信息
// 同时提供了保存、初始化这些设置以及检查是否存在有效代理的方法通过与SharedPreferences交互实现设置的持久化存储。
public class ProxyManager {
// 定义一个常量字符串作为在SharedPreferences中存储HTTP代理地址的键方便后续通过这个键来获取和设置对应的代理地址值
// 遵循统一的命名规范以便代码的可读性和维护性。
public static final String SP_KEY_PROXY_HTTP = "proxy_http";
// 定义一个常量字符串作为在SharedPreferences中存储代理状态布尔值表示是否启用代理的键
// 用于准确标识和操作代理状态相关的存储数据。
public static final String SP_KEY_PROXY_STATE = "proxy_state";
// 定义一个默认的HTTP代理地址值初始为空字符串表示没有设置代理地址时的默认情况
// 当获取代理地址且未进行过设置或者设置被清除时,会返回这个默认值。
public static final String PROXY_HTTP_DEFAULT = "";
// 定义一个默认的代理状态值初始为false表示默认情况下代理是未启用的
// 作为代理状态在没有从存储中获取到有效数据或者初始化时的默认设置。
public static final boolean PROXY_STATE_DEFAULT = false;
// 静态变量,用于存储当前应用的代理状态(是否启用代理),可在类的各个方法中访问和修改,
// 通过与存储的交互以及相关方法的调用保持其值与实际的代理设置情况一致。
public static boolean proxyState;
// 静态变量用于存储当前应用的HTTP代理地址方便在需要使用代理进行网络请求等操作时获取该地址信息
// 其值会根据用户设置以及从存储中读取的情况进行更新。
public static String proxyHttp;
private static final String PROXY_HTTP_MATCH = "(http|ftp|https):\\/\\/[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-\\.,@?^=%&amp;:/~\\+#]*[\\w\\-\\@?^=%&amp;/~\\+#])?";//http正则表达式
public static final String PROXY_PACKAGENAME_ENCODE = "代理包名加密key"; //代理包名加密key
public static String packageName; //加密后的包名
// 定义一个用于匹配HTTP代理地址格式的正则表达式字符串用于验证输入或存储的代理地址是否符合规范
// 涵盖了常见的HTTP、HTTPS、FTP协议开头的网络地址格式情况以便对代理地址进行合法性校验。
private static final String PROXY_HTTP_MATCH = "(http|ftp|https):\\/\\/[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-\\.,@?^=%&amp;:/~\\+#]*[\\w\\-\\@?^=%&amp;/~\\+#])?";
// 定义一个常量字符串,用于表示代理包名加密的密钥,具体加密相关功能可能在其他地方实现,
// 这里只是定义了这个密钥的常量表示,用于相关的加密操作或者与加密相关逻辑的标识。
public static final String PROXY_PACKAGENAME_ENCODE = "代理包名加密key";
// 静态变量,用于存储加密后的包名信息,具体加密过程以及其用途取决于应用中与代理相关的业务逻辑,
// 例如可能用于身份验证、权限控制等涉及到包名相关的代理功能场景中。
public static String packageName;
// 用于保存代理状态到SharedPreferences中的方法接收一个布尔值参数表示要设置的代理状态
// 同时更新类中的静态变量proxyState确保内存中的状态与存储中的设置保持一致。
public static void saveProxyState(boolean state) {
// 将传入的代理状态值赋给静态变量proxyState使其在内存中更新为最新的设置值。
proxyState = state;
// 获取应用程序的SharedPreferences对象的编辑器Editor用于后续对存储数据进行修改操作
// "CONFIG"是SharedPreferences文件的名称0表示操作模式MODE_PRIVATE即只有当前应用可以访问该文件
SharedPreferences.Editor editor = MApplication.getInstance().getSharedPreferences("CONFIG", 0).edit();
// 通过编辑器将代理状态值proxyState以指定的键SP_KEY_PROXY_STATE存储到SharedPreferences中实现数据的持久化保存。
editor.putBoolean(SP_KEY_PROXY_STATE, proxyState);
// 提交编辑器中的修改操作将数据真正写入到SharedPreferences文件中使设置生效。
editor.commit();
}
// 私有方法用于初始化代理状态信息主要从SharedPreferences中获取之前存储的代理状态值
// 如果获取失败则使用默认的代理状态值PROXY_STATE_DEFAULT同时尝试获取应用的包名信息可能用于后续相关逻辑
private static void initProxyState() {
try {
// 通过应用的包管理器PackageManager获取当前应用的包名信息将获取到的包名赋值给静态变量packageName
// 具体用途可能与代理相关的权限判断、标识等功能有关,这里先进行获取并存储以备后续使用。
packageName = MApplication.getInstance().getPackageManager().getPackageInfo(MApplication.getInstance().getPackageName(), 0).packageName;
} catch (Exception e) {
// 如果在获取包名过程中出现异常(例如找不到对应包信息等情况),则打印异常堆栈信息,方便排查问题,
// 同时在控制台输出提示信息,表示包名获取失败可能会影响代理请求功能,让开发者知晓潜在问题。
e.printStackTrace();
System.out.println("=================包名获取失败,可能会影响代理请求功能=================");
}
// 从应用的SharedPreferences中获取代理状态值以SP_KEY_PROXY_STATE为键进行查找
// 如果不存在对应的键值对则使用默认的代理状态值PROXY_STATE_DEFAULT作为初始值赋给proxyState变量实现代理状态的初始化。
proxyState = MApplication.getInstance().getSharedPreferences("CONFIG", 0).getBoolean(SP_KEY_PROXY_STATE, PROXY_STATE_DEFAULT);
}
// 用于保存HTTP代理地址到SharedPreferences中的方法接收一个字符串参数表示要设置的代理地址
// 同时更新类中的静态变量proxyHttp保证内存中的代理地址与存储中的设置一致。
public static void saveProxyHttp(String http) {
// 将传入的代理地址值赋给静态变量proxyHttp使其在内存中更新为最新的设置值。
proxyHttp = http;
// 获取应用程序的SharedPreferences对象的编辑器Editor用于后续对存储数据进行修改操作
// "CONFIG"是SharedPreferences文件的名称0表示操作模式MODE_PRIVATE即只有当前应用可以访问该文件
SharedPreferences.Editor editor = MApplication.getInstance().getSharedPreferences("CONFIG", 0).edit();
// 通过编辑器将代理地址值proxyHttp以指定的键SP_KEY_PROXY_HTTP存储到SharedPreferences中实现数据的持久化保存。
editor.putString(SP_KEY_PROXY_HTTP, proxyHttp);
// 提交编辑器中的修改操作将数据真正写入到SharedPreferences文件中使设置生效。
editor.commit();
}
// 私有方法用于初始化HTTP代理地址信息从SharedPreferences中获取之前存储的代理地址值
// 如果获取失败则使用默认的代理地址值PROXY_HTTP_DEFAULT以此来完成代理地址的初始化设置。
private static void initProxyHttp() {
// 从应用的SharedPreferences中获取代理地址值以SP_KEY_PROXY_HTTP为键进行查找
// 如果不存在对应的键值对则使用默认的代理地址值PROXY_HTTP_DEFAULT作为初始值赋给proxyHttp变量实现代理地址的初始化。
proxyHttp = MApplication.getInstance().getSharedPreferences("CONFIG", 0).getString(SP_KEY_PROXY_HTTP, PROXY_HTTP_DEFAULT);
}
// 用于整体初始化代理相关设置的方法,会依次调用初始化代理地址和初始化代理状态的方法,
// 然后调用hasProxy方法来检查当前设置的代理是否有效完成整个代理相关设置的初始化流程。
public static void initProxy() {
// 调用initProxyHttp方法初始化HTTP代理地址信息从存储中获取或设置默认的代理地址值到内存变量中。
initProxyHttp();
// 调用initProxyState方法初始化代理状态信息从存储中获取或设置默认的代理状态值到内存变量中。
initProxyState();
// 调用hasProxy方法检查当前设置的代理是否有效根据代理状态和代理地址的合法性进行判断
// 并根据判断结果可能更新代理状态的存储值,确保代理设置的准确性。
hasProxy();
}
// 用于检查当前是否存在有效代理的方法,根据代理状态以及代理地址是否符合格式要求来综合判断,
// 如果代理未启用或者代理地址不符合规范则返回false表示不存在有效代理否则返回true表示存在有效代理
// 同时在代理地址不符合规范时会自动将代理状态设置为false并保存到存储中以保证代理设置的合理性。
public static boolean hasProxy() {
// 如果代理状态为false即代理未启用直接返回false表示不存在有效代理无需再进行地址格式验证等操作。
if (!proxyState) {
return false;
}
// 使用定义好的正则表达式字符串PROXY_HTTP_MATCH创建一个Pattern对象用于后续对代理地址进行格式匹配验证
// Pattern类提供了强大的正则表达式匹配功能方便判断输入的字符串是否符合特定的格式要求。
Pattern pattern = Pattern.compile(PROXY_HTTP_MATCH);
// 通过StringUtil.isBlank方法检查代理地址proxyHttp是否为空包含空字符串、null以及只包含空白字符等情况
// 并且使用创建的Pattern对象对代理地址进行正则匹配验证只有当代理地址不为空且符合格式要求时才返回true表示存在有效代理
// 否则返回false表示代理地址不符合规范不存在有效代理。
if (!StringUtil.isBlank(proxyHttp) && pattern.matcher(proxyHttp).matches()) {
return true;
} else {
// 如果代理地址不符合规范调用saveProxyState方法将代理状态设置为false表示当前不存在有效代理
// 并将这个更新后的代理状态保存到SharedPreferences中确保存储中的代理状态与实际情况一致然后返回false。
saveProxyState(false);
return false;
}
}
}
}

@ -9,169 +9,255 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* ReadBookControl
* 便
*/
public class ReadBookControl {
// 定义默认的文字样式索引值用于在没有特定设置或者初始化时确定默认使用的文字相关配置这里值为2具体对应哪种文字样式由后续的逻辑决定。
public static final int DEFAULT_TEXT = 2;
// 定义默认的背景样式索引值用于在没有特定设置或者初始化时确定默认使用的背景相关配置这里值为1同样具体对应哪种背景样式由后续逻辑决定。
public static final int DEFAULT_BG = 1;
private static List<Map<String,Integer>> textKind;
private static List<Map<String,Integer>> textDrawable;
// 用于存储不同文字样式相关配置信息的列表每个元素是一个Map其中包含如文字大小、文字间距等具体配置项方便根据不同索引获取对应的文字样式配置。
private static List<Map<String, Integer>> textKind;
// 用于存储不同文字绘制相关配置信息的列表主要涉及文字颜色和文字背景相关设置每个元素是一个Map方便根据不同索引获取对应的文字绘制相关配置。1111
private static List<Map<String, Integer>> textDrawable;
// 记录当前的文字大小,单位可能是像素等(具体取决于存储和使用的情况),通过获取对应文字样式配置中的值来初始化和更新,用于控制阅读界面文字显示的大小。
private int textSize;
// 记录当前的文字间距(额外间距),单位可能是像素等,通过对应文字样式配置获取并更新,用于调整文字排版时的间距效果,提升阅读体验。
private int textExtra;
// 记录当前的文字颜色以颜色值的形式存储可能是通过Android系统的颜色表示方式如ARGB等从文字绘制相关配置中获取决定文字在阅读界面呈现的颜色。
private int textColor;
// 记录当前的文字背景以资源标识符如R.drawable中的相关资源ID的形式存储用于指定文字所在区域的背景样式从文字绘制相关配置中获取并应用。
private int textBackground;
// 当前选择的文字样式索引用于在textKind列表中定位对应的文字样式配置初始化为DEFAULT_TEXT可通过设置方法进行更新以切换不同的文字样式。
private int textKindIndex = DEFAULT_TEXT;
// 当前选择的文字绘制样式索引用于在textDrawable列表中定位对应的文字绘制相关配置初始化为DEFAULT_BG同样可通过设置方法更新来切换不同的文字背景和颜色组合等样式。
private int textDrawableIndex = DEFAULT_BG;
// 用于标识是否允许通过点击进行翻页操作初始值为true表示默认允许点击翻页可通过设置方法改变其值并将设置持久化存储方便用户根据自己的阅读习惯进行调整。
private Boolean canClickTurn = true;
// 用于标识是否允许通过按键如音量键等具体取决于应用的按键绑定设置进行翻页操作初始值为true可通过设置方法修改值并保存设置以满足不同用户对翻页操作方式的偏好。
private Boolean canKeyTurn = true;
// 用于存储应用的配置信息通过SharedPreferences与应用的配置文件通常以键值对形式存储数据进行交互实现阅读控制相关参数的持久化存储和读取。
private SharedPreferences preference;
// 静态变量用于保存ReadBookControl类的唯一实例遵循单例模式的设计初始值为null通过特定的获取实例方法来确保整个应用只有一个实例存在。
private static ReadBookControl readBookControl;
public static ReadBookControl getInstance(){
if(readBookControl == null){
synchronized (ReadBookControl.class){
if(readBookControl == null){
/**
* ReadBookControl
* readBookControlnull使synchronized线线
* readBookControlnullnullReadBookControlreadBookControl
* ReadBookControl便
* @return ReadBookControl
*/
public static ReadBookControl getInstance() {
if (readBookControl == null) {
synchronized (ReadBookControl.class) {
if (readBookControl == null) {
readBookControl = new ReadBookControl();
}
}
}
return readBookControl;
}
private ReadBookControl(){
if(null == textKind){
/**
* ReadBookControlSharedPreferences
* getInstance
*/
private ReadBookControl() {
// 如果textKind列表为空即还未初始化则进行初始化操作创建不同文字样式相关的配置信息并添加到textKind列表中。
if (null == textKind) {
textKind = new ArrayList<>();
Map<String,Integer> temp1 = new HashMap<>();
// 创建一个临时的Map用于存储一种文字样式的配置信息这里设置文字大小为14单位可能是像素等具体看后续使用情况文字间距通过DensityUtil工具类将dp值转换为像素值6.5dp转换后的像素值然后将这个配置信息添加到textKind列表中。
Map<String, Integer> temp1 = new HashMap<>();
temp1.put("textSize", 14);
temp1.put("textExtra", DensityUtil.dp2px(MApplication.getInstance(),6.5f));
temp1.put("textExtra", DensityUtil.dp2px(MApplication.getInstance(), 6.5f));
textKind.add(temp1);
Map<String,Integer> temp2 = new HashMap<>();
// 类似地创建另一种文字样式的配置信息文字大小设置为16文字间距通过DensityUtil转换对应dp值后的像素值再添加到textKind列表中方便后续根据索引选择不同的文字大小和间距配置。
Map<String, Integer> temp2 = new HashMap<>();
temp2.put("textSize", 16);
temp2.put("textExtra", DensityUtil.dp2px(MApplication.getInstance(),8));
temp2.put("textExtra", DensityUtil.dp2px(MApplication.getInstance(), 8));
textKind.add(temp2);
Map<String,Integer> temp3 = new HashMap<>();
Map<String, Integer> temp3 = new HashMap<>();
temp3.put("textSize", 17);
temp3.put("textExtra", DensityUtil.dp2px(MApplication.getInstance(),9));
temp3.put("textExtra", DensityUtil.dp2px(MApplication.getInstance(), 9));
textKind.add(temp3);
Map<String,Integer> temp4 = new HashMap<>();
Map<String, Integer> temp4 = new HashMap<>();
temp4.put("textSize", 20);
temp4.put("textExtra", DensityUtil.dp2px(MApplication.getInstance(),11));
temp4.put("textExtra", DensityUtil.dp2px(MApplication.getInstance(), 11));
textKind.add(temp4);
Map<String,Integer> temp5 = new HashMap<>();
Map<String, Integer> temp5 = new HashMap<>();
temp5.put("textSize", 22);
temp5.put("textExtra", DensityUtil.dp2px(MApplication.getInstance(),13));
temp5.put("textExtra", DensityUtil.dp2px(MApplication.getInstance(), 13));
textKind.add(temp5);
}
if(null == textDrawable){
// 如果textDrawable列表为空未初始化则进行初始化操作创建不同文字绘制样式相关的配置信息包含文字颜色和文字背景并添加到textDrawable列表中。
if (null == textDrawable) {
textDrawable = new ArrayList<>();
Map<String,Integer> temp1 = new HashMap<>();
temp1.put("textColor",Color.parseColor("#3E3D3B"));
temp1.put("textBackground",R.drawable.shape_bg_readbook_white);
// 创建一个临时的Map用于存储一种文字绘制样式的配置信息设置文字颜色通过Color.parseColor方法解析十六进制颜色代码这里是深灰色得到对应的颜色值文字背景设置为指定的白色背景样式资源通过R.drawable中的资源ID指定然后将这个配置添加到textDrawable列表中。
Map<String, Integer> temp1 = new HashMap<>();
temp1.put("textColor", Color.parseColor("#3E3D3B"));
temp1.put("textBackground", R.drawable.shape_bg_readbook_white);
textDrawable.add(temp1);
Map<String,Integer> temp2 = new HashMap<>();
temp2.put("textColor",Color.parseColor("#5E432E"));
temp2.put("textBackground",R.drawable.bg_readbook_yellow);
Map<String, Integer> temp2 = new HashMap<>();
temp2.put("textColor", Color.parseColor("#5E432E"));
temp2.put("textBackground", R.drawable.bg_readbook_yellow);
textDrawable.add(temp2);
Map<String,Integer> temp3 = new HashMap<>();
temp3.put("textColor",Color.parseColor("#22482C"));
temp3.put("textBackground",R.drawable.bg_readbook_green);
Map<String, Integer> temp3 = new HashMap<>();
temp3.put("textColor", Color.parseColor("#22482C"));
temp3.put("textBackground", R.drawable.bg_readbook_green);
textDrawable.add(temp3);
Map<String,Integer> temp4 = new HashMap<>();
temp4.put("textColor",Color.parseColor("#808080"));
temp4.put("textBackground",R.drawable.bg_readbook_black);
Map<String, Integer> temp4 = new HashMap<>();
temp4.put("textColor", Color.parseColor("#808080"));
temp4.put("textBackground", R.drawable.bg_readbook_black);
textDrawable.add(temp4);
}
// 获取应用的SharedPreferences对象用于读取和保存阅读控制相关的配置参数"CONFIG"是配置文件的名称0表示操作模式MODE_PRIVATE即只有当前应用可以访问该文件
preference = MApplication.getInstance().getSharedPreferences("CONFIG", 0);
this.textKindIndex = preference.getInt("textKindIndex",DEFAULT_TEXT);
// 从SharedPreferences中读取之前保存的文字样式索引值如果不存在则使用默认的文字样式索引值DEFAULT_TEXT然后根据这个索引获取对应的文字大小和文字间距并赋值给相应变量。
this.textKindIndex = preference.getInt("textKindIndex", DEFAULT_TEXT);
this.textSize = textKind.get(textKindIndex).get("textSize");
this.textExtra = textKind.get(textKindIndex).get("textExtra");
this.textDrawableIndex = preference.getInt("textDrawableIndex",DEFAULT_BG);
// 从SharedPreferences中读取之前保存的文字绘制样式索引值若不存在则用默认的背景样式索引值DEFAULT_BG再依据该索引获取对应的文字颜色和文字背景资源并赋值给相应变量。
this.textDrawableIndex = preference.getInt("textDrawableIndex", DEFAULT_BG);
this.textColor = textDrawable.get(textDrawableIndex).get("textColor");
this.textBackground = textDrawable.get(textDrawableIndex).get("textBackground");
this.canClickTurn = preference.getBoolean("canClickTurn",true);
this.canKeyTurn = preference.getBoolean("canClickTurn",true);
// 从SharedPreferences中读取之前保存的是否允许点击翻页的配置值如果不存在则使用默认值true赋值给canClickTurn变量用于确定当前是否允许点击翻页操作。
this.canClickTurn = preference.getBoolean("canClickTurn", true);
// 从SharedPreferences中读取之前保存的是否允许按键翻页的配置值同样若不存在使用默认值true赋值给canKeyTurn变量用于判断当前是否允许按键翻页操作这里代码可能存在重复设置canClickTurn的问题应该是读取另一个键对应的布尔值比如"canKeyTurn"对应的配置值,此处可能需要修正)。
this.canKeyTurn = preference.getBoolean("canClickTurn", true);
}
/**
* 便
* @return
*/
public int getTextSize() {
return textSize;
}
/**
* 使
* @return
*/
public int getTextExtra() {
return textExtra;
}
/**
* 使
* @return AndroidARGB
*/
public int getTextColor() {
return textColor;
}
/**
*
* @return R.drawableID
*/
public int getTextBackground() {
return textBackground;
}
/**
*
* @return textKind
*/
public int getTextKindIndex() {
return textKindIndex;
}
/**
* SharedPreferences
* 便
* @param textKindIndex textKind
*/
public void setTextKindIndex(int textKindIndex) {
this.textKindIndex = textKindIndex;
SharedPreferences.Editor editor = preference.edit();
editor.putInt("textKindIndex",textKindIndex);
editor.putInt("textKindIndex", textKindIndex);
editor.commit();
this.textSize = textKind.get(textKindIndex).get("textSize");
this.textExtra = textKind.get(textKindIndex).get("textExtra");
}
/**
*
* @return textDrawable
*/
public int getTextDrawableIndex() {
return textDrawableIndex;
}
/**
* SharedPreferences
* 便
* @param textDrawableIndex textDrawable
*/
public void setTextDrawableIndex(int textDrawableIndex) {
this.textDrawableIndex = textDrawableIndex;
SharedPreferences.Editor editor = preference.edit();
editor.putInt("textDrawableIndex",textDrawableIndex);
editor.putInt("textDrawableIndex", textDrawableIndex);
editor.commit();
this.textColor = textDrawable.get(textDrawableIndex).get("textColor");
this.textBackground = textDrawable.get(textDrawableIndex).get("textBackground");
}
/**
*
*
* @return Map
*/
public static List<Map<String, Integer>> getTextKind() {
return textKind;
}
/**
*
* 便
* @return Map
*/
public static List<Map<String, Integer>> getTextDrawable() {
return textDrawable;
}
/**
*
* @return truefalse
*/
public Boolean getCanKeyTurn() {
return canKeyTurn;
}
public void setCanKeyTurn(Boolean canKeyTurn) {
this.canKeyTurn = canKeyTurn;
SharedPreferences.Editor editor = preference.edit();
editor.putBoolean("canKeyTurn",canKeyTurn);
editor.commit();
}
public Boolean getCanClickTurn() {
return canClickTurn;
}
public void setCanClickTurn(Boolean canClickTurn) {
this.canClickTurn = canClickTurn;
SharedPreferences.Editor editor = preference.edit();
editor.putBoolean("canClickTurn",canClickTurn);
editor.commit();
}
}
/**
* SharedPreferences
*

@ -1,20 +1,17 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.base;
import com.monke.basemvplib.IPresenter;
import com.monke.basemvplib.impl.BaseActivity;
import com.umeng.analytics.MobclickAgent;
public abstract class MBaseActivity<T extends IPresenter> extends BaseActivity<T>{
//定义一个抽象的Activity类MBaseActivity它继承自BaseActivity并且指定了泛型类型T必须实现IPresenter接口
//这个类可以作为项目中其他具体Activity的基类来统一处理一些公共逻辑比如友盟统计相关逻辑等
public abstract class MBaseActivity<T extends IPresenter> extends BaseActivity<T> {
//重写Activity的onResume方法该方法在Activity可见并准备好与用户交互时被调用
@Override
protected void onResume() {
//首先调用父类BaseActivity的onResume方法确保父类中相关的onResume逻辑得以执行比如可能存在的一些界面恢复、初始化等逻辑
super.onResume();
//调用友盟统计的onResume方法传入当前Activity实例目的是通知友盟统计该页面恢复到前台显示状态了以便友盟准确统计该页面的相关数据比如页面停留时长等
MobclickAgent.onResume(this);
}
@Override
protected void onPause() {
super.onPause();
MobclickAgent.onPause(this);
}
}
}

@ -1,40 +1,58 @@
package com.monke.monkeybook.base;
import com.monke.basemvplib.EncodoConverter;
import com.monke.basemvplib.impl.RetryIntercepter;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;
public class MBaseModelImpl {
// 定义一个受保护的 OkHttpClient.Builder 类型的变量 clientBuilder
// 通过创建 OkHttpClient.Builder 的实例来构建一个 OkHttpClient 的配置构建器,
// 后续可以基于这个构建器来添加各种配置项,例如设置超时时间和添加拦截器等
protected OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
// 设置连接超时时间为 10 秒,即客户端尝试连接服务器的最长等待时间,超过这个时间若还未成功连接则认为连接超时
.connectTimeout(10, TimeUnit.SECONDS)
// 设置写超时时间为 10 秒,指客户端向服务器写入数据的最长时间限制,防止出现长时间写入无响应的情况
.writeTimeout(10, TimeUnit.SECONDS)
// 设置读超时时间为 10 秒,意味着客户端从服务器读取数据的最长允许时间,避免长时间等待读取数据导致阻塞等问题
.readTimeout(10, TimeUnit.SECONDS)
// 添加一个名为 ProxyInterceptor 的拦截器,这个拦截器可能用于对网络请求进行一些自定义的拦截处理,
// 比如添加请求头、修改请求参数、对请求进行代理转发等操作,但具体功能取决于 ProxyInterceptor 类的实现逻辑
.addInterceptor(new ProxyInterceptor());
// 定义一个受保护的方法 getRetrofitObject用于构建并返回一个 Retrofit 实例,
// 这个方法接收一个表示网络请求基础 URL 的字符串参数,用于配置 Retrofit 实例与哪个服务器地址进行通信
protected Retrofit getRetrofitObject(String url) {
// 通过 Retrofit.Builder 创建一个 Retrofit 的构建器实例,开始构建 Retrofit 对象,
// 构建过程中通过链式调用各种方法来添加不同的配置项
return new Retrofit.Builder().baseUrl(url)
//增加返回值为字符串的支持(以实体类返回)
// 添加 ScalarsConverterFactory使得 Retrofit 支持网络请求直接返回原始字符串类型的数据,
// 方便处理一些简单的文本数据返回的网络请求场景,不需要额外的复杂数据解析步骤
.addConverterFactory(ScalarsConverterFactory.create())
//增加返回值为Oservable<T>的支持
// 添加 RxJava2CallAdapterFactory让 Retrofit 能够将网络请求的返回结果转换为 RxJava 的 Observable 类型,
// 便于后续利用 RxJava 的响应式编程特性(如链式操作、异步处理、错误处理等)来处理网络请求的结果
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
// 将之前配置好的 OkHttpClient 实例(通过 clientBuilder 构建完成)设置到 Retrofit 中,
// 这样 Retrofit 在发起网络请求时就会使用这个配置好的客户端来进行实际的 HTTP 操作
.client(clientBuilder.build())
// 最后通过 build 方法完成 Retrofit 对象的构建并返回,得到一个可用于发起具体网络请求的 Retrofit 实例
.build();
}
protected Retrofit getRetrofitString(String url, String encode) {
// 与 getRetrofitObject 方法类似,通过 Retrofit.Builder 开始构建 Retrofit 对象,先指定基础 URL
return new Retrofit.Builder().baseUrl(url)
//增加返回值为字符串的支持(以实体类返回)
// 添加 EncodoConverter通过传入指定的编码格式 encode使得 Retrofit 在处理返回值为字符串的网络请求时,
// 可以按照这个编码格式进行数据的转换或解析等操作,确保返回的字符串数据符合预期的编码要求
.addConverterFactory(EncodoConverter.create(encode))
//增加返回值为Oservable<T>的支持
// 同样添加 RxJava2CallAdapterFactory使 Retrofit 支持将返回结果转换为 RxJava 的 Observable 类型,便于后续进行响应式编程处理
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
// 将配置好的 OkHttpClient 实例设置到 Retrofit 中,为网络请求提供实际的客户端支持
.client(clientBuilder.build())
// 完成 Retrofit 对象的构建并返回,得到一个适合处理特定编码格式字符串返回值的网络请求的 Retrofit 实例
.build();
}
}

@ -2,52 +2,92 @@ package com.monke.monkeybook.base;
import com.monke.monkeybook.ProxyManager;
import com.monke.monkeybook.utils.aes.AESUtil;
import org.jsoup.helper.StringUtil;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.UUID;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
public class ProxyInterceptor implements Interceptor {
// 类的默认构造函数,目前为空实现,不过在一些场景下可能后续会添加一些初始化相关的逻辑,
// 比如初始化一些成员变量等,但目前仅作为满足类构造的基本要求
public ProxyInterceptor() {
}
// 重写 Interceptor 接口中的 intercept 方法,这个方法就是拦截器发挥作用的核心方法,
// 当网络请求经过这个拦截器时,会执行该方法内定义的逻辑,在这里可以对请求进行修改、重定向,或者对响应进行处理等操作
@Override
public Response intercept(Chain chain) throws IOException {
// 获取当前拦截到的网络请求对象,这个对象包含了本次请求原本的所有信息,
// 例如请求的 URL、请求头、请求方法等后续可以基于这个原始请求进行各种修改操作
Request oldRequest = chain.request();
// 通过 ProxyManager 类的 hasProxy 方法判断当前是否处于代理模式,
// 如果是代理模式,则执行后续优先请求代理服务器的相关逻辑;若不是代理模式,则直接继续原请求链路,
// 最后返回原请求对应的响应结果(在代码后面有对应的处理逻辑)
if (ProxyManager.hasProxy()) { //如果是代理模式则优先请求代理服务器,失败再自行本地请求
//获取request的创建者builder
// 获取当前请求对象的构建器Builder通过这个构建器可以方便地对请求进行修改
// 例如修改请求头、修改 URL、修改请求方法等而不需要直接操作原始的请求对象符合构建者设计模式的理念
Request.Builder builder = oldRequest.newBuilder();
// 获取原始请求的 URL 并转换为字符串形式,后续可以基于这个字符串 URL 进行编码等操作,
// 确保 URL 在传递给代理服务器等场景下格式正确且符合要求
String oldUrl = oldRequest.url().toString();
// 使用 StringUtil 工具类的 isBlank 方法判断获取到的原始 URL 字符串是否为空(包括 null 或者空字符串的情况),
// 如果不为空,则对其进行 UTF-8 编码处理,避免 URL 中包含特殊字符导致解析错误等问题
if (!StringUtil.isBlank(oldUrl)) {
oldUrl = URLEncoder.encode(oldUrl, "utf-8");
}
try {
// 生成一个临时字符串,它由应用的包名(通过 ProxyManager.packageName 获取)、
// 一个随机生成的 UUID通过 UUID.randomUUID().toString() 获取保证每次请求有唯一标识以及当前系统时间戳System.currentTimeMillis())组成,
// 这个临时字符串可能用于后续在代理请求中作为一个标识或者加密的原始数据等用途
String temp = ProxyManager.packageName + UUID.randomUUID().toString() + System.currentTimeMillis();
// 使用 AESUtil 类的 aesEncode 方法对临时字符串进行 AES 加密操作,传入的加密密钥是通过 ProxyManager.PROXY_PACKAGENAME_ENCODE 获取的,
// 加密后的结果将作为一个重要参数用于构建代理请求等操作,以保证数据传输的安全性或者符合代理服务器的特定要求
String key = AESUtil.aesEncode(temp.trim(), ProxyManager.PROXY_PACKAGENAME_ENCODE);
try {
key = URLEncoder.encode(key,"utf-8");
// 对加密后的 key 再次进行 UTF-8 编码操作,确保它在作为 URL 查询参数等传递过程中格式正确,
// 如果编码过程中出现异常(例如不支持的字符等情况),则直接使用原始未再次编码的 key也就是 temp.trim() 的值)
key = URLEncoder.encode(key, "utf-8");
} catch (Exception e) {
key = temp.trim();
}
// 使用 HttpUrl.parse 方法将代理服务器的地址(通过 ProxyManager.proxyHttp 获取)解析为 HttpUrl 对象,
// 然后通过 newBuilder 方法获取这个 URL 的构建器,以便后续添加查询参数等操作来构建完整的代理请求 URL
HttpUrl newBaseUrl = HttpUrl.parse(ProxyManager.proxyHttp).newBuilder()
// 使用 setQueryParameter 方法添加名为 "proxyUrl" 的查询参数,其值为前面处理好的原始请求 URL经过编码后的 oldUrl
// 这样代理服务器就能知道要代理的具体目标 URL 是什么了
.setQueryParameter("proxyUrl", oldUrl)
// 再添加名为 "proxyPackagename" 的查询参数,其值为前面处理好的加密 key经过编码或者原始的 key
// 这个参数可能用于代理服务器识别请求来源、进行权限验证等用途
.setQueryParameter("proxyPackagename", key)
// 最后通过 build 方法构建出完整的代理请求 URL 对象
.build();
// 使用 chain.proceed 方法发起实际的代理请求,传入构建好的新请求对象(通过 builder.url(newBaseUrl).build() 构建),
// 这个方法会继续执行网络请求链路,直到获取到代理服务器返回的响应对象,后续可以对这个响应进行判断和处理
Response response = chain.proceed(builder.url(newBaseUrl).build());
// 判断代理请求的响应是否成功(通常根据响应的状态码等判断,例如状态码在 200 - 299 之间表示成功),
// 如果成功,则直接返回这个代理请求得到的响应对象,不再进行后续的原请求相关处理了
if (response.isSuccessful()) {
return response;
}
} catch (Exception e) {
// 如果在构建代理请求、进行加密操作、发起代理请求等过程中出现任何异常情况,
// 在这里目前只是捕获异常但没有做任何具体处理(可能后续可以添加日志记录、错误提示等逻辑),
// 然后会继续执行后续原请求相关的逻辑,尝试直接发起本地请求获取响应
}
}
// 如果不是代理模式或者代理请求失败了,则继续执行原请求链路,通过 chain.proceed 方法发起原始的网络请求,
// 并获取对应的响应对象,最后返回这个原请求得到的响应对象,完成整个拦截器的逻辑处理
Response oldResponse = chain.proceed(oldRequest);
return oldResponse;
}

@ -1,12 +1,13 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.base.observer;
import com.monke.monkeybook.utils.NetworkUtil;
public class SimpleObserClass<T> {
private int code;
private T t;
//构造函数接收一个泛型类型的参数t调用另一个构造函数并传递t和默认的成功状态码
public SimpleObserClass(T t){
this(t,NetworkUtil.SUCCESS);
}
@ -16,23 +17,29 @@ public class SimpleObserClass<T> {
this.code = code;
}
//获取成员变量code的值即获取当前存储的状态码外部可以通过调用这个方法来了解相关操作的状态情况
public int getCode() {
return code;
}
//设置成员变量code的值用于在需要更新状态码的情况下从外部传入新的状态码来改变当前存储的状态码信息
public void setCode(int code) {
this.code = code;
}
//定义一个方法success用于判断当前的状态码是否与NetworkUtil中定义的表示成功的状态码相等
//返回一个布尔值如果相等则表示操作成功返回true否则返回false方便外部快速判断当前操作是否成功
public Boolean success(){
return code == NetworkUtil.SUCCESS;
}
//获取成员变量t的值即获取当前存储的泛型数据对象外部可以通过调用这个方法来获取具体的数据内容
public T getT() {
return t;
}
//设置成员变量t的值用于在需要更新数据对象的情况下从外部传入新的数据来替换当前存储的数据内容
public void setT(T t) {
this.t = t;
}
}
}

@ -1,18 +1,24 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.base.observer;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
public abstract class SimpleObserver<T> implements Observer<T> {
//重写Observer接口中的onSubscribe方法该方法会在观察者与被观察者建立订阅关系时被调用
//传入的Disposable参数用于管理这个订阅关系例如可以保存这个Disposable对象在合适的时候调用其dispose方法来取消订阅
//当前这个抽象类中此方法为空实现,具体的操作可以由子类根据业务需求来补充,比如记录订阅开始的相关日志等
@Override
public void onSubscribe(Disposable d) {
}
//重写Observer接口中的onComplete方法该方法会在被观察者完成所有数据的发送并且不会再发送任何新数据时被调用
//通常在这个方法里可以进行一些清理资源、更新UI显示等操作表示整个数据观察过程已经结束
//当前这个抽象类中此方法同样为空实现,由子类根据具体业务场景去决定如何处理这个事件,比如隐藏加载动画等操作
@Override
public void onComplete() {
}
}
}

@ -1,4 +1,3 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.bean;
import android.os.Parcel;
@ -13,57 +12,110 @@ import java.util.List;
/**
*
* Parcelable便便ActivityFragment
*/
@Entity
public class BookContentBean implements Parcelable{
@Id
private String durChapterUrl; //对应BookInfoBean noteUrl;
private int durChapterIndex; //当前章节 (包括番外)
private String durCapterContent; //当前章节内容
private String tag; //来源 某个网站/本地
public class BookContentBean implements Parcelable {
/**
* BookInfoBeannoteUrlURL使@Entity
*/
@Id
private String durChapterUrl;
/**
* 便
*/
private int durChapterIndex;
/**
*
*/
private String durCapterContent;
/**
* 便
*/
private String tag;
/**
* @Transient使
* true
*/
@Transient
private Boolean isRight = true;
/**
* @Transient使
* 便
*/
@Transient
private List<String> lineContent = new ArrayList<>();
/**
* @Transient使
* 使
*/
@Transient
private float lineSize;
public BookContentBean(){
/**
* BookContentBean
*/
public BookContentBean() {
}
/**
*
* @return float
*/
public float getLineSize() {
return lineSize;
}
/**
*
* @param lineSize float
*/
public void setLineSize(float lineSize) {
this.lineSize = lineSize;
}
/**
* ParcelBookContentBeanParcelable
* @param in Parcel
*/
protected BookContentBean(Parcel in) {
durChapterUrl = in.readString();
durChapterIndex = in.readInt();
durCapterContent = in.readString();
tag = in.readString();
lineContent = in.createStringArrayList();
isRight = in.readByte()!=0;
isRight = in.readByte()!= 0;
}
/**
* BookContentBean便
* @param durChapterUrl URL
* @param durChapterIndex
* @param durCapterContent
* @param tag
*/
@Generated(hash = 1355824386)
public BookContentBean(String durChapterUrl, int durChapterIndex,
String durCapterContent, String tag) {
String durCapterContent, String tag) {
this.durChapterUrl = durChapterUrl;
this.durChapterIndex = durChapterIndex;
this.durCapterContent = durCapterContent;
this.tag = tag;
}
/**
* BookContentBeanParcel便Parcelable
* @param dest Parcel
* @param flags 使使
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(durChapterUrl);
@ -71,14 +123,21 @@ public class BookContentBean implements Parcelable{
dest.writeString(durCapterContent);
dest.writeString(tag);
dest.writeStringList(lineContent);
dest.writeByte((byte) (isRight ? 1 : 0));
dest.writeByte((byte) (isRight? 1 : 0));
}
/**
* 0Parcelable
* @return 0
*/
@Override
public int describeContents() {
return 0;
}
/**
* BookContentBeanCreatorParcelable.CreatorParcelBookContentBeanParcelable
*/
@Transient
public static final Creator<BookContentBean> CREATOR = new Creator<BookContentBean>() {
@Override
@ -92,53 +151,101 @@ public class BookContentBean implements Parcelable{
}
};
/**
* URL
* @return URL
*/
public String getDurChapterUrl() {
return durChapterUrl;
}
/**
* URL
* @param durChapterUrl URL
*/
public void setDurChapterUrl(String durChapterUrl) {
this.durChapterUrl = durChapterUrl;
}
/**
*
* @return
*/
public int getDurChapterIndex() {
return durChapterIndex;
}
/**
*
* @param durChapterIndex
*/
public void setDurChapterIndex(int durChapterIndex) {
this.durChapterIndex = durChapterIndex;
}
/**
*
* @return
*/
public String getDurCapterContent() {
return durCapterContent;
}
/**
* isRightisRightfalse
* @param durCapterContent
*/
public void setDurCapterContent(String durCapterContent) {
this.durCapterContent = durCapterContent;
if(durCapterContent==null || durCapterContent.length()==0)
if (durCapterContent == null || durCapterContent.length() == 0)
this.isRight = false;
}
/**
*
* @return
*/
public String getTag() {
return tag;
}
/**
*
* @param tag
*/
public void setTag(String tag) {
this.tag = tag;
}
/**
*
* @return List<String>
*/
public List<String> getLineContent() {
return lineContent;
}
/**
*
* @param lineContent List<String>
*/
public void setLineContent(List<String> lineContent) {
this.lineContent = lineContent;
}
/**
*
* @return truefalse
*/
public Boolean getRight() {
return isRight;
}
/**
*
* @param right
*/
public void setRight(Boolean right) {
isRight = right;
}
}
}

@ -13,39 +13,88 @@ import java.util.List;
/**
*
* Parcelable便便
* Cloneable便使
*/
@Entity
public class BookInfoBean implements Parcelable,Cloneable{
public class BookInfoBean implements Parcelable, Cloneable {
/**
* @Transient使
* 1010 * 60 * 1000
*
*/
@Transient
public static final long REFRESH_DUR = 10*60*1000;
public static final long REFRESH_DUR = 10 * 60 * 1000;
/**
* 便
*/
private String name; //小说名
/**
*
*/
private String tag;
/**
* @Id使 GreenDao
* MD5
*/
@Id
private String noteUrl; //如果是来源网站 则小说根地址 /如果是本地 则是小说本地MD5
/**
*
*
*/
private String chapterUrl; //章节目录地址
/**
* @Transient使
* ChapterListBean便
*/
@Transient
private List<ChapterListBean> chapterlist = new ArrayList<>(); //章节列表
/**
* REFRESH_DUR
*
*/
private long finalRefreshData; //章节最后更新时间
/**
*
*/
private String coverUrl; //小说封面
private String author;//作者
/**
* 便
*/
private String author; //作者
/**
*
*/
private String introduce; //简介
/**
* 便
*/
private String origin; //来源
public BookInfoBean(){
/**
* BookInfoBean使
*/
public BookInfoBean() {
}
/**
* ParcelBookInfoBeanParcelable
* Parcel
* @param in Parcel
*/
protected BookInfoBean(Parcel in) {
name = in.readString();
tag = in.readString();
@ -59,10 +108,23 @@ public class BookInfoBean implements Parcelable,Cloneable{
origin = in.readString();
}
/**
* BookInfoBean便
*
* @param name
* @param tag
* @param noteUrl MD5
* @param chapterUrl
* @param finalRefreshData
* @param coverUrl
* @param author
* @param introduce
* @param origin
*/
@Generated(hash = 1627552162)
public BookInfoBean(String name, String tag, String noteUrl, String chapterUrl,
long finalRefreshData, String coverUrl, String author, String introduce,
String origin) {
long finalRefreshData, String coverUrl, String author, String introduce,
String origin) {
this.name = name;
this.tag = tag;
this.noteUrl = noteUrl;
@ -74,6 +136,12 @@ public class BookInfoBean implements Parcelable,Cloneable{
this.origin = origin;
}
/**
* BookInfoBeanParcel便Parcelable
* Parcel便
* @param dest Parcel
* @param flags 使使
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
@ -88,11 +156,18 @@ public class BookInfoBean implements Parcelable,Cloneable{
dest.writeString(origin);
}
/**
* 0Parcelable
* @return 0
*/
@Override
public int describeContents() {
return 0;
}
/**
* BookInfoBeanCreatorParcelable.CreatorParcelBookInfoBeanParcelable
*/
@Transient
public static final Creator<BookInfoBean> CREATOR = new Creator<BookInfoBean>() {
@Override
@ -106,89 +181,182 @@ public class BookInfoBean implements Parcelable,Cloneable{
}
};
/**
*
* @return
*/
public String getName() {
return name;
}
/**
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
*
* @return
*/
public String getTag() {
return tag;
}
/**
*
* @param tag
*/
public void setTag(String tag) {
this.tag = tag;
}
/**
* MD5
* @return noteUrl
*/
public String getNoteUrl() {
return noteUrl;
}
/**
* MD5
* @param noteUrl
*/
public void setNoteUrl(String noteUrl) {
this.noteUrl = noteUrl;
}
/**
*
* @return
*/
public String getChapterUrl() {
return chapterUrl;
}
/**
*
* @param chapterUrl
*/
public void setChapterUrl(String chapterUrl) {
this.chapterUrl = chapterUrl;
}
/**
*
* @return ChapterListBeanList
*/
public List<ChapterListBean> getChapterlist() {
return chapterlist;
}
/**
*
* @param chapterlist ChapterListBeanList
*/
public void setChapterlist(List<ChapterListBean> chapterlist) {
this.chapterlist = chapterlist;
}
public void addChapterlist(List<ChapterListBean> chapterlist){
/**
*
* @param chapterlist ChapterListBeanList
*/
public void addChapterlist(List<ChapterListBean> chapterlist) {
this.chapterlist.addAll(chapterlist);
}
/**
*
* @return
*/
public long getFinalRefreshData() {
return finalRefreshData;
}
/**
*
* @param finalRefreshData
*/
public void setFinalRefreshData(long finalRefreshData) {
this.finalRefreshData = finalRefreshData;
}
/**
*
* @return
*/
public String getCoverUrl() {
return coverUrl;
}
/**
*
* @param coverUrl
*/
public void setCoverUrl(String coverUrl) {
this.coverUrl = coverUrl;
}
/**
*
* @return
*/
public String getAuthor() {
return author;
}
/**
*
* @param author
*/
public void setAuthor(String author) {
this.author = author;
}
/**
*
* @return
*/
public String getIntroduce() {
return introduce;
}
/**
*
* @param introduce
*/
public void setIntroduce(String introduce) {
this.introduce = introduce;
}
/**
*
* @return
*/
public String getOrigin() {
return this.origin;
}
/**
*
* @param origin
*/
public void setOrigin(String origin) {
this.origin = origin;
}
/**
* ObjectcloneBookInfoBean
* clone
* ChapterListBean
* BookInfoBean
* @return BookInfoBean
* @throws CloneNotSupportedException Cloneable
*/
@Override
protected Object clone() throws CloneNotSupportedException {
BookInfoBean bookInfoBean = (BookInfoBean) super.clone();
@ -200,10 +368,10 @@ public class BookInfoBean implements Parcelable,Cloneable{
bookInfoBean.author = author;
bookInfoBean.introduce = introduce;
bookInfoBean.origin = origin;
if(chapterlist!=null){
if (chapterlist!= null) {
List<ChapterListBean> newList = new ArrayList<>();
Iterator<ChapterListBean> iterator = chapterlist.iterator();
while(iterator.hasNext()){
while (iterator.hasNext()) {
newList.add((ChapterListBean) iterator.next().clone());
}
bookInfoBean.setChapterlist(newList);

@ -11,33 +11,74 @@ import org.greenrobot.greendao.annotation.Transient;
/**
* item Bean
* Parcelable便Activity
* Cloneable便使
*/
@Entity
public class BookShelfBean implements Parcelable,Cloneable{
public class BookShelfBean implements Parcelable, Cloneable {
/**
* @Transient使
* 55 * 60 * 1000
*
*/
@Transient
public static final long REFRESH_TIME = 5*60*1000; //更新时间间隔 至少
public static final long REFRESH_TIME = 5 * 60 * 1000; //更新时间间隔 至少
/**
* @Transient使
* 便
*/
@Transient
public static final String LOCAL_TAG = "loc_book";
/**
* @Id使 GreenDao
* BookInfoBeannoteUrlMD5
*/
@Id
private String noteUrl; //对应BookInfoBean noteUrl;
/**
* 便
*/
private int durChapter; //当前章节 (包括番外)
/**
* BookContentView.DURPAGEINDEXBEGINBookContentView
*
*/
private int durChapterPage = BookContentView.DURPAGEINDEXBEGIN; // 当前章节位置 用页码
/**
*
*/
private long finalDate; //最后阅读时间
/**
* BookInfoBeantag使
*/
private String tag;
/**
* @Transient使
* BookInfoBean便使
*/
@Transient
private BookInfoBean bookInfoBean = new BookInfoBean();
public BookShelfBean(){
/**
* BookShelfBean使
*/
public BookShelfBean() {
}
/**
* ParcelBookShelfBeanParcelable
* ParcelParcelBookInfoBean
* @param in Parcel
*/
protected BookShelfBean(Parcel in) {
noteUrl = in.readString();
durChapter = in.readInt();
@ -47,9 +88,18 @@ public class BookShelfBean implements Parcelable,Cloneable{
bookInfoBean = in.readParcelable(BookInfoBean.class.getClassLoader());
}
/**
* BookShelfBean便
*
* @param noteUrl BookInfoBeannoteUrlMD5
* @param durChapter
* @param durChapterPage
* @param finalDate
* @param tag
*/
@Generated(hash = 2028192361)
public BookShelfBean(String noteUrl, int durChapter, int durChapterPage, long finalDate,
String tag) {
String tag) {
this.noteUrl = noteUrl;
this.durChapter = durChapter;
this.durChapterPage = durChapterPage;
@ -57,6 +107,12 @@ public class BookShelfBean implements Parcelable,Cloneable{
this.tag = tag;
}
/**
* BookShelfBeanParcel便Parcelable
* ParcelBookInfoBeanParcel便
* @param dest Parcel
* @param flags 使使
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(noteUrl);
@ -67,11 +123,18 @@ public class BookShelfBean implements Parcelable,Cloneable{
dest.writeParcelable(bookInfoBean, flags);
}
/**
* 0Parcelable
* @return 0
*/
@Override
public int describeContents() {
return 0;
}
/**
* BookShelfBeanCreatorParcelable.CreatorParcelBookShelfBeanParcelable
*/
@Transient
public static final Creator<BookShelfBean> CREATOR = new Creator<BookShelfBean>() {
@Override
@ -85,54 +148,110 @@ public class BookShelfBean implements Parcelable,Cloneable{
}
};
/**
* BookInfoBeannoteUrlMD5
* @return noteUrl
*/
public String getNoteUrl() {
return noteUrl;
}
/**
* BookInfoBeannoteUrlMD5
* @param noteUrl
*/
public void setNoteUrl(String noteUrl) {
this.noteUrl = noteUrl;
}
/**
*
* @return
*/
public int getDurChapter() {
return durChapter;
}
/**
*
* @param durChapter
*/
public void setDurChapter(int durChapter) {
this.durChapter = durChapter;
}
/**
*
* @return
*/
public int getDurChapterPage() {
return durChapterPage;
}
/**
*
* @param durChapterPage
*/
public void setDurChapterPage(int durChapterPage) {
this.durChapterPage = durChapterPage;
}
/**
*
* @return
*/
public long getFinalDate() {
return finalDate;
}
/**
*
* @param finalDate
*/
public void setFinalDate(long finalDate) {
this.finalDate = finalDate;
}
/**
*
* @return
*/
public String getTag() {
return tag;
}
/**
*
* @param tag
*/
public void setTag(String tag) {
this.tag = tag;
}
/**
* BookInfoBean
* @return BookInfoBean
*/
public BookInfoBean getBookInfoBean() {
return bookInfoBean;
}
/**
* BookInfoBean
* @param bookInfoBean BookInfoBean
*/
public void setBookInfoBean(BookInfoBean bookInfoBean) {
this.bookInfoBean = bookInfoBean;
}
/**
* ObjectcloneBookShelfBean
* clone
* BookInfoBeanclone
* BookShelfBean
* @return BookShelfBean
* @throws CloneNotSupportedException Cloneable
*/
@Override
public Object clone() throws CloneNotSupportedException {
BookShelfBean bookShelfBean = (BookShelfBean) super.clone();

@ -10,24 +10,57 @@ import org.greenrobot.greendao.annotation.Generated;
/**
*
* Parcelable便便
* Cloneable便使
*/
@Entity
public class ChapterListBean implements Parcelable,Cloneable{
public class ChapterListBean implements Parcelable, Cloneable {
/**
* BookInfoBeannoteUrlMD5
*
*/
private String noteUrl; //对应BookInfoBean noteUrl;
/**
* 便
*/
private int durChapterIndex; //当前章节数
/**
* @Id使 GreenDao
*
*/
@Id
private String durChapterUrl; //当前章节对应的文章地址
/**
* 便便
*/
private String durChapterName; //当前章节名称
/**
*
*/
private String tag;
/**
* falsetrue便
*/
private Boolean hasCache = false;
/**
* @Transient使
* BookContentBean便
*/
@Transient
private BookContentBean bookContentBean = new BookContentBean();
/**
* ParcelChapterListBeanParcelable
* ParcelParcelBookContentBean
* @param in Parcel
*/
protected ChapterListBean(Parcel in) {
noteUrl = in.readString();
durChapterIndex = in.readInt();
@ -35,12 +68,22 @@ public class ChapterListBean implements Parcelable,Cloneable{
durChapterName = in.readString();
tag = in.readString();
bookContentBean = in.readParcelable(BookContentBean.class.getClassLoader());
hasCache = in.readByte() != 0;
hasCache = in.readByte()!= 0;
}
/**
* ChapterListBean便
*
* @param noteUrl BookInfoBeannoteUrlMD5
* @param durChapterIndex
* @param durChapterUrl
* @param durChapterName
* @param tag
* @param hasCache
*/
@Generated(hash = 1225922702)
public ChapterListBean(String noteUrl, int durChapterIndex, String durChapterUrl,
String durChapterName, String tag, Boolean hasCache) {
String durChapterName, String tag, Boolean hasCache) {
this.noteUrl = noteUrl;
this.durChapterIndex = durChapterIndex;
this.durChapterUrl = durChapterUrl;
@ -49,10 +92,19 @@ public class ChapterListBean implements Parcelable,Cloneable{
this.hasCache = hasCache;
}
/**
* ChapterListBean使
*/
@Generated(hash = 1096893365)
public ChapterListBean() {
}
/**
* ChapterListBeanParcel便Parcelable
* ParcelBookContentBeanParcel便
* @param dest Parcel
* @param flags 使使
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(noteUrl);
@ -61,70 +113,133 @@ public class ChapterListBean implements Parcelable,Cloneable{
dest.writeString(durChapterName);
dest.writeString(tag);
dest.writeParcelable(bookContentBean, flags);
dest.writeByte((byte)(hasCache?1:0));
dest.writeByte((byte) (hasCache? 1 : 0));
}
/**
* 0Parcelable
* @return 0
*/
@Override
public int describeContents() {
return 0;
}
/**
* BookContentBean
* @return BookContentBean
*/
public BookContentBean getBookContentBean() {
return bookContentBean;
}
/**
* BookContentBean
* @param bookContentBean BookContentBean
*/
public void setBookContentBean(BookContentBean bookContentBean) {
this.bookContentBean = bookContentBean;
}
/**
*
* @return truefalse
*/
public Boolean getHasCache() {
return this.hasCache;
}
/**
*
* @param hasCache
*/
public void setHasCache(Boolean hasCache) {
this.hasCache = hasCache;
}
/**
*
* @return
*/
public String getTag() {
return this.tag;
}
/**
*
* @param tag
*/
public void setTag(String tag) {
this.tag = tag;
}
/**
*
* @return
*/
public String getDurChapterName() {
return this.durChapterName;
}
/**
*
* @param durChapterName
*/
public void setDurChapterName(String durChapterName) {
this.durChapterName = durChapterName;
}
/**
*
* @return
*/
public String getDurChapterUrl() {
return this.durChapterUrl;
}
/**
*
* @param durChapterUrl
*/
public void setDurChapterUrl(String durChapterUrl) {
this.durChapterUrl = durChapterUrl;
}
/**
*
* @return
*/
public int getDurChapterIndex() {
return this.durChapterIndex;
}
/**
*
* @param durChapterIndex
*/
public void setDurChapterIndex(int durChapterIndex) {
this.durChapterIndex = durChapterIndex;
}
/**
* BookInfoBeannoteUrlMD5
* @return
*/
public String getNoteUrl() {
return this.noteUrl;
}
/**
* BookInfoBeannoteUrlMD5
* @param noteUrl
*/
public void setNoteUrl(String noteUrl) {
this.noteUrl = noteUrl;
}
/**
* ChapterListBeanCreatorParcelable.CreatorParcelChapterListBeanParcelable
*/
@Transient
public static final Creator<ChapterListBean> CREATOR = new Creator<ChapterListBean>() {
@Override
@ -138,6 +253,14 @@ public class ChapterListBean implements Parcelable,Cloneable{
}
};
/**
* ObjectcloneChapterListBean
* clone
* BookContentBeanBookContentBean
* ChapterListBean
* @return ChapterListBean
* @throws CloneNotSupportedException Cloneable
*/
@Override
protected Object clone() throws CloneNotSupportedException {
ChapterListBean chapterListBean = (ChapterListBean) super.clone();
@ -149,4 +272,4 @@ public class ChapterListBean implements Parcelable,Cloneable{
chapterListBean.bookContentBean = new BookContentBean();
return chapterListBean;
}
}
}

@ -8,22 +8,60 @@ import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Transient;
import org.greenrobot.greendao.annotation.Generated;
/**
* DownloadChapterBeanParcelable便便
* 使@Entity使
*/
@Entity
public class DownloadChapterBean implements Parcelable {
/**
* BookInfoBeannoteUrlMD5
* 便
*/
private String noteUrl;
/**
* 便
*
*/
private int durChapterIndex; //当前章节数
/**
* @Id使 GreenDao
*
*
*/
@Id
private String durChapterUrl; //当前章节对应的文章地址
/**
* 便便
*/
private String durChapterName; //当前章节名称
/**
*
*
*/
private String tag;
/**
* 便
*/
private String bookName;
/**
*
*
*/
private String coverUrl; //小说封面
/**
* ParcelDownloadChapterBeanParcelable
* Parcel
* @param in Parcel
*/
protected DownloadChapterBean(Parcel in) {
noteUrl = in.readString();
durChapterIndex = in.readInt();
@ -34,9 +72,20 @@ public class DownloadChapterBean implements Parcelable {
coverUrl = in.readString();
}
/**
* DownloadChapterBean便
*
* @param noteUrl BookInfoBeannoteUrlMD5
* @param durChapterIndex
* @param durChapterUrl
* @param durChapterName
* @param tag
* @param bookName
* @param coverUrl
*/
@Generated(hash = 757008458)
public DownloadChapterBean(String noteUrl, int durChapterIndex, String durChapterUrl,
String durChapterName, String tag, String bookName, String coverUrl) {
String durChapterName, String tag, String bookName, String coverUrl) {
this.noteUrl = noteUrl;
this.durChapterIndex = durChapterIndex;
this.durChapterUrl = durChapterUrl;
@ -46,10 +95,16 @@ public class DownloadChapterBean implements Parcelable {
this.coverUrl = coverUrl;
}
/**
* DownloadChapterBean使
*/
@Generated(hash = 301211198)
public DownloadChapterBean() {
}
/**
* DownloadChapterBeanCreatorParcelable.CreatorParcelDownloadChapterBeanParcelable
*/
@Transient
public static final Creator<DownloadChapterBean> CREATOR = new Creator<DownloadChapterBean>() {
@Override
@ -63,11 +118,21 @@ public class DownloadChapterBean implements Parcelable {
}
};
/**
* 0Parcelable
* @return 0
*/
@Override
public int describeContents() {
return 0;
}
/**
* DownloadChapterBeanParcel便Parcelable
* Parcel便
* @param dest Parcel
* @param flags 使使
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(noteUrl);
@ -79,59 +144,115 @@ public class DownloadChapterBean implements Parcelable {
dest.writeString(coverUrl);
}
/**
* BookInfoBeannoteUrlMD5
* @return
*/
public String getNoteUrl() {
return noteUrl;
}
/**
* BookInfoBeannoteUrlMD5
* @param noteUrl
*/
public void setNoteUrl(String noteUrl) {
this.noteUrl = noteUrl;
}
/**
*
* @return
*/
public int getDurChapterIndex() {
return durChapterIndex;
}
/**
*
* @param durChapterIndex
*/
public void setDurChapterIndex(int durChapterIndex) {
this.durChapterIndex = durChapterIndex;
}
/**
*
* @return
*/
public String getDurChapterUrl() {
return durChapterUrl;
}
/**
*
* @param durChapterUrl
*/
public void setDurChapterUrl(String durChapterUrl) {
this.durChapterUrl = durChapterUrl;
}
/**
*
* @return
*/
public String getDurChapterName() {
return durChapterName;
}
/**
*
* @param durChapterName
*/
public void setDurChapterName(String durChapterName) {
this.durChapterName = durChapterName;
}
/**
*
* @return
*/
public String getTag() {
return tag;
}
/**
*
* @param tag
*/
public void setTag(String tag) {
this.tag = tag;
}
/**
*
* @return
*/
public String getBookName() {
return bookName;
}
/**
*
* @param bookName
*/
public void setBookName(String bookName) {
this.bookName = bookName;
}
/**
*
* @return
*/
public String getCoverUrl() {
return coverUrl;
}
/**
*
* @param coverUrl
*/
public void setCoverUrl(String coverUrl) {
this.coverUrl = coverUrl;
}
}
}

@ -8,20 +8,42 @@ import java.util.List;
/**
*
* Parcelable便
* 使便
*/
public class DownloadChapterListBean implements Parcelable {
/**
* DownloadChapterBean
* 便
*/
private List<DownloadChapterBean> data;
/**
* DownloadChapterListBean
* DownloadChapterBeandata
* @param result DownloadChapterBean
*/
public DownloadChapterListBean(List<DownloadChapterBean> result) {
this.data = result;
}
/**
* ParcelDownloadChapterListBeanParcelable
* datanullArrayListParcelDownloadChapterBean
* DownloadChapterBeanCREATORdata
* @param in Parcel
*/
protected DownloadChapterListBean(Parcel in) {
if(data == null)
if (data == null)
data = new ArrayList<>();
in.readTypedList(data,DownloadChapterBean.CREATOR);
in.readTypedList(data, DownloadChapterBean.CREATOR);
}
/**
* DownloadChapterListBeanCreatorParcelable.CreatorParcelDownloadChapterListBean
* Parcelable使便
*/
public static final Creator<DownloadChapterListBean> CREATOR = new Creator<DownloadChapterListBean>() {
@Override
public DownloadChapterListBean createFromParcel(Parcel in) {
@ -34,21 +56,39 @@ public class DownloadChapterListBean implements Parcelable {
}
};
/**
*
* @return DownloadChapterBean
*/
public List<DownloadChapterBean> getData() {
return data;
}
/**
*
* @param data DownloadChapterBean
*/
public void setData(List<DownloadChapterBean> data) {
this.data = data;
}
/**
* 0Parcelable
* @return 0
*/
@Override
public int describeContents() {
return 0;
}
/**
* DownloadChapterListBeanParcel便Parcelable
* writeTypedListdataDownloadChapterBeanParcel便
* @param dest Parcel
* @param flags 使
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedList(data);
}
}
}

@ -5,24 +5,51 @@ import java.util.List;
/**
* Data bean
* 便
*
*/
public class LibraryBean {
/**
* LibraryNewBookBean
* 便
*/
private List<LibraryNewBookBean> libraryNewBooks;
/**
* LibraryKindBookListBean
* 便
*/
private List<LibraryKindBookListBean> kindBooks;
/**
* 便
* @return LibraryNewBookBean
*/
public List<LibraryNewBookBean> getLibraryNewBooks() {
return libraryNewBooks;
}
/**
*
* @param libraryNewBooks LibraryNewBookBean
*/
public void setLibraryNewBooks(List<LibraryNewBookBean> libraryNewBooks) {
this.libraryNewBooks = libraryNewBooks;
}
/**
* 便
* @return LibraryKindBookListBean
*/
public List<LibraryKindBookListBean> getKindBooks() {
return kindBooks;
}
/**
*
*/
public void setKindBooks(List<LibraryKindBookListBean> kindBooks) {
this.kindBooks = kindBooks;
}
}
}

@ -5,33 +5,73 @@ import java.util.List;
/**
*
*
*
*/
public class LibraryKindBookListBean {
/**
* 便
*/
private String kindName;
/**
*
*
*/
private String kindUrl;
/**
* SearchBookBean
*
*/
private List<SearchBookBean> books;
/**
* 便
* @return
*/
public String getKindName() {
return kindName;
}
/**
*
* @param kindName
*/
public void setKindName(String kindName) {
this.kindName = kindName;
}
public List<SearchBookBean> getBooks() {
/**
* 便
* @return SearchBookBean
*/
public String getBooks() {
return books;
}
/**
*
* @param books SearchBookBean
*/
public void setBooks(List<SearchBookBean> books) {
this.books = books;
}
/**
* 便
* @return
*/
public String getKindUrl() {
return kindUrl;
}
/**
*
* @param kindUrl
*/
public void setKindUrl(String kindUrl) {
this.kindUrl = kindUrl;
}
}
}

@ -1,12 +1,45 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.bean;
/**
* LibraryNewBookBean便
*
*/
public class LibraryNewBookBean {
/**
* 便
*
*/
private String name;
/**
*
*
*
*/
private String url;
/**
*
* 便
*/
private String tag;
/**
*
*
*/
private String orgin;
/**
* LibraryNewBookBean
* 便使
* @param name
* @param url
* @param tag
* @param orgin
*/
public LibraryNewBookBean(String name, String url, String tag, String orgin) {
this.name = name;
this.url = url;
@ -14,35 +47,75 @@ public class LibraryNewBookBean {
this.orgin = orgin;
}
/**
*
* 便
* @return
*/
public String getOrgin() {
return orgin;
}
/**
*
*
* @param orgin
*/
public void setOrgin(String orgin) {
this.orgin = orgin;
}
/**
*
* 便使
* @return
*/
public String getTag() {
return tag;
}
/**
*
*
* @param tag
*/
public void setTag(String tag) {
this.tag = tag;
}
/**
* 便使
*
* @return
*/
public String getName() {
return name;
}
/**
*
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
*
*
* @return
*/
public String getUrl() {
return url;
}
/**
*
*
* @param url
*/
public void setUrl(String url) {
this.url = url;
}
}
}

@ -1,28 +1,68 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.bean;
/**
* LocBookShelfBeanBookShelfBean
* 便使
*/
public class LocBookShelfBean {
/**
* truefalse
*
*/
private Boolean isNew;
/**
* BookShelfBean
*
*/
private BookShelfBean bookShelfBean;
public LocBookShelfBean(Boolean isNew,BookShelfBean bookShelfBean){
/**
* LocBookShelfBean
* 便使
* @param isNew truefalse
* @param bookShelfBean BookShelfBean
*/
public LocBookShelfBean(Boolean isNew, BookShelfBean bookShelfBean) {
this.isNew = isNew;
this.bookShelfBean = bookShelfBean;
}
/**
*
* 便
* @return truefalse
*/
public Boolean getNew() {
return isNew;
}
/**
*
* 便
* @param aNew truefalse
*/
public void setNew(Boolean aNew) {
isNew = aNew;
}
/**
* BookShelfBean
*
* @return BookShelfBean
*/
public BookShelfBean getBookShelfBean() {
return bookShelfBean;
}
/**
*
* BookShelfBean便
* @param bookShelfBean BookShelfBean
*/
public void setBookShelfBean(BookShelfBean bookShelfBean) {
this.bookShelfBean = bookShelfBean;
}
}
}

@ -3,28 +3,68 @@ package com.monke.monkeybook.bean;
import java.util.List;
/**
* ReadBookContentBeanBookContentBean
* 便使
*/
public class ReadBookContentBean {
/**
* BookContentBeanBookContentBean
* 便
*/
private List<BookContentBean> bookContentList;
/**
*
* 便
*/
private int pageIndex;
public ReadBookContentBean(List<BookContentBean> bookContentList,int pageIndex){
this.bookContentList = bookContentList;
/**
* ReadBookContentBean
* 便使
* @param bookContentList BookContentBean
* @param pageIndex
*/
public ReadBookContentBean(List<BookContentBean> bookContentList, int pageIndex) {
this.bookContentList = bookContentList;
this.pageIndex = pageIndex;
}
/**
*
* 便
* @return BookContentBean
*/
public List<BookContentBean> getBookContentList() {
return bookContentList;
}
/**
*
*
* @param bookContentList BookContentBean
*/
public void setBookContentList(List<BookContentBean> bookContentList) {
this.bookContentList = bookContentList;
}
/**
*
*
* @return
*/
public int getPageIndex() {
return pageIndex;
}
/**
*
* 便
* @param pageIndex
*/
public void setPageIndex(int pageIndex) {
this.pageIndex = pageIndex;
}
}
}

@ -4,36 +4,87 @@ package com.monke.monkeybook.bean;
import android.os.Parcel;
import android.os.Parcelable;
public class SearchBookBean implements Parcelable{
/**
* SearchBookBeanParcelable便
* 便
*/
public class SearchBookBean implements Parcelable {
/**
* `noteUrl` MD5
* 便
*/
private String noteUrl;
/**
*
*
*/
private String coverUrl;
/**
* 便便
*/
private String name;
/**
*
*/
private String author;
/**
*
*/
private long words;
/**
* 便
*/
private String state;
/**
*
*/
private String lastChapter;
/**
* `false`便
*/
private Boolean isAdd = false;
/**
* 便
*/
private String tag;
/**
* 便
*/
private String kind;
/**
*
*
*/
private String origin;
/**
*
*/
private String desc;
public SearchBookBean(){
/**
* SearchBookBean使便
*/
public SearchBookBean() {
}
/**
* ParcelSearchBookBeanParcelable
* Parcel
* @param in Parcel
*/
protected SearchBookBean(Parcel in) {
noteUrl = in.readString();
coverUrl = in.readString();
@ -42,13 +93,19 @@ public class SearchBookBean implements Parcelable{
words = in.readLong();
state = in.readString();
lastChapter = in.readString();
isAdd = in.readByte() != 0;
isAdd = in.readByte()!= 0;
tag = in.readString();
kind = in.readString();
origin = in.readString();
desc = in.readString();
}
/**
* SearchBookBeanParcel便Parcelable
* Parcel便
* @param dest Parcel
* @param flags 使使
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(noteUrl);
@ -58,18 +115,26 @@ public class SearchBookBean implements Parcelable{
dest.writeLong(words);
dest.writeString(state);
dest.writeString(lastChapter);
dest.writeByte((byte)(isAdd?1:0));
dest.writeByte((byte) (isAdd? 1 : 0));
dest.writeString(tag);
dest.writeString(kind);
dest.writeString(origin);
dest.writeString(desc);
}
/**
* 0Parcelable
* @return 0
*/
@Override
public int describeContents() {
return 0;
}
/**
* SearchBookBeanCreatorParcelable.CreatorParcelSearchBookBeanParcelable
* 使便使
*/
public static final Creator<SearchBookBean> CREATOR = new Creator<SearchBookBean>() {
@Override
public SearchBookBean createFromParcel(Parcel in) {
@ -82,98 +147,193 @@ public class SearchBookBean implements Parcelable{
}
};
/**
* 便
* @return noteUrl
*/
public String getNoteUrl() {
return noteUrl;
}
/**
*
* @param noteUrl
*/
public void setNoteUrl(String noteUrl) {
this.noteUrl = noteUrl;
}
/**
*
* @return
*/
public String getCoverUrl() {
return coverUrl;
}
/**
*
* @param coverUrl
*/
public void setCoverUrl(String coverUrl) {
this.coverUrl = coverUrl;
}
/**
* 便
* @return
*/
public String getName() {
return name;
}
/**
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
*
* @return
*/
public String getAuthor() {
return author;
}
/**
*
* @param author
*/
public void setAuthor(String author) {
this.author = author;
}
/**
*
* @return truefalse
*/
public Boolean getAdd() {
return isAdd;
}
/**
* 便
* @param add truefalse
*/
public void setAdd(Boolean add) {
isAdd = add;
}
/**
* 使
* @return
*/
public long getWords() {
return words;
}
/**
*
* @param words
*/
public void setWords(long words) {
this.words = words;
}
/**
* 使
* @return
*/
public String getState() {
return state;
}
/**
*
* @param state
*/
public void setState(String state) {
this.state = state;
}
/**
* 便
* @return null
*/
public String getLastChapter() {
return lastChapter==null?"":lastChapter;
return lastChapter == null? "" : lastChapter;
}
/**
* 便
* @param lastChapter
*/
public void setLastChapter(String lastChapter) {
this.lastChapter = lastChapter;
}
/**
* 便
* @return
*/
public String getKind() {
return kind;
}
/**
*
* @param kind
*/
public void setKind(String kind) {
this.kind = kind;
}
/**
* 便
* @return
*/
public String getTag() {
return tag;
}
/**
*
* @param tag
*/
public void setTag(String tag) {
this.tag = tag;
}
/**
*
* @return
*/
public String getOrigin() {
return origin;
}
/**
*
*/
public void setOrigin(String origin) {
this.origin = origin;
}
/**
*
* @return
*/
public String getDesc() {
return desc;
}
/**
*
* @param desc
*/
public void setDesc(String desc) {
this.desc = desc;
}

@ -6,43 +6,128 @@ import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Unique;
import org.greenrobot.greendao.annotation.Generated;
/**
* SearchHistoryBean使GreenRobotGreenDao@Entity@Id
* 便便
*/
@Entity
public class SearchHistoryBean {
/**
* @Id`autoincrement = true`
* 便
* `null`
*/
@Id(autoincrement = true)
private Long id = null;
/**
*
*
*/
private int type;
/**
* 便
*
*/
private String content;
/**
*
* 便
*/
private long date;
/**
*
* @return
*/
public long getDate() {
return this.date;
}
/**
*
* @param date
*/
public void setDate(long date) {
this.date = date;
}
/**
*
* 使
* @return
*/
public String getContent() {
return this.content;
}
/**
*
* @param content
*/
public void setContent(String content) {
this.content = content;
}
/**
*
*
* @return
*/
public int getType() {
return this.type;
}
/**
*
* @param type
*/
public void setType(int type) {
this.type = type;
}
/**
*
* 使
* @return Long
*/
public Long getId() {
return this.id;
}
/**
*
*
* @param id Long
*/
public void setId(Long id) {
this.id = id;
}
/**
* SearchHistoryBean
* 便使
* @param type
* @param content
* @param date
*/
public SearchHistoryBean(int type, String content, long date) {
this.type = type;
this.content = content;
this.date = date;
}
/**
* GreenDao使@Generated
* SearchHistoryBean
* @param id Long
* @param type
* @param content
* @param date
*/
@Generated(hash = 488115752)
public SearchHistoryBean(Long id, int type, String content, long date) {
this.id = id;
@ -50,8 +135,12 @@ public class SearchHistoryBean {
this.content = content;
this.date = date;
}
/**
* GreenDaoSearchHistoryBean使
* 使`id``null``type`便
*/
@Generated(hash = 1570282321)
public SearchHistoryBean() {
}
}
}

@ -1,29 +1,68 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.bean;
/**
* WebChapterBeanT
* 便
*/
public class WebChapterBean<T> {
/**
*
* 便使
*/
private T data;
/**
* truefalse
*
*/
private Boolean next;
public WebChapterBean(T data,Boolean next){
/**
* WebChapterBean
* 便使
* @param data T
* @param next truefalse
*/
public WebChapterBean(T data, Boolean next) {
this.data = data;
this.next = next;
}
/**
*
* 便
* @return T
*/
public T getData() {
return data;
}
/**
*
* 使
* @param data T
*/
public void setData(T data) {
this.data = data;
}
/**
*
*
* @return truefalse
*/
public Boolean getNext() {
return next;
}
/**
*
* 便
* @param next truefalse
*/
public void setNext(Boolean next) {
this.next = next;
}
}
}

@ -1,28 +1,68 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.bean;
/**
* WebContentBean
* 便
*/
public class WebContentBean {
/**
* URL "https://www.example.com/book/chapter1.html"
* 便访使
*/
private String url;
/**
* HTML
* 使
*/
private String content;
public WebContentBean(String url,String content){
/**
* WebContentBean
* 便使
* @param url URL
* @param content HTML
*/
public WebContentBean(String url, String content) {
this.url = url;
this.content = content;
}
/**
*
* 使
* @return URL
*/
public String getUrl() {
return url;
}
/**
*
*
* @param url URL
*/
public void setUrl(String url) {
this.url = url;
}
/**
*
* 使
* @return HTML
*/
public String getContent() {
return content;
}
/**
*
* 使
* @param content HTML
*/
public void setContent(String content) {
this.content = content;
}
}
}

@ -5,39 +5,68 @@ import android.content.Context;
import android.content.SharedPreferences;
/**
* 使SP
* ACache
* 使 Android SharedPreferences SP
*
*/
public class ACache {
/**
* SharedPreferences SharedPreferences Android
* 便
*
*/
private SharedPreferences preference;
private ACache(Context ctx){
preference = ctx.getSharedPreferences("ACache",0);
/**
* ACache Context Context SharedPreferences
* SharedPreferences "ACache" 0 MODE_PRIVATE访 SharedPreferences
*
* @param ctx Android Context SharedPreferences
*/
private ACache(Context ctx) {
preference = ctx.getSharedPreferences("ACache", 0);
}
/**
* ACache Context ACache
* 使便使
* @param ctx Android Context ACache 便
* @return ACache
*/
public static ACache get(Context ctx) {
return new ACache(ctx);
}
/**
* keyvalue SharedPreferences
* SharedPreferences Editor使
*
* @param key 便
* @param value HTML
*/
public void put(String key, String value) {
try{
try {
SharedPreferences.Editor editor = preference.edit();
editor.putString(key, value);
editor.commit();
}catch (Exception e){
} catch (Exception e) {
}
}
/**
* String
*
* @param key
* @return String
* key SharedPreferences
* null
*
* @param key 使便
* @return null
*/
public String getAsString(String key) {
try{
return preference.getString(key,null);
}catch (Exception e){
try {
return preference.getString(key, null);
} catch (Exception e) {
return null;
}
}
}
}

@ -19,83 +19,122 @@ import android.widget.Checkable;
import com.monke.monkeybook.R;
import com.monke.monkeybook.utils.DensityUtil;
// 自定义的CheckBox视图类实现了平滑的勾选动画效果并且实现了Checkable接口
public class SmoothCheckBox extends View implements Checkable {
// 用于保存和恢复视图状态的键
private static final String KEY_INSTANCE_STATE = "InstanceState";
// 勾选标记的颜色,默认为白色
private static final int COLOR_TICK = Color.WHITE;
// 未选中时内部填充颜色,默认为白色
private static final int COLOR_UNCHECKED = Color.WHITE;
// 选中时的颜色,这里解析为特定的十六进制颜色值
private static final int COLOR_CHECKED = Color.parseColor("#FB4846");
// 未选中时边框颜色,解析为特定十六进制颜色值
private static final int COLOR_FLOOR_UNCHECKED = Color.parseColor("#DFDFDF");
// 默认的绘制尺寸单位dp用于在未指定具体大小时的参考尺寸
private static final int DEF_DRAW_SIZE = 25;
// 默认的动画持续时间(单位:毫秒)
private static final int DEF_ANIM_DURATION = 300;
// 用于绘制内部填充、勾选标记、边框等的画笔对象
private Paint mPaint, mTickPaint, mFloorPaint;
// 构成勾选标记的点坐标数组
private Point[] mTickPoints;
// 视图的中心点坐标
private Point mCenterPoint;
// 用于绘制勾选标记的路径对象
private Path mTickPath;
// 记录勾选标记左边线已绘制的距离、右边线已绘制的距离、总的已绘制距离
private float mLeftLineDistance, mRightLineDistance, mDrewDistance;
// 用于控制内部填充缩放的比例值、边框缩放比例值
private float mScaleVal = 1.0f, mFloorScale = 1.0f;
// 视图的宽度、动画持续时间、画笔的描边宽度
private int mWidth, mAnimDuration, mStrokeWidth;
// 选中时颜色、未选中时颜色、边框颜色、未选中时边框原本颜色(初始与边框颜色一致,可能后续变化)
private int mCheckedColor, mUnCheckedColor, mFloorColor, mFloorUnCheckedColor;
// 记录当前是否被选中
private boolean mChecked;
// 标记是否正在绘制勾选标记
private boolean mTickDrawing;
// 用于监听选中状态变化的监听器对象
private OnCheckedChangeListener mListener;
// 构造函数调用含AttributeSet参数的构造函数并传入默认的属性集为null
public SmoothCheckBox(Context context) {
this(context, null);
}
// 构造函数调用含AttributeSet和默认样式属性参数的构造函数并传入默认样式属性为0
public SmoothCheckBox(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
// 主要的构造函数,用于初始化视图,获取自定义属性并进行一些初始化操作
public SmoothCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
// 针对Android Lollipop及以上版本的构造函数同样调用init方法进行初始化
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public SmoothCheckBox(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs);
}
// 初始化方法,获取自定义属性,创建画笔对象,设置点击事件等
private void init(AttributeSet attrs) {
// 获取自定义属性数组
TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.SmoothCheckBox);
// 获取勾选标记颜色,若未设置则使用默认颜色
int tickColor = ta.getColor(R.styleable.SmoothCheckBox_color_tick, COLOR_TICK);
// 获取动画持续时间,若未设置则使用默认时间
mAnimDuration = ta.getInt(R.styleable.SmoothCheckBox_duration, DEF_ANIM_DURATION);
// 获取未选中时边框颜色,若未设置则使用默认颜色
mFloorColor = ta.getColor(R.styleable.SmoothCheckBox_color_unchecked_stroke, COLOR_FLOOR_UNCHECKED);
// 获取选中时颜色,若未设置则使用默认颜色
mCheckedColor = ta.getColor(R.styleable.SmoothCheckBox_color_checked, COLOR_CHECKED);
// 获取未选中时内部填充颜色,若未设置则使用默认颜色
mUnCheckedColor = ta.getColor(R.styleable.SmoothCheckBox_color_unchecked, COLOR_UNCHECKED);
// 获取画笔描边宽度若未设置则转换dp值为px值这里为0时的处理
mStrokeWidth = ta.getDimensionPixelSize(R.styleable.SmoothCheckBox_stroke_width, DensityUtil.dp2px(getContext(), 0));
// 回收属性数组资源
ta.recycle();
// 初始化未选中时边框原本颜色,初始与边框颜色一致
mFloorUnCheckedColor = mFloorColor;
// 创建勾选标记画笔,设置抗锯齿、描边样式、端点样式以及颜色
mTickPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTickPaint.setStyle(Paint.Style.STROKE);
mTickPaint.setStrokeCap(Paint.Cap.ROUND);
mTickPaint.setColor(tickColor);
// 创建边框画笔,设置抗锯齿、填充样式以及颜色
mFloorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mFloorPaint.setStyle(Paint.Style.FILL);
mFloorPaint.setColor(mFloorColor);
// 创建内部填充画笔,设置抗锯齿、填充样式以及颜色
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(mCheckedColor);
// 创建绘制勾选标记的路径对象
mTickPath = new Path();
// 创建中心点坐标对象
mCenterPoint = new Point();
// 创建构成勾选标记的点坐标数组并初始化三个点对象
mTickPoints = new Point[3];
mTickPoints[0] = new Point();
mTickPoints[1] = new Point();
mTickPoints[2] = new Point();
// 设置点击事件,点击时切换选中状态,重置相关绘制参数,并根据选中与否启动相应动画
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@ -111,6 +150,7 @@ public class SmoothCheckBox extends View implements Checkable {
});
}
// 保存视图状态将当前选中状态和父类的状态一起保存到Bundle中
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
@ -119,6 +159,7 @@ public class SmoothCheckBox extends View implements Checkable {
return bundle;
}
// 恢复视图状态从Bundle中获取选中状态并设置同时恢复父类的状态
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
@ -131,31 +172,30 @@ public class SmoothCheckBox extends View implements Checkable {
super.onRestoreInstanceState(state);
}
// 获取当前是否被选中的状态
@Override
public boolean isChecked() {
return mChecked;
}
// 切换选中状态
@Override
public void toggle() {
this.setChecked(!isChecked());
}
// 设置选中状态,更新选中标记,重绘视图,若有监听器则触发状态变化回调
@Override
public void setChecked(boolean checked) {
mChecked = checked;
reset();
invalidate();
if (mListener != null) {
if (mListener!= null) {
mListener.onCheckedChanged(SmoothCheckBox.this, mChecked);
}
}
/**
* checked with animation
* @param checked checked
* @param animate change with animation
*/
// 设置选中状态,可选择是否带动画效果,根据参数决定是否启动相应动画以及触发监听器回调
public void setChecked(boolean checked, boolean animate) {
if (animate) {
mTickDrawing = false;
@ -166,7 +206,7 @@ public class SmoothCheckBox extends View implements Checkable {
} else {
startUnCheckedAnimation();
}
if (mListener != null) {
if (mListener!= null) {
mListener.onCheckedChanged(SmoothCheckBox.this, mChecked);
}
@ -175,14 +215,16 @@ public class SmoothCheckBox extends View implements Checkable {
}
}
// 重置一些绘制相关的参数,根据选中状态设置初始值
private void reset() {
mTickDrawing = true;
mFloorScale = 1.0f;
mScaleVal = isChecked() ? 0f : 1.0f;
mFloorColor = isChecked() ? mCheckedColor : mFloorUnCheckedColor;
mDrewDistance = isChecked() ? (mLeftLineDistance + mRightLineDistance) : 0;
mScaleVal = isChecked()? 0f : 1.0f;
mFloorColor = isChecked()? mCheckedColor : mFloorUnCheckedColor;
mDrewDistance = isChecked()? (mLeftLineDistance + mRightLineDistance) : 0;
}
// 根据测量规格计算视图的尺寸,参考默认尺寸和测量规格中的尺寸限制
private int measureSize(int measureSpec) {
int defSize = DensityUtil.dp2px(getContext(), DEF_DRAW_SIZE);
int specSize = MeasureSpec.getSize(measureSpec);
@ -201,18 +243,20 @@ public class SmoothCheckBox extends View implements Checkable {
return result;
}
// 测量视图尺寸调用measureSize方法确定宽高尺寸
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(measureSize(widthMeasureSpec), measureSize(heightMeasureSpec));
}
// 布局视图,确定视图的宽度、画笔描边宽度(进行一些边界处理),计算中心点坐标以及构成勾选标记的点坐标等
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
mWidth = getMeasuredWidth();
mStrokeWidth = (mStrokeWidth == 0 ? getMeasuredWidth() / 10 : mStrokeWidth);
mStrokeWidth = mStrokeWidth > getMeasuredWidth() / 5 ? getMeasuredWidth() / 5 : mStrokeWidth;
mStrokeWidth = (mStrokeWidth < 3) ? 3 : mStrokeWidth;
mStrokeWidth = (mStrokeWidth == 0? getMeasuredWidth() / 10 : mStrokeWidth);
mStrokeWidth = mStrokeWidth > getMeasuredWidth() / 5? getMeasuredWidth() / 5 : mStrokeWidth;
mStrokeWidth = (mStrokeWidth < 3)? 3 : mStrokeWidth;
mCenterPoint.x = mWidth / 2;
mCenterPoint.y = getMeasuredHeight() / 2;
@ -230,6 +274,7 @@ public class SmoothCheckBox extends View implements Checkable {
mTickPaint.setStrokeWidth(mStrokeWidth);
}
// 绘制视图,依次调用绘制边框、绘制内部填充、绘制勾选标记的方法
@Override
protected void onDraw(Canvas canvas) {
drawBorder(canvas);
@ -237,29 +282,34 @@ public class SmoothCheckBox extends View implements Checkable {
drawTick(canvas);
}
// 绘制内部填充,根据选中状态设置颜色,并根据缩放比例绘制圆形填充
private void drawCenter(Canvas canvas) {
mPaint.setColor(mUnCheckedColor);
float radius = (mCenterPoint.x - mStrokeWidth) * mScaleVal;
canvas.drawCircle(mCenterPoint.x, mCenterPoint.y, radius, mPaint);
}
private void drawBorder(Canvas canvas) {
// 绘制边框,设置边框颜色,根据边框缩放比例绘制圆形边框
private void drawBorder(canvas) {
mFloorPaint.setColor(mFloorColor);
int radius = mCenterPoint.x;
canvas.drawCircle(mCenterPoint.x, mCenterPoint.y, radius * mFloorScale, mFloorPaint);
}
// 根据是否正在绘制以及是否选中来决定是否绘制勾选标记
private void drawTick(Canvas canvas) {
if (mTickDrawing && isChecked()) {
drawTickPath(canvas);
}
}
private void drawTickPath(Canvas canvas) {
// 绘制勾选标记的具体方法,根据已绘制距离逐步绘制勾选标记的线段,处理左边线、右边线的绘制逻辑,
// 并且根据是否绘制完成决定是否继续触发重绘来完成动画效果
private void drawTickPath(canvas) {
mTickPath.reset();
// draw left of the tick
// 绘制左边的勾选标记线段
if (mDrewDistance < mLeftLineDistance) {
float step = (mWidth / 20.0f) < 3 ? 3 : (mWidth / 20.0f);
float step = (mWidth / 20.0f) < 3? 3 : (mWidth / 20.0f);
mDrewDistance += step;
float stopX = mTickPoints[0].x + (mTickPoints[1].x - mTickPoints[0].x) * mDrewDistance / mLeftLineDistance;
float stopY = mTickPoints[0].y + (mTickPoints[1].y - mTickPoints[0].y) * mDrewDistance / mLeftLineDistance;
@ -277,7 +327,7 @@ public class SmoothCheckBox extends View implements Checkable {
mTickPath.lineTo(mTickPoints[1].x, mTickPoints[1].y);
canvas.drawPath(mTickPath, mTickPaint);
// draw right of the tick
// 绘制右边的勾选标记线段
if (mDrewDistance < mLeftLineDistance + mRightLineDistance) {
float stopX = mTickPoints[1].x + (mTickPoints[2].x - mTickPoints[1].x) * (mDrewDistance - mLeftLineDistance) / mRightLineDistance;
float stopY = mTickPoints[1].y - (mTickPoints[1].y - mTickPoints[2].y) * (mDrewDistance - mLeftLineDistance) / mRightLineDistance;
@ -287,7 +337,7 @@ public class SmoothCheckBox extends View implements Checkable {
mTickPath.lineTo(stopX, stopY);
canvas.drawPath(mTickPath, mTickPaint);
float step = (mWidth / 20) < 3 ? 3 : (mWidth / 20);
float step = (mWidth / 20) < 3? 3 : (mWidth / 20);
mDrewDistance += step;
} else {
mTickPath.reset();
@ -297,7 +347,7 @@ public class SmoothCheckBox extends View implements Checkable {
}
}
// invalidate
// 若还未完成勾选标记绘制,则延迟触发重绘来继续动画效果
if (mDrewDistance < mLeftLineDistance + mRightLineDistance) {
postDelayed(new Runnable() {
@Override
@ -306,97 +356,4 @@ public class SmoothCheckBox extends View implements Checkable {
}
}, 10);
}
}
private void startCheckedAnimation() {
ValueAnimator animator = ValueAnimator.ofFloat(1.0f, 0f);
animator.setDuration(mAnimDuration / 3 * 2);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mScaleVal = (float) animation.getAnimatedValue();
mFloorColor = getGradientColor(mUnCheckedColor, mCheckedColor, 1 - mScaleVal);
postInvalidate();
}
});
animator.start();
ValueAnimator floorAnimator = ValueAnimator.ofFloat(1.0f, 0.8f, 1.0f);
floorAnimator.setDuration(mAnimDuration);
floorAnimator.setInterpolator(new LinearInterpolator());
floorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mFloorScale = (float) animation.getAnimatedValue();
postInvalidate();
}
});
floorAnimator.start();
drawTickDelayed();
}
private void startUnCheckedAnimation() {
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1.0f);
animator.setDuration(mAnimDuration);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mScaleVal = (float) animation.getAnimatedValue();
mFloorColor = getGradientColor(mCheckedColor, mFloorUnCheckedColor, mScaleVal);
postInvalidate();
}
});
animator.start();
ValueAnimator floorAnimator = ValueAnimator.ofFloat(1.0f, 0.8f, 1.0f);
floorAnimator.setDuration(mAnimDuration);
floorAnimator.setInterpolator(new LinearInterpolator());
floorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mFloorScale = (float) animation.getAnimatedValue();
postInvalidate();
}
});
floorAnimator.start();
}
private void drawTickDelayed() {
postDelayed(new Runnable() {
@Override
public void run() {
mTickDrawing = true;
postInvalidate();
}
}, mAnimDuration);
}
private static int getGradientColor(int startColor, int endColor, float percent) {
int startA = Color.alpha(startColor);
int startR = Color.red(startColor);
int startG = Color.green(startColor);
int startB = Color.blue(startColor);
int endA = Color.alpha(endColor);
int endR = Color.red(endColor);
int endG = Color.green(endColor);
int endB = Color.blue(endColor);
int currentA = (int) (startA * (1 - percent) + endA * percent);
int currentR = (int) (startR * (1 - percent) + endR * percent);
int currentG = (int) (startG * (1 - percent) + endG * percent);
int currentB = (int) (startB * (1 - percent) + endB * percent);
return Color.argb(currentA, currentR, currentG, currentB);
}
public void setOnCheckedChangeListener(OnCheckedChangeListener l) {
this.mListener = l;
}
public interface OnCheckedChangeListener {
void onCheckedChanged(SmoothCheckBox checkBox, boolean isChecked);
}
}
}

@ -18,62 +18,95 @@ import com.monke.monkeybook.widget.MTextView;
import java.util.List;
// 表示书籍内容展示的自定义视图类继承自FrameLayout用于展示书籍相关内容、加载状态等信息
public class BookContentView extends FrameLayout {
// 用于标记当前视图的一个唯一标识,这里初始化为当前系统时间的毫秒数,可能用于区分不同的数据请求等情况
public long qTag = System.currentTimeMillis();
// 定义常量,表示章节起始页面索引的特殊值(从头开始)
public static final int DURPAGEINDEXBEGIN = -1;
// 定义常量,表示章节结束页面索引的特殊值(从尾开始)
public static final int DURPAGEINDEXEND = -2;
// 根视图对象通过LayoutInflater加载布局文件得到
private View view;
// 用于显示背景的ImageView
private ImageView ivBg;
// 用于显示章节标题的TextView
private TextView tvTitle;
// 用于容纳书籍内容文本的LinearLayout
private LinearLayout llContent;
// 自定义的用于显示书籍内容的TextViewMTextView类型可能有特殊功能
private MTextView tvContent;
// 底部的视图,可能用于布局分割等作用
private View vBottom;
// 用于显示当前页码相关信息的TextView
private TextView tvPage;
// 用于显示加载中的提示文本的TextView
private TextView tvLoading;
// 用于显示加载出错相关信息的LinearLayout包含错误提示和重新加载按钮等
private LinearLayout llError;
// 用于显示具体错误信息的TextView
private TextView tvErrorInfo;
// 用于点击重新加载的TextView按钮
private TextView tvLoadAgain;
// 书籍章节标题
private String title;
// 书籍章节内容
private String content;
// 当前章节索引
private int durChapterIndex;
// 总章节数
private int chapterAll;
private int durPageIndex; //如果durPageIndex = -1 则是从头开始 -2则是从尾开始
// 当前页面索引,如果值为 -1 表示从头开始, -2 表示从尾开始
private int durPageIndex;
// 总页面数
private int pageAll;
// 用于监听数据加载的监听器接口对象,由外部传入,负责加载数据相关操作
private ContentSwitchView.LoadDataListener loadDataListener;
// 用于监听设置数据完成的监听器接口对象,由外部传入,在数据设置完成后进行相应处理
private SetDataListener setDataListener;
// 定义设置数据完成的监听器接口,外部类实现此接口来处理数据设置完成后的逻辑
public interface SetDataListener {
// 当数据设置完成时调用的方法,传入相关章节、页面等索引信息
public void setDataFinish(BookContentView bookContentView, int durChapterIndex, int chapterAll, int durPageIndex, int pageAll, int fromPageIndex);
}
// 构造函数调用含AttributeSet参数的构造函数并传入默认的属性集为null
public BookContentView(Context context) {
this(context, null);
}
// 构造函数调用含AttributeSet和默认样式属性参数的构造函数并传入默认样式属性为0
public BookContentView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
// 主要的构造函数用于初始化视图调用init方法进行初始化操作
public BookContentView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
// 针对Android Lollipop及以上版本的构造函数同样调用init方法进行初始化
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public BookContentView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
// 初始化方法,加载布局文件,找到各个子视图组件,并设置重新加载按钮的点击事件
private void init() {
// 通过LayoutInflater从当前上下文加载指定布局文件并且不立即添加到父视图this
view = LayoutInflater.from(getContext()).inflate(R.layout.adapter_content_switch_item, this, false);
// 将加载的视图添加到当前的FrameLayout中
addView(view);
// 通过findViewById方法找到布局中的各个子视图组件
ivBg = (ImageView) view.findViewById(R.id.iv_bg);
tvTitle = (TextView) view.findViewById(R.id.tv_title);
llContent = (LinearLayout) view.findViewById(R.id.ll_content);
@ -86,32 +119,36 @@ public class BookContentView extends FrameLayout {
tvErrorInfo = (TextView) view.findViewById(R.id.tv_error_info);
tvLoadAgain = (TextView) view.findViewById(R.id.tv_load_again);
// 设置重新加载按钮的点击事件点击时如果加载数据监听器不为空则调用loading方法进行数据重新加载
tvLoadAgain.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (loadDataListener != null)
if (loadDataListener!= null)
loading();
}
});
}
// 用于显示加载中的状态隐藏错误信息视图显示加载提示视图隐藏内容视图并更新qTag同时调用加载数据监听器的加载方法
public void loading() {
llError.setVisibility(GONE);
tvLoading.setVisibility(VISIBLE);
llContent.setVisibility(INVISIBLE);
qTag = System.currentTimeMillis();
//执行请求操作
if (loadDataListener != null) {
// 如果加载数据监听器不为空则调用其loaddata方法传入当前视图、qTag以及章节、页面索引等信息进行数据加载
if (loadDataListener!= null) {
loadDataListener.loaddata(this, qTag, durChapterIndex, durPageIndex);
}
}
// 用于完成数据加载后的状态设置,隐藏错误信息视图,显示内容视图,隐藏加载提示视图
public void finishLoading() {
llError.setVisibility(GONE);
llContent.setVisibility(VISIBLE);
tvLoading.setVisibility(GONE);
}
// 设置无数据时的显示内容更新当前内容设置页码文本并调用finishLoading方法完成加载状态设置
public void setNoData(String contentLines) {
this.content = contentLines;
@ -120,9 +157,11 @@ public class BookContentView extends FrameLayout {
finishLoading();
}
// 根据传入的标记、标题、内容列表等信息更新视图显示的数据,判断标记是否匹配,若匹配则调用设置数据监听器方法,
// 处理内容文本拼接、更新各个相关属性并更新视图上的标题、内容、页码等显示信息最后调用finishLoading方法
public void updateData(long tag, String title, List<String> contentLines, int durChapterIndex, int chapterAll, int durPageIndex, int durPageAll) {
if (tag == qTag) {
if (setDataListener != null) {
if (setDataListener!= null) {
setDataListener.setDataFinish(this, durChapterIndex, chapterAll, durPageIndex, durPageAll, this.durPageIndex);
}
if (contentLines == null) {
@ -148,6 +187,7 @@ public class BookContentView extends FrameLayout {
}
}
// 用于加载数据的入口方法设置标题、章节、页面索引等属性更新标题和页码文本显示然后调用loading方法开始加载数据
public void loadData(String title, int durChapterIndex, int chapterAll, int durPageIndex) {
this.title = title;
this.durChapterIndex = durChapterIndex;
@ -159,77 +199,95 @@ public class BookContentView extends FrameLayout {
loading();
}
// 获取数据加载监听器对象
public ContentSwitchView.LoadDataListener getLoadDataListener() {
return loadDataListener;
}
// 设置数据加载监听器和设置数据监听器对象
public void setLoadDataListener(ContentSwitchView.LoadDataListener loadDataListener, SetDataListener setDataListener) {
this.loadDataListener = loadDataListener;
this.setDataListener = setDataListener;
}
// 单独设置数据加载监听器对象
public void setLoadDataListener(ContentSwitchView.LoadDataListener loadDataListener) {
this.loadDataListener = loadDataListener;
}
// 用于显示加载出错的状态,显示错误信息视图,隐藏加载提示视图,隐藏内容视图
public void loadError() {
llError.setVisibility(VISIBLE);
tvLoading.setVisibility(GONE);
llContent.setVisibility(INVISIBLE);
}
// 获取总页面数
public int getPageAll() {
return pageAll;
}
// 设置总页面数
public void setPageAll(int pageAll) {
this.pageAll = pageAll;
}
// 获取当前页面索引
public int getDurPageIndex() {
return durPageIndex;
}
// 设置当前页面索引
public void setDurPageIndex(int durPageIndex) {
this.durPageIndex = durPageIndex;
}
// 获取当前章节索引
public int getDurChapterIndex() {
return durChapterIndex;
}
// 设置当前章节索引
public void setDurChapterIndex(int durChapterIndex) {
this.durChapterIndex = durChapterIndex;
}
// 获取总章节数
public int getChapterAll() {
return chapterAll;
}
// 设置总章节数
public void setChapterAll(int chapterAll) {
this.chapterAll = chapterAll;
}
// 获取设置数据监听器对象
public SetDataListener getSetDataListener() {
return setDataListener;
}
// 设置设置数据监听器对象
public void setSetDataListener(SetDataListener setDataListener) {
this.setDataListener = setDataListener;
}
// 获取用于标记的qTag值
public long getqTag() {
return qTag;
}
// 设置用于标记的qTag值
public void setqTag(long qTag) {
this.qTag = qTag;
}
// 获取用于显示书籍内容的TextView对象
public TextView getTvContent() {
return tvContent;
}
// 根据给定的高度计算可显示的文本行数通过获取文本的ascent、descent来计算文本高度结合行间距等信息进行计算
public int getLineCount(int height) {
float ascent = tvContent.getPaint().ascent();
float descent = tvContent.getPaint().descent();
@ -237,11 +295,13 @@ public class BookContentView extends FrameLayout {
return (int) ((height * 1.0f - tvContent.getLineSpacingExtra()) / (textHeight + tvContent.getLineSpacingExtra()));
}
// 设置阅读书籍相关的控制属性,调用设置文本样式和背景的方法
public void setReadBookControl(ReadBookControl readBookControl) {
setTextKind(readBookControl);
setBg(readBookControl);
}
// 设置背景相关属性包括背景图片、各个文本组件的颜色等根据传入的ReadBookControl对象中的配置进行设置
public void setBg(ReadBookControl readBookControl) {
ivBg.setImageResource(readBookControl.getTextBackground());
tvTitle.setTextColor(readBookControl.getTextColor());
@ -252,8 +312,9 @@ public class BookContentView extends FrameLayout {
tvErrorInfo.setTextColor(readBookControl.getTextColor());
}
// 设置文本相关样式如文本大小、行间距等根据传入的ReadBookControl对象中的配置进行设置
public void setTextKind(ReadBookControl readBookControl) {
tvContent.setTextSize(readBookControl.getTextSize());
tvContent.setLineSpacing(readBookControl.getTextExtra(), 1);
}
}
}

@ -20,73 +20,97 @@ import com.monke.monkeybook.utils.DensityUtil;
import java.util.ArrayList;
import java.util.List;
// ContentSwitchView类继承自FrameLayout并实现了BookContentView.SetDataListener接口
public class ContentSwitchView extends FrameLayout implements BookContentView.SetDataListener {
// 获取屏幕宽度
private final int screenWidth = DensityUtil.getWindowWidth(getContext());
// 动画持续时间
private final long animDuration = 300;
// 表示没有上一页和下一页的状态常量
public final static int NONE = -1;
// 表示既有上一页又有下一页的状态常量
public final static int PREANDNEXT = 0;
// 表示只有上一页的状态常量
public final static int ONLYPRE = 1;
// 表示只有下一页的状态常量
public final static int ONLYNEXT = 2;
private int state = NONE; //0是有上一页 也有下一页 ; 2是只有下一页 1是只有上一页;-1是没有上一页 也没有下一页;
// 当前视图的状态初始化为NONE
private int state = NONE;
// 横向滚动的偏移量
private int scrollX;
// 表示是否正在移动的标志
private Boolean isMoving = false;
// 当前显示的页面视图
private BookContentView durPageView;
// 存储所有页面视图的列表
private List<BookContentView> viewContents;
// 定义一个接口,用于在书籍阅读初始化成功时回调
public interface OnBookReadInitListener {
public void success();
}
// 书籍阅读初始化监听器
private OnBookReadInitListener bookReadInitListener;
// 构造函数,用于在代码中创建视图时初始化
public ContentSwitchView(Context context) {
super(context);
init();
}
// 构造函数用于在XML布局中创建视图时初始化并接收属性集
public ContentSwitchView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
// 构造函数用于在XML布局中创建视图时初始化并接收属性集和默认样式属性
public ContentSwitchView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
// 构造函数用于在XML布局中创建视图时初始化并接收属性集、默认样式属性和特定版本的样式资源
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ContentSwitchView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
// 读取书籍控制实例
private ReadBookControl readBookControl;
// 初始化方法
private void init() {
readBookControl = ReadBookControl.getInstance();
// 将30dp转换为像素值作为横向滚动的偏移量
scrollX = DensityUtil.dp2px(getContext(), 30f);
// 创建当前页面视图
durPageView = new BookContentView(getContext());
// 设置当前页面视图的书籍控制实例
durPageView.setReadBookControl(readBookControl);
viewContents = new ArrayList<>();
viewContents.add(durPageView);
// 将当前页面视图添加到布局中
addView(durPageView);
}
// 用于开始书籍阅读的初始化,并设置初始化监听器
public void bookReadInit(OnBookReadInitListener bookReadInitListener) {
this.bookReadInitListener = bookReadInitListener;
durPageView.getTvContent().getViewTreeObserver().addOnGlobalLayoutListener(layoutInitListener);
}
// 开始加载数据
public void startLoading() {
int height = durPageView.getTvContent().getHeight();
if (height > 0) {
if (loadDataListener != null && durHeight != height) {
if (loadDataListener!= null && durHeight!= height) {
durHeight = height;
loadDataListener.initData(durPageView.getLineCount(height));
}
@ -94,18 +118,22 @@ public class ContentSwitchView extends FrameLayout implements BookContentView.Se
durPageView.getTvContent().getViewTreeObserver().addOnGlobalLayoutListener(layoutListener);
}
// 测量视图的大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
// 触摸事件的起始X坐标
private float startX = -1;
// 处理触摸事件
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
if (!isMoving) {
int durWidth = screenWidth > 1400 ? 10 : 0; //当分辨率过大时,添加横向滑动冗余值
// 当屏幕宽度大于1400时设置横向滑动冗余值为10否则为0
int durWidth = screenWidth > 1400? 10 : 0;
switch (action) {
case MotionEvent.ACTION_DOWN:
startX = event.getX();
@ -115,7 +143,7 @@ public class ContentSwitchView extends FrameLayout implements BookContentView.Se
if (startX == -1)
startX = event.getX();
//处理分辨率过大,移动冗余值,当横向滑动值超过冗余值则开始滑动
// 处理分辨率过大时的横向滑动冗余值
int durX = (int) (event.getX() - startX);
if(durX>durWidth){
durX = durX - durWidth;
@ -138,62 +166,62 @@ public class ContentSwitchView extends FrameLayout implements BookContentView.Se
tempX = 0;
else if (tempX < -getWidth())
tempX = -getWidth();
int tempIndex = (state == PREANDNEXT ? 1 : 0);
int tempIndex = (state == PREANDNEXT? 1 : 0);
viewContents.get(tempIndex).layout(tempX, viewContents.get(tempIndex).getTop(), tempX + getWidth(), viewContents.get(tempIndex).getBottom());
}
}
break;
case MotionEvent.ACTION_CANCEL: //小米8长按传送门会引导手势进入action_cancel
case MotionEvent.ACTION_CANCEL: // 小米8长按传送门会引导手势进入action_cancel
case MotionEvent.ACTION_UP:
if (startX == -1)
startX = event.getX();
if (event.getX() - startX > durWidth) {
if (state == PREANDNEXT || state == ONLYPRE) {
//注意冗余值
// 注意冗余值,判断是否向前翻页成功
if (event.getX() - startX + durWidth> scrollX) {
//向前翻页成功
// 向前翻页成功
initMoveSuccessAnim(viewContents.get(0), 0);
} else {
initMoveFailAnim(viewContents.get(0), -getWidth());
}
} else {
//没有上一页
// 没有上一页
noPre();
}
} else if (event.getX() - startX < -durWidth) {
if (state == PREANDNEXT || state == ONLYNEXT) {
int tempIndex = (state == PREANDNEXT ? 1 : 0);
//注意冗余值
int tempIndex = (state == PREANDNEXT? 1 : 0);
// 注意冗余值,判断是否向后翻页成功
if (startX - event.getX() - durWidth > scrollX) {
//向后翻页成功
// 向后翻页成功
initMoveSuccessAnim(viewContents.get(tempIndex), -getWidth());
} else {
initMoveFailAnim(viewContents.get(tempIndex), 0);
}
} else {
//没有下一页
// 没有下一页
noNext();
}
} else {
//点击事件
// 点击事件
if (readBookControl.getCanClickTurn() && event.getX() <= getWidth() / 3) {
//点击向前翻页
// 点击向前翻页
if (state == PREANDNEXT || state == ONLYPRE) {
initMoveSuccessAnim(viewContents.get(0), 0);
} else {
noPre();
}
} else if (readBookControl.getCanClickTurn() && event.getX() >= getWidth() / 3 * 2) {
//点击向后翻页
// 点击向后翻页
if (state == PREANDNEXT || state == ONLYNEXT) {
int tempIndex = (state == PREANDNEXT ? 1 : 0);
int tempIndex = (state == PREANDNEXT? 1 : 0);
initMoveSuccessAnim(viewContents.get(tempIndex), -getWidth());
} else {
noNext();
}
} else {
//点击中间部位
if (loadDataListener != null)
// 点击中间部位
if (loadDataListener!= null)
loadDataListener.showMenu();
}
}
@ -206,6 +234,7 @@ public class ContentSwitchView extends FrameLayout implements BookContentView.Se
return super.onTouchEvent(event);
}
// 布局子视图
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (viewContents.size() > 0) {
@ -227,14 +256,16 @@ public class ContentSwitchView extends FrameLayout implements BookContentView.Se
}
}
// 初始化翻页成功的动画
private void initMoveSuccessAnim(final View view, final int orderX) {
if (null != view) {
if (null!= view) {
// 根据视图的移动距离和屏幕宽度计算动画持续时间
long temp = Math.abs(view.getLeft() - orderX) / (getWidth() / animDuration);
ValueAnimator tempAnim = ValueAnimator.ofInt(view.getLeft(), orderX).setDuration(temp);
tempAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (null != view) {
if (null!= view) {
int value = (int) animation.getAnimatedValue();
view.layout(value, view.getTop(), value + getWidth(), view.getBottom());
}
@ -250,9 +281,10 @@ public class ContentSwitchView extends FrameLayout implements BookContentView.Se
public void onAnimationEnd(Animator animation) {
isMoving = false;
if (orderX == 0) {
//翻向前一页
// 翻向前一页
durPageView = viewContents.get(0);
if (state == PREANDNEXT) {
// 移除最后一页视图
ContentSwitchView.this.removeView(viewContents.get(viewContents.size() - 1));
viewContents.remove(viewContents.size() - 1);
}
@ -264,11 +296,12 @@ public class ContentSwitchView extends FrameLayout implements BookContentView.Se
else state = PREANDNEXT;
}
} else {
//翻向后一夜
// 翻向后一页
if (state == ONLYNEXT) {
durPageView = viewContents.get(1);
} else {
durPageView = viewContents.get(2);
// 移除第一页视图
ContentSwitchView.this.removeView(viewContents.get(0));
viewContents.remove(0);
}
@ -280,7 +313,7 @@ public class ContentSwitchView extends FrameLayout implements BookContentView.Se
else state = PREANDNEXT;
}
}
if (loadDataListener != null)
if (loadDataListener!= null)
loadDataListener.updateProgress(durPageView.getDurChapterIndex(), durPageView.getDurPageIndex());
}
@ -298,14 +331,15 @@ public class ContentSwitchView extends FrameLayout implements BookContentView.Se
}
}
// 初始化翻页失败的动画
private void initMoveFailAnim(final View view, int orderX) {
if (null != view) {
if (null!= view) {
long temp = Math.abs(view.getLeft() - orderX) / (getWidth() / animDuration);
ValueAnimator tempAnim = ValueAnimator.ofInt(view.getLeft(), orderX).setDuration(temp);
tempAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (null != view) {
if (null!= view) {
int value = (int) animation.getAnimatedValue();
view.layout(value, view.getTop(), value + getWidth(), view.getBottom());
}
@ -315,27 +349,29 @@ public class ContentSwitchView extends FrameLayout implements BookContentView.Se
}
}
// 设置初始数据
public void setInitData(int durChapterIndex, int chapterAll, int durPageIndex) {
updateOtherPage(durChapterIndex, chapterAll, durPageIndex, -1);
durPageView.setLoadDataListener(loadDataListener, this);
durPageView.loadData(null != loadDataListener ? loadDataListener.getChapterTitle(durChapterIndex) : "", durChapterIndex, chapterAll, durPageIndex);
durPageView.loadData(null!= loadDataListener? loadDataListener.getChapterTitle(durChapterIndex) : "", durChapterIndex, chapterAll, durPageIndex);
if (loadDataListener != null)
if (loadDataListener!= null)
loadDataListener.updateProgress(durPageView.getDurChapterIndex(), durPageView.getDurPageIndex());
}
// 根据当前页面的位置和总页数等信息更新其他页面的状态和视图
private void updateOtherPage(int durChapterIndex, int chapterAll, int durPageIndex, int pageAll) {
if (chapterAll > 1 || pageAll > 1) {
if ((durChapterIndex == 0 && pageAll == -1) || (durChapterIndex == 0 && durPageIndex == 0 && pageAll != -1)) {
//ONLYNEXT
if ((durChapterIndex == 0 && pageAll == -1) || (durChapterIndex == 0 && durPageIndex == 0 && pageAll!= -1)) {
// ONLYNEXT,只有下一页的情况
addNextPage(durChapterIndex, chapterAll, durPageIndex, pageAll);
if (state == ONLYPRE || state == PREANDNEXT) {
this.removeView(viewContents.get(0));
viewContents.remove(0);
}
state = ONLYNEXT;
} else if ((durChapterIndex == chapterAll - 1 && pageAll == -1) || (durChapterIndex == chapterAll - 1 && durPageIndex == pageAll - 1 && pageAll != -1)) {
//ONLYPRE
} else if ((durChapterIndex == chapterAll - 1 && pageAll == -1) || (durChapterIndex == chapterAll - 1 && durPageIndex == pageAll - 1 && pageAll!= -1)) {
// ONLYPRE,只有上一页的情况
addPrePage(durChapterIndex, chapterAll, durPageIndex, pageAll);
if (state == ONLYNEXT || state == PREANDNEXT) {
this.removeView(viewContents.get(2));
@ -343,13 +379,13 @@ public class ContentSwitchView extends FrameLayout implements BookContentView.Se
}
state = ONLYPRE;
} else {
//PREANDNEXT
// PREANDNEXT,既有上一页又有下一页的情况
addNextPage(durChapterIndex, chapterAll, durPageIndex, pageAll);
addPrePage(durChapterIndex, chapterAll, durPageIndex, pageAll);
state = PREANDNEXT;
}
} else {
//NONE
// NONE,没有上一页和下一页的情况
if (state == ONLYPRE) {
this.removeView(viewContents.get(0));
viewContents.remove(0);
@ -358,180 +394,4 @@ public class ContentSwitchView extends FrameLayout implements BookContentView.Se
viewContents.remove(1);
} else if (state == PREANDNEXT) {
this.removeView(viewContents.get(0));
this.removeView(viewContents.get(2));
viewContents.remove(2);
viewContents.remove(0);
}
state = NONE;
}
}
private void addNextPage(int durChapterIndex, int chapterAll, int durPageIndex, int pageAll) {
if (state == ONLYNEXT || state == PREANDNEXT) {
int temp = (state == ONLYNEXT ? 1 : 2);
if (pageAll > 0 && durPageIndex >= 0 && durPageIndex < pageAll - 1)
viewContents.get(temp).loadData(null != loadDataListener ? loadDataListener.getChapterTitle(durChapterIndex) : "", durChapterIndex, chapterAll, durPageIndex + 1);
else
viewContents.get(temp).loadData(null != loadDataListener ? loadDataListener.getChapterTitle(durChapterIndex + 1) : "", durChapterIndex + 1, chapterAll, BookContentView.DURPAGEINDEXBEGIN);
} else if (state == ONLYPRE || state == NONE) {
BookContentView next = new BookContentView(getContext());
next.setReadBookControl(readBookControl);
next.setLoadDataListener(loadDataListener, this);
if (pageAll > 0 && durPageIndex >= 0 && durPageIndex < pageAll - 1)
next.loadData(null != loadDataListener ? loadDataListener.getChapterTitle(durChapterIndex) : "", durChapterIndex, chapterAll, durPageIndex + 1);
else
next.loadData(null != loadDataListener ? loadDataListener.getChapterTitle(durChapterIndex + 1) : "", durChapterIndex + 1, chapterAll, BookContentView.DURPAGEINDEXBEGIN);
viewContents.add(next);
this.addView(next, 0);
}
}
private void addPrePage(int durChapterIndex, int chapterAll, int durPageIndex, int pageAll) {
if (state == ONLYNEXT || state == NONE) {
BookContentView pre = new BookContentView(getContext());
pre.setReadBookControl(readBookControl);
pre.setLoadDataListener(loadDataListener, this);
if (pageAll > 0 && durPageIndex >= 0 && durPageIndex > 0)
pre.loadData(null != loadDataListener ? loadDataListener.getChapterTitle(durChapterIndex) : "", durChapterIndex, chapterAll, durPageIndex - 1);
else
pre.loadData(null != loadDataListener ? loadDataListener.getChapterTitle(durChapterIndex - 1) : "", durChapterIndex - 1, chapterAll, BookContentView.DURPAGEINDEXEND);
viewContents.add(0, pre);
this.addView(pre);
} else if (state == ONLYPRE || state == PREANDNEXT) {
if (pageAll > 0 && durPageIndex >= 0 && durPageIndex > 0)
viewContents.get(0).loadData(null != loadDataListener ? loadDataListener.getChapterTitle(durChapterIndex) : "", durChapterIndex, chapterAll, durPageIndex - 1);
else
viewContents.get(0).loadData(null != loadDataListener ? loadDataListener.getChapterTitle(durChapterIndex - 1) : "", durChapterIndex - 1, chapterAll, BookContentView.DURPAGEINDEXEND);
}
}
@Override
public void setDataFinish(BookContentView bookContentView, int durChapterIndex, int chapterAll, int durPageIndex, int pageAll, int fromPageIndex) {
if (null != getDurContentView() && bookContentView == getDurContentView() && chapterAll > 0 && pageAll > 0) {
updateOtherPage(durChapterIndex, chapterAll, durPageIndex, pageAll);
}
}
public interface LoadDataListener {
public void loaddata(BookContentView bookContentView, long tag, int chapterIndex, int pageIndex);
public void updateProgress(int chapterIndex, int pageIndex);
public String getChapterTitle(int chapterIndex);
public void initData(int lineCount);
public void showMenu();
}
private LoadDataListener loadDataListener;
public LoadDataListener getLoadDataListener() {
return loadDataListener;
}
public void setLoadDataListener(LoadDataListener loadDataListener) {
this.loadDataListener = loadDataListener;
}
public BookContentView getDurContentView() {
return durPageView;
}
private void noPre() {
Toast.makeText(getContext(), "没有上一页", Toast.LENGTH_SHORT).show();
}
private void noNext() {
Toast.makeText(getContext(), "没有下一页", Toast.LENGTH_SHORT).show();
}
private ViewTreeObserver.OnGlobalLayoutListener layoutInitListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (bookReadInitListener != null) {
bookReadInitListener.success();
}
durPageView.getTvContent().getViewTreeObserver().removeOnGlobalLayoutListener(layoutInitListener);
}
};
private ViewTreeObserver.OnGlobalLayoutListener layoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int height = durPageView.getTvContent().getHeight();
if (height > 0) {
if (loadDataListener != null && durHeight != height) {
durHeight = height;
loadDataListener.initData(durPageView.getLineCount(height));
}
}
}
};
private int durHeight = 0;
public Paint getTextPaint() {
return durPageView.getTvContent().getPaint();
}
public int getContentWidth() {
return durPageView.getTvContent().getWidth();
}
public void changeBg() {
for (BookContentView item : viewContents) {
item.setBg(readBookControl);
}
}
public void changeTextSize() {
for (BookContentView item : viewContents) {
item.setTextKind(readBookControl);
}
loadDataListener.initData(durPageView.getLineCount(durHeight));
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (readBookControl.getCanKeyTurn() && keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
if (state == PREANDNEXT || state == ONLYNEXT) {
int tempIndex = (state == PREANDNEXT ? 1 : 0);
initMoveSuccessAnim(viewContents.get(tempIndex), -getWidth());
} else {
noNext();
}
return true;
} else if (readBookControl.getCanKeyTurn() && keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
if (state == PREANDNEXT || state == ONLYPRE) {
initMoveSuccessAnim(viewContents.get(0), 0);
} else {
noPre();
}
return true;
}
return false;
}
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (readBookControl.getCanKeyTurn() && keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
return true;
} else if (readBookControl.getCanKeyTurn() && keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
return true;
}
return false;
}
public OnBookReadInitListener getBookReadInitListener() {
return bookReadInitListener;
}
public void setBookReadInitListener(OnBookReadInitListener bookReadInitListener) {
this.bookReadInitListener = bookReadInitListener;
}
public void loadError() {
if (durPageView != null) {
durPageView.loadError();
}
}
}
this.removeView(viewContents.get(2));
Loading…
Cancel
Save