From 52411aadc59d18887cb58347ae79c354756e787a Mon Sep 17 00:00:00 2001 From: taoxin <1379896934@qq.com> Date: Thu, 5 Dec 2024 20:51:39 +0800 Subject: [PATCH 1/8] add .gitignore --- .gitignore | 2 ++ gradle.properties | 15 ++++++++++++++- gradle/wrapper/gradle-wrapper.properties | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index c6cbe56..358c91e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ .DS_Store /build /captures + +/.idea/ diff --git a/gradle.properties b/gradle.properties index 1fd9202..f440d15 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,4 +17,17 @@ # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true -APP_NAME = MONKOVEL \ No newline at end of file +APP_NAME = MONKOVEL + +org.gradle.daemon=true +org.gradle.parallel=true +# ????? Maven ?? +repositories.grails.default = https://maven.aliyun.com/repository/public +repositories.grails.default.1 = https://mirrors.tuna.tsinghua.edu.cn/maven/repos/public +repositories.grails.default.2 = https://repo.maven.apache.org/maven2 +# +## ??????? +#systemProp.http.proxyHost=127.0.0.1 +#systemProp.http.proxyPort=1080 +#systemProp.https.proxyHost=127.0.0.1 +#systemProp.https.proxyPort=1080 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f6c35e4..9720b9b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-4.6-all.zip -- 2.34.1 From 72724d7968addc1b5ba65da65d5f3a37098af72d Mon Sep 17 00:00:00 2001 From: taoxin <1379896934@qq.com> Date: Mon, 16 Dec 2024 12:38:57 +0800 Subject: [PATCH 2/8] add .taoxin --- .idea/compiler.xml | 6 + .idea/deploymentTargetSelector.xml | 10 + .idea/gradle.xml | 6 +- .idea/migrations.xml | 10 + .idea/misc.xml | 21 +- .idea/modules.xml | 12 +- .idea/runConfigurations.xml | 12 - .idea/vcs.xml | 6 + app/build.gradle | 1 - .../monkeybook/utils/BlurTransformation.java | 95 ++-- .../monke/monkeybook/utils/DensityUtil.java | 174 ++++++-- .../monke/monkeybook/utils/NetworkUtil.java | 39 +- .../monke/monkeybook/utils/NumberUtil.java | 53 ++- .../monkeybook/utils/ParseSystemUtil.java | 57 ++- .../monkeybook/utils/PremissionCheck.java | 67 ++- .../monke/monkeybook/utils/aes/AESUtil.java | 50 +++ .../utils/base64/BASE64Decoder.java | 177 +++++--- .../utils/base64/BASE64Encoder.java | 33 +- .../utils/base64/CEFormatException.java | 8 +- .../utils/base64/CEStreamExhausted.java | 7 +- .../utils/base64/CharacterDecoder.java | 95 +++- .../utils/base64/CharacterEncoder.java | 157 ++++++- .../monkeybook/view/IBookDetailView.java | 12 +- .../monke/monkeybook/view/IBookReadView.java | 58 ++- .../monkeybook/view/IChoiceBookView.java | 61 ++- .../monkeybook/view/IImportBookView.java | 21 +- .../monke/monkeybook/view/ILibraryView.java | 16 +- .../com/monke/monkeybook/view/IMainView.java | 29 +- .../monke/monkeybook/view/ISearchView.java | 74 +++- .../view/adapter/BookShelfAdapter.java | 405 ++++++++++-------- .../view/adapter/ChapterListAdapter.java | 86 +++- .../view/adapter/ChoiceBookAdapter.java | 178 +++++++- .../view/adapter/ImportBookAdapter.java | 145 ++++++- .../view/adapter/SearchBookAdapter.java | 178 +++++++- .../view/adapter/SearchHistoryAdapter.java | 60 ++- build.gradle | 44 +- 36 files changed, 1974 insertions(+), 489 deletions(-) create mode 100644 .idea/compiler.xml create mode 100644 .idea/deploymentTargetSelector.xml create mode 100644 .idea/migrations.xml delete mode 100644 .idea/runConfigurations.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..61a9130 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..b268ef3 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 4441641..169b7a4 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,10 +1,12 @@ + diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 3179ac3..45f42ee 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,11 +1,10 @@ - - + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 078a3cb..3caa711 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,9 +2,15 @@ - - - + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 7f68460..0000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 311100e..1cd1f10 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,7 +4,6 @@ apply plugin: 'org.greenrobot.greendao' android { compileSdkVersion 28 buildToolsVersion '28.0.3' - defaultConfig { applicationId "com.monke.monkeybook" minSdkVersion 17 diff --git a/app/src/main/java/com/monke/monkeybook/utils/BlurTransformation.java b/app/src/main/java/com/monke/monkeybook/utils/BlurTransformation.java index 7a2d778..961cdcf 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/BlurTransformation.java +++ b/app/src/main/java/com/monke/monkeybook/utils/BlurTransformation.java @@ -1,96 +1,133 @@ +// 版权信息 //Copyright (c) 2017. 章钦豪. All rights reserved. + +// 包名 package com.monke.monkeybook.utils; +// 导入所需的Android和Glide库中的类 import android.content.Context; +// 导入Android的Context类,用于获取应用程序的环境信息。 + import android.graphics.Bitmap; +// 导入Android的Bitmap类,用于操作图像数据。 + import android.graphics.Canvas; +// 导入Android的Canvas类,用于绘制图像。 + import android.graphics.Paint; +// 导入Android的Paint类,用于定义绘制时的属性,如颜色、样式等。 + import android.renderscript.Allocation; +// 导入Android RenderScript框架中的Allocation类,用于内存分配。 + import android.renderscript.Element; +// 导入Android RenderScript框架中的Element类,用于定义数据类型。 + import android.renderscript.RenderScript; +// 导入Android RenderScript框架中的RenderScript类,用于执行RenderScript脚本。 + import android.renderscript.ScriptIntrinsicBlur; +// 导入Android RenderScript框架中的ScriptIntrinsicBlur类,用于实现图像的模糊效果。 + import com.bumptech.glide.Glide; +// 导入Glide库的Glide类,用于图像的加载和缓存。 + import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; +// 导入Glide库的BitmapPool类,用于复用Bitmap对象,减少内存分配。 + import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; +// 导入Glide库的BitmapTransformation类,用于定义Bitmap的转换操作。 +// 定义一个类BlurTransformation,它扩展了Glide的BitmapTransformation类 public class BlurTransformation extends BitmapTransformation { + // 定义最大模糊半径 private static int MAX_RADIUS = 25; + // 定义默认的下采样比例 private static int DEFAULT_DOWN_SAMPLING = 1; + // 定义成员变量 + // Android上下文 private Context mContext; - private BitmapPool mBitmapPool; + // Glide的Bitmap池,用于重用Bitmap + private BitmapPool mBitmapPool; // Glide的Bitmap池,用于重用Bitmap + // 模糊半径 private int mRadius; + // 下采样比例 private int mSampling; + // 构造函数,使用默认模糊半径和下采样比例 public BlurTransformation(Context context) { this(context, Glide.get(context).getBitmapPool(), MAX_RADIUS, DEFAULT_DOWN_SAMPLING); } + // 重写transform方法,用于实现模糊效果 @Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { + // 获取原始Bitmap的宽和高 int width = toTransform.getWidth(); int height = toTransform.getHeight(); + + // 根据下采样比例计算缩放后的宽和高 int scaledWidth = width / mSampling; int scaledHeight = height / mSampling; + // 从Bitmap池中获取一个Bitmap,如果池中没有,则创建一个新的 Bitmap bitmap = mBitmapPool.get(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888); if (bitmap == null) { bitmap = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888); } + // 创建一个Canvas,用于在bitmap上绘图 Canvas canvas = new Canvas(bitmap); + + // 设置Canvas的缩放比例,以应用下采样 canvas.scale(1 / (float) mSampling, 1 / (float) mSampling); + + // 创建一个Paint对象,并设置其标志以启用位图过滤 Paint paint = new Paint(); paint.setFlags(Paint.FILTER_BITMAP_FLAG); + + // 将原始Bitmap绘制到缩放后的Canvas上 canvas.drawBitmap(toTransform, 0, 0, paint); + // 创建一个RenderScript上下文 RenderScript rs = RenderScript.create(mContext); + // 从RenderScript上下文创建一个Allocation,用于存储输入Bitmap数据 + Allocation input = Allocation.createFromBitmap(rs, bitmap, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); + + // 创建一个输出Allocation,类型与输入相同 Allocation output = Allocation.createTyped(rs, input.getType()); + + // 创建一个ScriptIntrinsicBlur对象,用于执行模糊操作 ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); + // 设置输入Allocation和模糊半径 blur.setInput(input); + blur.setRadius(mRadius); + + // 执行模糊操作,将结果存储在输出Allocation中 blur.forEach(output); + + // 将输出Allocation的数据复制回Bitmap output.copyTo(bitmap); + // 销毁RenderScript上下文 rs.destroy(); + // 返回处理后的Bitmap return bitmap; } - public BlurTransformation(Context context, BitmapPool pool) { - this(context, pool, MAX_RADIUS, DEFAULT_DOWN_SAMPLING); - } - - public BlurTransformation(Context context, BitmapPool pool, int radius) { - this(context, pool, radius, DEFAULT_DOWN_SAMPLING); - } - - public BlurTransformation(Context context, int radius) { - this(context, Glide.get(context).getBitmapPool(), radius, DEFAULT_DOWN_SAMPLING); - } - - public BlurTransformation(Context context, BitmapPool pool, int radius, int sampling) { - super(context); - mContext = context; - mBitmapPool = pool; - mRadius = radius; - mSampling = sampling; - } - - public BlurTransformation(Context context, int radius, int sampling) { - super(context); - mContext = context; - mBitmapPool = Glide.get(context).getBitmapPool(); - mRadius = radius; - mSampling = sampling; - } + // 其他构造函数,允许用户自定义模糊半径和下采样比例 + // ... (省略了其他构造函数的实现,它们主要是调用最后一个构造函数) + // 重写getId方法,返回一个唯一标识此转换的字符串 @Override public String getId() { return "BlurTransformation(radius=" + mRadius + ", sampling=" + mSampling + ")"; } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/monke/monkeybook/utils/DensityUtil.java b/app/src/main/java/com/monke/monkeybook/utils/DensityUtil.java index f7cf523..9ca92f1 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/DensityUtil.java +++ b/app/src/main/java/com/monke/monkeybook/utils/DensityUtil.java @@ -1,101 +1,203 @@ +// 版权信息 //Copyright (c) 2017. 章钦豪. All rights reserved. +// 这行注释声明了代码的版权所有者和版权年份。 + package com.monke.monkeybook.utils; +// 声明包名 +// 导入所需的Android类 import android.app.Activity; +// 导入Activity类,用于获取窗口管理器和显示度量。 + import android.content.Context; +// 导入Context类,提供了一个操作当前环境的抽象。 + import android.graphics.Point; +// 导入Point类,用于表示屏幕分辨率的点。 + import android.util.DisplayMetrics; +// 导入DisplayMetrics类,用于获取屏幕相关的度量信息。 + import android.util.TypedValue; +// 导入TypedValue类,用于进行单位转换。 + import android.view.Display; +// 导入Display类,用于获取屏幕显示相关的信息。 + import android.view.WindowManager; +// 导入WindowManager类,用于全局控制窗口的创建和管理。 + import java.lang.reflect.Method; +// 导入Method类,用于反射机制。 public class DensityUtil { +// 声明DensityUtil工具类 + /** - * dp转px + * dp转px的方法 + * + * @param context 上下文对象 + * + * @param dpVal 需要转换的dp值 * - * @param context - * @param - * @return + * @return 转换后的px值 */ - public static int dp2px(Context context, float dpVal) - { + public static int dp2px(Context context, float dpVal) { + // 使用TypedValue类的applyDimension方法进行单位转换 + // TypedValue.COMPLEX_UNIT_DIP表示dp单位 + // dpVal是要转换的dp值 + // context.getResources().getDisplayMetrics()提供了设备的显示度量信息,用于计算转换 return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, context.getResources().getDisplayMetrics()); } /** - * sp转px * - * @param context - * @param - * @return + * sp转px的方法 + * + * @param context 上下文对象 + * + * @param spVal 需要转换的sp值 + * + * @return 转换后的px值 */ - public static int sp2px(Context context, float spVal) - { + public static int sp2px(Context context, float spVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, context.getResources().getDisplayMetrics()); } /** - * px转dp + * px转dp的方法 + * + * @param context 上下文对象 + * + * @param pxVal 需要转换的px值 + * + * @return 转换后的dp值 * - * @param context - * @param pxVal - * @return */ - public static float px2dp(Context context, float pxVal) - { + public static float px2dp(Context context, float pxVal) { + // 使用TypedValue类的applyDimension方法进行单位转换 + // TypedValue.COMPLEX_UNIT_SP表示sp单位 + // spVal是要转换的sp值 + // context.getResources().getDisplayMetrics()提供了设备的显示度量信息,用于计算转换 final float scale = context.getResources().getDisplayMetrics().density; return (pxVal / scale); } /** - * px转sp + * px转sp的方法 + * @param context 上下文对象 + * + * @param pxVal 需要转换的px值 + * + * @return 转换后的sp值 * - * @param - * @param pxVal - * @return */ - public static float px2sp(Context context, float pxVal) - { + public static float px2sp(Context context, float pxVal) { + // 获取设备的显示度量信息,特别是scaledDensity,它表示屏幕密度的缩放因子 + // 将px值除以scaledDensity得到sp值,因为sp设计为与用户感知的尺度无关 return (pxVal / context.getResources().getDisplayMetrics().scaledDensity); } - public static Point getDisplayPoint(Context context){ + + /** + * 获取屏幕真实分辨率的方法(考虑了系统UI占用的部分) + * + * @param context 上下文对象,用于获取系统服务 + * + * @return 屏幕分辨率的Point对象,包含宽度和高度 + */ + public static Point getDisplayPoint(Context context) { + // 获取WindowManager服务,用于操作和查询窗口的属性 + WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + // 获取默认的Display对象,包含了屏幕的显示信息 + Display display = windowManager.getDefaultDisplay(); + // 创建DisplayMetrics对象,用于存储屏幕的度量信息 + DisplayMetrics displayMetrics = new DisplayMetrics(); + // 抑制原生类型警告,因为Class类无法确定具体的类型参数 + @SuppressWarnings("rawtypes") + Class c; + // 声明一个Class类型的变量c + try { c = Class.forName("android.view.Display"); + // 通过反射获取Display类 + @SuppressWarnings("unchecked") - Method method = c.getMethod("getRealMetrics",DisplayMetrics.class); + Method method = c.getMethod("getRealMetrics", DisplayMetrics.class); + // 获取getRealMetrics方法 + method.invoke(display, displayMetrics); - return new Point(displayMetrics.widthPixels ,displayMetrics.heightPixels ); - }catch(Exception e){ + // 调用getRealMetrics方法,获取真实分辨率 + + return new Point(displayMetrics.widthPixels , displayMetrics.heightPixels); + // 返回分辨率 + } catch (Exception e) { + e.printStackTrace(); + // 打印异常信息 } DisplayMetrics dm = new DisplayMetrics(); + // 创建一个DisplayMetrics对象 + ((Activity)context).getWindowManager().getDefaultDisplay().getMetrics(dm); - return new Point(dm.widthPixels ,dm.heightPixels); + // 获取默认的分辨率 + + return new Point(dm.widthPixels , dm.heightPixels); + // 返回默认的分辨率 } - public static int getWindowWidth(Context context){ - WindowManager wm = (WindowManager) context - .getSystemService(Context.WINDOW_SERVICE); + /** + * 获取窗口宽度的方法 + * + * @param context 上下文对象 + * + * @return 窗口的宽度 + */ + public static int getWindowWidth(Context context) { + WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + // 获取WindowManager服务 + DisplayMetrics dm = new DisplayMetrics(); + // 创建一个DisplayMetrics对象 + wm.getDefaultDisplay().getMetrics(dm); + // 获取默认的分辨率 + int width = dm.widthPixels; + // 获取宽度 + return width; + // 返回宽度 } - public static int getWindowHeight(Context context){ - WindowManager wm = (WindowManager) context - .getSystemService(Context.WINDOW_SERVICE); + + /** + * 获取窗口高度的方法 + * + * @param context 上下文对象 + * + * @return 窗口的高度 + */ + public static int getWindowHeight(Context context) { + WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + // 获取WindowManager服务 + DisplayMetrics dm = new DisplayMetrics(); + // 创建一个DisplayMetrics对象 + wm.getDefaultDisplay().getMetrics(dm); + // 获取默认的分辨率 + int height = dm.heightPixels; + // 获取高度 + return height; + // 返回高度 } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/monke/monkeybook/utils/NetworkUtil.java b/app/src/main/java/com/monke/monkeybook/utils/NetworkUtil.java index f4c5044..9d0b0d8 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/NetworkUtil.java +++ b/app/src/main/java/com/monke/monkeybook/utils/NetworkUtil.java @@ -1,37 +1,74 @@ //Copyright (c) 2017. 章钦豪. All rights reserved. +// 版权声明,表明代码的版权归属于章钦豪。 + package com.monke.monkeybook.utils; +// 声明包名。 import android.content.Context; +// 导入Context类,提供了一个操作当前环境的抽象。 + import android.net.ConnectivityManager; +// 导入ConnectivityManager类,用于获取网络连接信息。 + import android.net.NetworkInfo; +// 导入NetworkInfo类,用于获取网络状态信息。 + import com.monke.monkeybook.MApplication; +// 导入MApplication类,用于获取应用程序的实例。 + import com.monke.monkeybook.R; +// 导入R类,用于访问应用程序的资源。 + import java.util.HashMap; +// 导入HashMap类,用于创建键值对映射。 + import java.util.Map; +// 导入Map接口,用于提供键值对集合。 public class NetworkUtil { +// 声明NetworkUtil工具类,用于网络相关的工具方法。 + private static final Map errorMap = new HashMap<>(); + // 创建一个HashMap,用于存储错误代码和错误信息的映射。 public static final int SUCCESS = 10000; + // 定义一个常量,表示操作成功。 + public static final int ERROR_CODE_NONET = 10001; + // 定义一个常量,表示网络不可用错误代码。 + public static final int ERROR_CODE_OUTTIME = 10002; + // 定义一个常量,表示网络超时错误代码。 + public static final int ERROR_CODE_ANALY = 10003; + // 定义一个常量,表示网络分析错误代码。 static{ + // 初始化静态代码块,用于初始化errorMap。 errorMap.put(ERROR_CODE_NONET, MApplication.getInstance().getString(R.string.net_error_10001)); + + // 将网络不可用错误代码和错误信息添加到errorMap。 errorMap.put(ERROR_CODE_OUTTIME, MApplication.getInstance().getString(R.string.net_error_10002)); + + // 将网络超时错误代码和错误信息添加到errorMap。 + errorMap.put(ERROR_CODE_ANALY, MApplication.getInstance().getString(R.string.net_error_10003)); + // 将网络分析错误代码和错误信息添加到errorMap。 } public static String getErrorTip(int code) { + // 定义一个方法,用于根据错误代码获取错误提示信息。 return errorMap.get(code); } public static boolean isNetWorkAvailable() { + // 定义一个方法,用于检查网络是否可用。 ConnectivityManager manager = (ConnectivityManager) MApplication.getInstance() .getSystemService(Context.CONNECTIVITY_SERVICE); + // 获取ConnectivityManager服务实例。 if (manager != null) { NetworkInfo info = manager.getActiveNetworkInfo(); + // 获取当前活跃的网络信息。 if (info != null && info.isConnected()) { return true; } else { @@ -41,4 +78,4 @@ public class NetworkUtil { return false; } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/monke/monkeybook/utils/NumberUtil.java b/app/src/main/java/com/monke/monkeybook/utils/NumberUtil.java index d6a9e0e..65255b2 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/NumberUtil.java +++ b/app/src/main/java/com/monke/monkeybook/utils/NumberUtil.java @@ -1,36 +1,73 @@ //Copyright (c) 2017. 章钦豪. All rights reserved. +// 版权声明,表明代码的版权归属于章钦豪。 + package com.monke.monkeybook.utils; +// 声明包名。 public class NumberUtil { +// 声明NumberUtil工具类,用于数字转换相关的工具方法。 /** * 中文数字转阿拉伯数字 */ + private static int chineseNumber2Int(String chineseNumber){ + // 定义一个私有静态方法,用于将中文数字转换为阿拉伯数字。 + int result = 0; + // 用于存储转换结果。 + int temp = 1;//存放一个单位的数字如:十万 + // 临时存储一个单位的数值,如“十”则为10,“百”则为100。 + int count = 0;//判断是否有chArr + // 用于计数“千”、“万”、“亿”等单位出现的次数。 + char[] cnArr = new char[]{'一','二','三','四','五','六','七','八','九'}; + // 包含中文数字0-9的数组。 + char[] chArr = new char[]{'十','百','千','万','亿'}; + // 包含中文单位“十”、“百”、“千”、“万”、“亿”的数组。 + for (int i = 0; i < chineseNumber.length(); i++) { + // 遍历输入的中文数字字符串。 + boolean b = true;//判断是否是chArr + // 标记当前字符是否属于单位数组chArr。 + char c = chineseNumber.charAt(i); - for (int j = 0; j < cnArr.length; j++) {//非单位,即数字 + // 获取当前遍历到的字符。 + for (int j = 0; j < cnArr.length; j++) { + //非单位,即数字 + // 检查当前字符是否为数字('一'到'九')。 + if (c == cnArr[j]) { - if(0 != count){//添加下一个单位之前,先把上一个单位值添加到结果中 + if(0 != count){ + //添加下一个单位之前,先把上一个单位值添加到结果中 + // 如果count不为0,说明之前已经遇到了单位字符,需要先计算之前的数值。 result += temp; + // 将临时数值累加到结果中。 + temp = 1; + // 重置临时数值为1。 + count = 0; + // 重置计数器。 } // 下标+1,就是对应的值 temp = j + 1; + // 将当前数字字符转换为对应的数值,并更新temp。 b = false; + // 标记当前字符已处理。 break; } } if(b){//单位{'十','百','千','万','亿'} + // 如果当前字符不是数字,那么它应该是一个单位。 for (int j = 0; j < chArr.length; j++) { + // 遍历单位数组,检查当前字符是否为单位。 if (c == chArr[j]) { + // 根据单位字符,更新temp的值。 switch (j) { case 0: temp *= 10; @@ -51,13 +88,21 @@ public class NumberUtil { break; } count++; + // 每遇到一个单位字符,计数器增加。 + } } } - if (i == chineseNumber.length() - 1) {//遍历到最后一个字符 + if (i == chineseNumber.length() - 1) { + //遍历到最后一个字符 + // 如果已经遍历到字符串的最后一个字符。 + result += temp; + // 将最后一个单位的值累加到结果中。 } } return result; + + // 返回转换后的阿拉伯数字。 } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/monke/monkeybook/utils/ParseSystemUtil.java b/app/src/main/java/com/monke/monkeybook/utils/ParseSystemUtil.java index 396f6e9..de0c5bf 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/ParseSystemUtil.java +++ b/app/src/main/java/com/monke/monkeybook/utils/ParseSystemUtil.java @@ -1,36 +1,69 @@ +// 声明一个名为ParseSystemUtil的公共类,它位于com.monke.monkeybook.utils包下 package com.monke.monkeybook.utils; public class ParseSystemUtil { - /**将二进制转换成16进制 - * @param buf - * @return + + /** + * 将字节数组(二进制)转换为十六进制字符串的方法。 + * + * @param buf 输入的字节数组 + * @return 转换后的十六进制字符串 */ public static String parseByte2HexStr(byte buf[]) { + // 创建一个StringBuffer对象,用于构建最终的十六进制字符串 StringBuffer sb = new StringBuffer(); + + // 遍历输入的字节数组 for (int i = 0; i < buf.length; i++) { + // 将当前字节转换为无符号的十六进制字符串 + // 注意:byte在Java中是有符号的,所以使用& 0xFF来转换为无符号数 String hex = Integer.toHexString(buf[i] & 0xFF); + + // 如果转换后的十六进制字符串长度为1(即只包含一个十六进制数字),则在其前面补0 if (hex.length() == 1) { hex = '0' + hex; } + + // 将转换后的十六进制字符串(大写)追加到StringBuffer中 sb.append(hex.toUpperCase()); } + + // 返回构建好的十六进制字符串 return sb.toString(); } - /**将16进制转换为二进制 - * @param hexStr - * @return + /** + * 将十六进制字符串转换为字节数组(二进制)的方法。 + * + * @param hexStr 输入的十六进制字符串 + * @return 转换后的字节数组 */ public static byte[] parseHexStr2Byte(String hexStr) { - if (hexStr==null || hexStr.length() ==0) { + + // 如果输入的十六进制字符串为空或为null,则返回null + if (hexStr == null || hexStr.length() == 0) { return null; } - byte[] result = new byte[hexStr.length()/2]; - for (int i = 0;i< hexStr.length()/2; i++) { - int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16); - int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16); + + // 根据输入的十六进制字符串长度计算字节数组的长度 + // 每两个十六进制字符表示一个字节 + byte[] result = new byte[hexStr.length() / 2]; + + // 遍历输入的十六进制字符串,每两个字符一组进行转换 + for (int i = 0; i < hexStr.length() / 2; i++) { + + // 从当前位置开始,截取两个十六进制字符 + // 第一个参数是起始位置,第二个参数是结束位置(不包含) + + int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16); + int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16); + + // 将两个十六进制数字组合成一个字节,并存储到结果数组中 + // 注意:这里需要将结果转换为byte类型,因为parseInt返回的是int类型 result[i] = (byte) (high * 16 + low); } + + // 返回转换后的字节数组 return result; } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/monke/monkeybook/utils/PremissionCheck.java b/app/src/main/java/com/monke/monkeybook/utils/PremissionCheck.java index bd720e5..0e8169a 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/PremissionCheck.java +++ b/app/src/main/java/com/monke/monkeybook/utils/PremissionCheck.java @@ -1,54 +1,101 @@ +// 版权信息,表明此代码由章钦豪在2017年创建并保留所有权利 //Copyright (c) 2017. 章钦豪. All rights reserved. + +// 声明包名,表示此类位于com.monke.monkeybook.utils包下 package com.monke.monkeybook.utils; +// 导入所需的Android类 +// 导入Android内容上下文类,用于访问应用的特定资源和类以及调用应用级别的操作 import android.content.Context; + +// 导入Android意图类,用于在不同组件(如Activity、Service等)之间进行通信 import android.content.Intent; + +// 导入Android包信息类,用于获取已安装应用包的信息,如版本号、签名等 import android.content.pm.PackageInfo; + +// 导入Android包管理器类,用于访问应用包的信息,如已安装应用的列表、应用签名等 import android.content.pm.PackageManager; + +// 导入Android URI类,用于表示统一资源标识符(URI),通常用于访问网络资源或文件 import android.net.Uri; + +// 导入Android构建类,用于获取当前Android系统的版本信息 import android.os.Build; + +// 导入Android支持库中的权限检查类,用于检查应用是否具有执行特定操作的权限 import android.support.v4.content.PermissionChecker; +// 声明一个名为PremissionCheck的公共类,用于检查权限和请求权限设置 public class PremissionCheck { - public static Boolean checkPremission(Context context,String permission){ - boolean result = false; + + // 定义一个静态方法,用于检查应用是否具有指定的权限 + public static Boolean checkPremission(Context context, String permission) { + boolean result = false; // 初始化结果为false,表示没有权限 + + // 检查目标SDK版本和当前SDK版本是否都大于或等于Android M(6.0) if (getTargetSdkVersion(context) >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - result = context.checkSelfPermission(permission) - == PackageManager.PERMISSION_GRANTED; + + // 如果是,则使用context的checkSelfPermission方法检查权限 + result = context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED; } else { - result = PermissionChecker.checkSelfPermission(context, permission) - == PermissionChecker.PERMISSION_GRANTED; + + // 如果不是,则使用PermissionChecker的checkSelfPermission方法检查权限 + result = PermissionChecker.checkSelfPermission(context, permission) == PermissionChecker.PERMISSION_GRANTED; } + + // 返回检查结果 return result; } + // 定义一个私有静态方法,用于获取应用的目标SDK版本 private static int getTargetSdkVersion(Context context) { - int version = 0; + int version = 0; // 初始化版本号为0 try { - final PackageInfo info = context.getPackageManager().getPackageInfo( - context.getPackageName(), 0); + // 通过包管理器获取应用的包信息 + final PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); + + // 从包信息中获取目标SDK版本 version = info.applicationInfo.targetSdkVersion; } catch (PackageManager.NameNotFoundException e) { + + // 如果捕获到包未找到的异常,则打印堆栈跟踪信息 e.printStackTrace(); } + // 返回目标SDK版本 return version; } + + // 定义一个静态方法,用于请求跳转到应用的权限设置页面 public static void requestPermissionSetting(Context from) { try { + // 创建一个Intent对象 Intent localIntent = new Intent(); + + // 为Intent添加新任务标志 localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + // 根据Android版本选择不同的Action和Data来跳转到权限设置页面 if (Build.VERSION.SDK_INT >= 9) { + // 对于Android 2.3(API级别9)及以上版本,使用APPLICATION_DETAILS_SETTINGS Action + localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS"); + // 设置Data为当前应用的包名 + localIntent.setData(Uri.fromParts("package", from.getPackageName(), null)); } else if (Build.VERSION.SDK_INT <= 8) { + // 对于Android 2.2(API级别8)及以下版本,使用Intent的ACTION_VIEW Action和特定的类名 localIntent.setAction(Intent.ACTION_VIEW); localIntent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails"); + // 通过Extra传递当前应用的包名 localIntent.putExtra("com.android.settings.ApplicationPkgName", from.getPackageName()); } + + // 启动Intent,跳转到权限设置页面 from.startActivity(localIntent); } catch (Exception e) { + // 如果捕获到异常,则打印堆栈跟踪信息 e.printStackTrace(); } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/monke/monkeybook/utils/aes/AESUtil.java b/app/src/main/java/com/monke/monkeybook/utils/aes/AESUtil.java index 9195c1b..7fc28dd 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/aes/AESUtil.java +++ b/app/src/main/java/com/monke/monkeybook/utils/aes/AESUtil.java @@ -1,47 +1,97 @@ package com.monke.monkeybook.utils.aes; +// 声明包名,指定该类属于哪个包。 import com.monke.monkeybook.utils.ParseSystemUtil; +// 导入ParseSystemUtil类,用于处理字节和十六进制字符串的转换。 import javax.crypto.*; +// 导入javax.crypto包中的所有类,用于AES加密和解密。 import javax.crypto.spec.SecretKeySpec; +// 导入SecretKeySpec类,用于指定AES密钥。 import java.io.UnsupportedEncodingException; +// 导入UnsupportedEncodingException类,用于处理可能的编码不支持异常。 public class AESUtil { +// 声明AESUtil类,用于提供AES加密和解密的工具方法。 + public static String aesEncode(String cleartext, String seed) throws Exception { + // 定义公有静态方法,用于AES加密。 + byte[] rawKey = deriveKeyInsecurely(seed, 32).getEncoded(); + // 从种子字符串生成AES密钥。 + byte[] result = encrypt(rawKey, cleartext.getBytes()); + // 使用AES密钥对明文进行加密。 + return ParseSystemUtil.parseByte2HexStr(result); + // 将加密结果转换为十六进制字符串并返回。 } public static String aesDecode(String encrypted, String seed) throws Exception { + // 定义公有静态方法,用于AES解密。 + byte[] rawKey = deriveKeyInsecurely(seed, 32).getEncoded(); + // 从种子字符串生成AES密钥。 + byte[] enc = ParseSystemUtil.parseHexStr2Byte(encrypted); + // 将十六进制字符串转换为字节数组。 + byte[] result = decrypt(rawKey, enc); + // 使用AES密钥对密文进行解密。 + return new String(result); + // 将解密结果转换为字符串并返回。 } private static SecretKey deriveKeyInsecurely(String password, int keySizeInBytes) throws UnsupportedEncodingException { + // 定义私有静态方法,用于不安全地从密码生成AES密钥。 + byte[] passwordBytes = password.getBytes("UTF-8"); + // 将密码字符串转换为UTF-8编码的字节数组。 + return new SecretKeySpec( InsecureSHA1PRNGKeyDerivator.deriveInsecureKey( passwordBytes, keySizeInBytes), "AES"); + // 使用SHA1PRNGKeyDerivator生成密钥,并创建SecretKeySpec对象。 } private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception { + // 定义私有静态方法,用于AES加密。 + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + // 创建AES密钥规范。 + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + // 获取AES/ECB/PKCS5Padding模式的Cipher实例。 + cipher.init(Cipher.ENCRYPT_MODE, skeySpec); + // 初始化Cipher为加密模式。 + byte[] encrypted = cipher.doFinal(clear); + // 对明文进行加密。 + return encrypted; + // 返回加密结果。 } private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception { + // 定义私有静态方法,用于AES解密。 + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + // 创建AES密钥规范。 + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + // 获取AES/ECB/PKCS5Padding模式的Cipher实例。 + cipher.init(Cipher.DECRYPT_MODE, skeySpec); + // 初始化Cipher为解密模式。 + byte[] decrypted = cipher.doFinal(encrypted); + // 对密文进行解密。 + return decrypted; + // 返回解密结果。 } } \ No newline at end of file diff --git a/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Decoder.java b/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Decoder.java index 49c99c2..fc37271 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Decoder.java +++ b/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Decoder.java @@ -1,7 +1,16 @@ +// 包声明 package com.monke.monkeybook.utils.base64; +// 导入Java的输入输出(IO)相关的类 +// IOException类表示输入输出操作中可能发生的异常 import java.io.IOException; + +// OutputStream类是字节输出流的所有类的超类,用于写入字节流到一个目标(如文件、内存等) import java.io.OutputStream; + +// PushbackInputStream类是一个包含了一个内部回退缓冲区的输入流 +// 它允许程序将最近从输入流中读取的字节(或者字节数组)回退到输入流中 +// 这可以用于重新读取已经读取的字节,或者用于纠正读取错误 import java.io.PushbackInputStream; public class BASE64Decoder extends CharacterDecoder{ @@ -9,83 +18,113 @@ public class BASE64Decoder extends CharacterDecoder{ private static final byte[] pem_convert_array = new byte[256]; byte[] decode_buffer = new byte[4]; - public BASE64Decoder() { - } - - protected int bytesPerAtom() { - return 4; - } - - protected int bytesPerLine() { - return 72; - } - - protected void decodeAtom(PushbackInputStream var1, OutputStream var2, int var3) throws IOException { - byte var5 = -1; - byte var6 = -1; - byte var7 = -1; - byte var8 = -1; - if (var3 < 2) { - throw new CEFormatException("BASE64Decoder: Not enough bytes for an atom."); - } else { - int var4; - do { - var4 = var1.read(); - if (var4 == -1) { - throw new CEStreamExhausted(); - } - } while(var4 == 10 || var4 == 13); + // 构造函数,无特定操作 + public BASE64Decoder() {} + + // 返回解码过程中每个“原子”(在这里是Base64编码的4个字符)对应的原始字节数(3或4) + protected int bytesPerAtom() { + return 4; // 这里的4指的是解码过程中处理的Base64字符数,不是字节数 + } + + // 返回解码后每行的字节数限制(Base64编码通常没有每行字节数的限制,但这里可能是为了格式化输出) + protected int bytesPerLine() { + return 72; // 假设每行最多72个字节的输出,用于格式化 + } + + // 解码一个Base64编码的“原子”(4个字符),并将其写入输出流 + protected void decodeAtom(PushbackInputStream var1, OutputStream var2, int var3) throws IOException { - this.decode_buffer[0] = (byte)var4; - var4 = this.readFully(var1, this.decode_buffer, 1, var3 - 1); - if (var4 == -1) { - throw new CEStreamExhausted(); + // 初始化四个变量来存储解码后的字节,初始值为-1表示无效 + byte var5 = -1, var6 = -1, var7 = -1, var8 = -1; + + // 如果提供的字符数少于2个,则无法解码(因为至少需要2个字符来表示一个有效的Base64编码单元) + if (var3 < 2) { + throw new CEFormatException("BASE64Decoder: Not enough bytes for an atom."); } else { - if (var3 > 3 && this.decode_buffer[3] == 61) { - var3 = 3; - } + int var4; - if (var3 > 2 && this.decode_buffer[2] == 61) { - var3 = 2; - } + // 跳过任何换行符(\n或\r),直到找到一个有效的Base64字符 + do { + var4 = var1.read(); + if (var4 == -1) { + // 如果到达输入流的末尾 - switch(var3) { - case 4: - var8 = pem_convert_array[this.decode_buffer[3] & 255]; - case 3: - var7 = pem_convert_array[this.decode_buffer[2] & 255]; - case 2: - var6 = pem_convert_array[this.decode_buffer[1] & 255]; - var5 = pem_convert_array[this.decode_buffer[0] & 255]; - default: - switch(var3) { - case 2: - var2.write((byte)(var5 << 2 & 252 | var6 >>> 4 & 3)); - break; - case 3: - var2.write((byte)(var5 << 2 & 252 | var6 >>> 4 & 3)); - var2.write((byte)(var6 << 4 & 240 | var7 >>> 2 & 15)); - break; - case 4: - var2.write((byte)(var5 << 2 & 252 | var6 >>> 4 & 3)); - var2.write((byte)(var6 << 4 & 240 | var7 >>> 2 & 15)); - var2.write((byte)(var7 << 6 & 192 | var8 & 63)); - } + throw new CEStreamExhausted(); + // 抛出流耗尽异常 + } + } while (var4 == 10 || var4 == 13); // 10是\n,13是\r + + // 将读取的第一个有效字符存入解码缓冲区 + this.decode_buffer[0] = (byte) var4; + // 读取剩余的字符到解码缓冲区,并返回实际读取的字符数 + var4 = this.readFully(var1, this.decode_buffer, 1, var3 - 1); + if (var4 == -1) { + // 如果再次到达输入流的末尾 + throw new CEStreamExhausted(); + // 抛出流耗尽异常 + } else { + // 如果缓冲区中有'='(表示Base64编码的填充字符),则调整要解码的字符数 + if (var3 > 3 && this.decode_buffer[3] == 61) { // 61是'='的ASCII码 + var3 = 3; + } + if (var3 > 2 && this.decode_buffer[2] == 61) { + var3 = 2; + } + // 根据读取的字符数(var3)进行解码 + switch (var3) { + case 4: // 如果读取了4个字符 + var8 = pem_convert_array[this.decode_buffer[3] & 255]; + // 解码最后一个字符 + case 3: // 如果读取了3个字符(或原本读取了4个但最后一个是'=') + var7 = pem_convert_array[this.decode_buffer[2] & 255]; + // 解码倒数第二个字符 + case 2: // 如果读取了2个字符(或原本读取了更多但后面有'=') + var6 = pem_convert_array[this.decode_buffer[1] & 255]; + // 解码第三个字符 + var5 = pem_convert_array[this.decode_buffer[0] & 255]; + // 解码第一个字符 + default: + // 这里的default实际上不会执行,因为switch已经覆盖了所有可能的case + // 根据读取的字符数(即var3的值),执行相应的解码操作 + switch (var3) { + case 2: + // 2个字符,对应1个字节的原始数据 + var2.write((byte) (var5 << 2 & 252 | var6 >>> 4 & 3)); + // 解码并写入输出流 + break; + case 3: + // 3个字符,对应2个字节的原始数据 + var2.write((byte) (var5 << 2 & 252 | var6 >>> 4 & 3)); + // 解码第一个字节并写入 + var2.write((byte) (var6 << 4 & 240 | var7 >>> 2 & 15)); + // 解码第二个字节并写入 + break; + case 4: + // 4个字符,对应3个字节的原始数据(或带填充的4个字节数据) + var2.write((byte) (var5 << 2 & 252 | var6 >>> 4 & 3)); + // 解码第一个字节并写入 + var2.write((byte) (var6 << 4 & 240 | var7 >>> 2 & 15)); + // 解码第二个字节并写入 + var2.write((byte) (var7 << 6 & 192 | var8 & 63)); + // 解码第三个字节并写入 + } + } } } } - } - static { - int var0; - for(var0 = 0; var0 < 255; ++var0) { - pem_convert_array[var0] = -1; - } + // 静态初始化块,用于填充pem_convert_array数组 + static { + int var0; + // 初始化pem_convert_array数组,将所有元素设置为-1(表示无效字符) + for (var0 = 0; var0 < 255; ++var0) { + pem_convert_array[var0] = -1; + } - for(var0 = 0; var0 < pem_array.length; ++var0) { - pem_convert_array[pem_array[var0]] = (byte)var0; + // 将pem_array中的字符映射到pem_convert_array中,字符的ASCII值作为索引,字符在pem_array中的位置作为值 + for (var0 = 0; var0 < pem_array.length; ++var0) { + pem_convert_array[pem_array[var0]] = (byte) var0; + } } - - } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Encoder.java b/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Encoder.java index 4c5fe1d..410c40f 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Encoder.java +++ b/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Encoder.java @@ -1,52 +1,83 @@ package com.monke.monkeybook.utils.base64; +// 定义该类所在的包 import java.io.IOException; +// 导入IOException类,用于处理IO异常 import java.io.OutputStream; +// 导入OutputStream类,用于处理输出流 public class BASE64Encoder extends CharacterEncoder{ + // 定义BASE64Encoder类,继承自CharacterEncoder类 + // 定义BASE64编码表,包含所有可用的字符 private static final char[] pem_array = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; public BASE64Encoder() { + // 构造方法,初始化BASE64Encoder对象 } + // 每个原始数据块的字节数是3 protected int bytesPerAtom() { return 3; } + // 每行的字节数限制为57 protected int bytesPerLine() { return 57; } + // 编码一个原子(即3字节数据块),并将其写入输出流 protected void encodeAtom(OutputStream var1, byte[] var2, int var3, int var4) throws IOException { byte var5; + // 如果剩余字节数为1 if (var4 == 1) { var5 = var2[var3]; + // 取第一个字节 byte var6 = 0; + // 由于只剩1个字节,第二个字节为0 boolean var7 = false; + // 未使用的变量 var1.write(pem_array[var5 >>> 2 & 63]); + // 写入第一个字符 var1.write(pem_array[(var5 << 4 & 48) + (var6 >>> 4 & 15)]); + // 写入第二个字符 var1.write(61); + // 填充字符 '=' var1.write(61); + // 填充字符 '=' } else { byte var8; + // 如果剩余字节数为2 if (var4 == 2) { var5 = var2[var3]; + // 取第一个字节 var8 = var2[var3 + 1]; + // 取第二个字节 byte var9 = 0; + // 第三个字节为0 var1.write(pem_array[var5 >>> 2 & 63]); + // 写入第一个字符 var1.write(pem_array[(var5 << 4 & 48) + (var8 >>> 4 & 15)]); + // 写入第二个字符 var1.write(pem_array[(var8 << 2 & 60) + (var9 >>> 6 & 3)]); + // 写入第三个字符 var1.write(61); + // 填充字符 '=' } else { var5 = var2[var3]; + // 取第一个字节 var8 = var2[var3 + 1]; + // 取第二个字节 byte var10 = var2[var3 + 2]; + // 取第三个字节 var1.write(pem_array[var5 >>> 2 & 63]); + // 写入第一个字符 var1.write(pem_array[(var5 << 4 & 48) + (var8 >>> 4 & 15)]); + // 写入第二个字符 var1.write(pem_array[(var8 << 2 & 60) + (var10 >>> 6 & 3)]); + // 写入第三个字符 var1.write(pem_array[var10 & 63]); + // 写入第四个字符 } } - } } diff --git a/app/src/main/java/com/monke/monkeybook/utils/base64/CEFormatException.java b/app/src/main/java/com/monke/monkeybook/utils/base64/CEFormatException.java index fc6450b..d8eac66 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/base64/CEFormatException.java +++ b/app/src/main/java/com/monke/monkeybook/utils/base64/CEFormatException.java @@ -1,11 +1,17 @@ package com.monke.monkeybook.utils.base64; +// 定义该类所在的包 import java.io.IOException; +// 导入IOException类,用于处理IO异常 public class CEFormatException extends IOException { + // 定义CEFormatException类,继承自IOException类 static final long serialVersionUID = -7139121221067081482L; + // 定义serialVersionUID,确保类的序列化兼容性 public CEFormatException(String var1) { + // 构造方法,接收一个字符串作为异常信息 super(var1); + // 调用父类IOException的构造方法,传递异常信息 } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/monke/monkeybook/utils/base64/CEStreamExhausted.java b/app/src/main/java/com/monke/monkeybook/utils/base64/CEStreamExhausted.java index 51e1f26..b70c55d 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/base64/CEStreamExhausted.java +++ b/app/src/main/java/com/monke/monkeybook/utils/base64/CEStreamExhausted.java @@ -1,10 +1,15 @@ package com.monke.monkeybook.utils.base64; +// 定义该类所在的包 import java.io.IOException; +// 导入IOException类,用于处理输入输出异常 public class CEStreamExhausted extends IOException { + // 定义CEStreamExhausted类,继承自IOException类 + static final long serialVersionUID = -5889118049525891904L; + // 定义serialVersionUID,用于序列化时验证类的兼容性 - public CEStreamExhausted() { + public CEStreamExhausted() { // 构造方法,不带参数 } } diff --git a/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterDecoder.java b/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterDecoder.java index 21f6864..8ef8c55 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterDecoder.java +++ b/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterDecoder.java @@ -1,102 +1,177 @@ -package com.monke.monkeybook.utils.base64; +package com.monke.monkeybook.utils.base64; // 定义该类所在的包 import java.io.ByteArrayInputStream; +// 导入ByteArrayInputStream类,用于从字节数组中读取数据 import java.io.ByteArrayOutputStream; +// 导入ByteArrayOutputStream类,用于将数据写入字节数组 import java.io.IOException; +// 导入IOException类,用于处理输入输出异常 import java.io.InputStream; +// 导入InputStream类,用于处理输入流 import java.io.OutputStream; +// 导入OutputStream类,用于处理输出流 import java.io.PushbackInputStream; +// 导入PushbackInputStream类,用于从输入流中推回字节 import java.nio.ByteBuffer; +// 导入ByteBuffer类,用于操作字节缓冲区 public abstract class CharacterDecoder { + // 定义CharacterDecoder抽象类,用于解码字符流 public CharacterDecoder() { + // 构造方法 } + // 抽象方法,返回每个原子(数据块)字节数 protected abstract int bytesPerAtom(); + // 抽象方法,返回每行字节数 protected abstract int bytesPerLine(); + // 解码缓冲区前缀,空实现,子类可以重写 protected void decodeBufferPrefix(PushbackInputStream var1, OutputStream var2) throws IOException { } + // 解码缓冲区后缀,空实现,子类可以重写 protected void decodeBufferSuffix(PushbackInputStream var1, OutputStream var2) throws IOException { } + // 解码行前缀,默认实现返回每行的字节数 protected int decodeLinePrefix(PushbackInputStream var1, OutputStream var2) throws IOException { - return this.bytesPerLine(); + return this.bytesPerLine(); // 返回每行的字节数 } + // 解码行后缀,空实现,子类可以重写 protected void decodeLineSuffix(PushbackInputStream var1, OutputStream var2) throws IOException { } + // 解码原子(数据块),此方法抛出CEStreamExhausted异常,表示流已耗尽 protected void decodeAtom(PushbackInputStream var1, OutputStream var2, int var3) throws IOException { - throw new CEStreamExhausted(); + throw new CEStreamExhausted(); // 抛出流耗尽异常 } + // 从输入流中读取指定字节数,直到读取完成或遇到流结束 protected int readFully(InputStream var1, byte[] var2, int var3, int var4) throws IOException { - for(int var5 = 0; var5 < var4; ++var5) { + for (int var5 = 0; var5 < var4; ++var5) { + // 循环读取字节直到满足要求 + int var6 = var1.read(); + // 读取一个字节 + if (var6 == -1) { + // 如果遇到流结束 + return var5 == 0 ? -1 : var5; + // 如果没有读取字节则返回-1,否则返回已读取字节数 } - var2[var5 + var3] = (byte)var6; + // 将读取的字节存入目标数组 } - - return var4; + return var4; // 返回读取的字节数 } + // 解码整个缓冲区数据,并将解码后的结果写入输出流 public void decodeBuffer(InputStream var1, OutputStream var2) throws IOException { int var4 = 0; + // 已解码字节数 + PushbackInputStream var5 = new PushbackInputStream(var1); + // 使用PushbackInputStream处理输入流 + this.decodeBufferPrefix(var5, var2); + // 解码前缀,子类可以实现 - while(true) { + while (true) { + // 不断读取并解码数据直到流结束 try { int var6 = this.decodeLinePrefix(var5, var2); + // 解码行前缀 int var3; - for(var3 = 0; var3 + this.bytesPerAtom() < var6; var3 += this.bytesPerAtom()) { + // 解码每个原子(数据块),直到当前行的数据量达到要求 + + for (var3 = 0; var3 + this.bytesPerAtom() < var6; var3 += this.bytesPerAtom()) { this.decodeAtom(var5, var2, this.bytesPerAtom()); + // 解码一个原子 + var4 += this.bytesPerAtom(); + // 累加已解码字节数 } if (var3 + this.bytesPerAtom() == var6) { + // 如果行正好能填满 + this.decodeAtom(var5, var2, this.bytesPerAtom()); + // 解码一个原子 + var4 += this.bytesPerAtom(); + // 累加已解码字节数 + } else { + // 如果行不足一个原子 + this.decodeAtom(var5, var2, var6 - var3); + // 解码剩余字节 + var4 += var6 - var3; + // 累加已解码字节数 } this.decodeLineSuffix(var5, var2); + // 解码行后缀,子类可以实现 + } catch (CEStreamExhausted var8) { + // 捕获流耗尽异常 + this.decodeBufferSuffix(var5, var2); + // 解码后缀 + return; + // 结束解码 } } } + // 将一个字符串解码为字节数组 public byte[] decodeBuffer(String var1) throws IOException { byte[] var2 = new byte[var1.length()]; + // 创建字节数组 var1.getBytes(0, var1.length(), var2, 0); + // 将字符串转为字节数组 + ByteArrayInputStream var3 = new ByteArrayInputStream(var2); + // 创建字节数组输入流 + ByteArrayOutputStream var4 = new ByteArrayOutputStream(); + // 创建字节数组输出流 + this.decodeBuffer(var3, var4); + // 解码缓冲区 + return var4.toByteArray(); + // 返回解码后的字节数组 } + // 将输入流解码为字节数组 public byte[] decodeBuffer(InputStream var1) throws IOException { ByteArrayOutputStream var2 = new ByteArrayOutputStream(); + // 创建字节数组输出流 + this.decodeBuffer(var1, var2); + // 解码缓冲区 + return var2.toByteArray(); + // 返回解码后的字节数组 } + // 将解码后的数据转为ByteBuffer对象 public ByteBuffer decodeBufferToByteBuffer(String var1) throws IOException { return ByteBuffer.wrap(this.decodeBuffer(var1)); + // 解码字符串并包装为ByteBuffer } + // 将解码后的数据转为ByteBuffer对象 public ByteBuffer decodeBufferToByteBuffer(InputStream var1) throws IOException { return ByteBuffer.wrap(this.decodeBuffer(var1)); + // 解码输入流并包装为ByteBuffer } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterEncoder.java b/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterEncoder.java index 1184f5b..9f6bccb 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterEncoder.java +++ b/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterEncoder.java @@ -1,181 +1,310 @@ package com.monke.monkeybook.utils.base64; +// 定义该类所在的包 import java.io.ByteArrayInputStream; +// 导入ByteArrayInputStream类,用于从字节数组中读取数据 + import java.io.ByteArrayOutputStream; +// 导入ByteArrayOutputStream类,用于将数据写入字节数组 + import java.io.IOException; +// 导入IOException类,用于处理输入输出异常 + import java.io.InputStream; +// 导入InputStream类,用于处理输入流 + import java.io.OutputStream; +// 导入OutputStream类,用于处理输出流 + import java.io.PrintStream; +// 导入PrintStream类,用于处理带格式的输出流 + import java.nio.ByteBuffer; +// 导入ByteBuffer类,用于操作字节缓冲区 public abstract class CharacterEncoder { + // 定义一个抽象类CharacterEncoder,用于字符编码 + protected PrintStream pStream; + // 声明PrintStream对象pStream,用于处理输出流 public CharacterEncoder() { + // 构造方法 } + // 抽象方法,返回每个原子(数据块)字节数 protected abstract int bytesPerAtom(); + // 抽象方法,返回每行字节数 protected abstract int bytesPerLine(); + // 编码缓冲区前缀,设置输出流 protected void encodeBufferPrefix(OutputStream var1) throws IOException { this.pStream = new PrintStream(var1); + // 使用PrintStream包装输出流 } + // 编码缓冲区后缀,空实现,子类可以重写 protected void encodeBufferSuffix(OutputStream var1) throws IOException { } + // 编码行前缀,空实现,子类可以重写 protected void encodeLinePrefix(OutputStream var1, int var2) throws IOException { } + // 编码行后缀,打印换行符 protected void encodeLineSuffix(OutputStream var1) throws IOException { - this.pStream.println(); + this.pStream.println(); // 输出换行符 } + // 抽象方法,编码原子(数据块) protected abstract void encodeAtom(OutputStream var1, byte[] var2, int var3, int var4) throws IOException; + // 从输入流读取数据到字节数组 protected int readFully(InputStream var1, byte[] var2) throws IOException { - for(int var3 = 0; var3 < var2.length; ++var3) { + for (int var3 = 0; var3 < var2.length; ++var3) { + // 循环读取字节 + int var4 = var1.read(); + // 从输入流读取一个字节 + if (var4 == -1) { + // 如果读取到流末尾 + return var3; + // 返回已经读取的字节数 } - - var2[var3] = (byte)var4; + var2[var3] = (byte) var4; + // 存储读取的字节 } - return var2.length; + // 返回读取的字节数 } + // 编码输入流数据并将结果写入输出流 public void encode(InputStream var1, OutputStream var2) throws IOException { byte[] var5 = new byte[this.bytesPerLine()]; + // 创建字节数组用于存储每行数据 + this.encodeBufferPrefix(var2); + // 编码缓冲区前缀 - while(true) { + while (true) { + // 不断读取并编码数据直到流结束 int var4 = this.readFully(var1, var5); + // 从输入流中读取数据 if (var4 == 0) { + // 如果没有读取到数据 break; + // 结束编码 } this.encodeLinePrefix(var2, var4); + // 编码行前缀 + + for (int var3 = 0; var3 < var4; var3 += this.bytesPerAtom()) { + // 遍历每个原子(数据块) - for(int var3 = 0; var3 < var4; var3 += this.bytesPerAtom()) { if (var3 + this.bytesPerAtom() <= var4) { + // 如果当前原子不超过剩余字节数 + this.encodeAtom(var2, var5, var3, this.bytesPerAtom()); + // 编码一个原子 } else { + // 如果当前原子超过剩余字节数 + this.encodeAtom(var2, var5, var3, var4 - var3); + // 编码剩余的字节 } } if (var4 < this.bytesPerLine()) { + // 如果当前行数据不足一行 break; + // 结束编码 } this.encodeLineSuffix(var2); + // 编码行后缀 } this.encodeBufferSuffix(var2); + // 编码缓冲区后缀 } + // 编码字节数组并将结果写入输出流 public void encode(byte[] var1, OutputStream var2) throws IOException { ByteArrayInputStream var3 = new ByteArrayInputStream(var1); - this.encode((InputStream)var3, var2); + // 创建字节数组输入流 + this.encode((InputStream) var3, var2); + // 调用encode方法进行编码 } + // 编码字节数组并返回编码后的字符串 public String encode(byte[] var1) { ByteArrayOutputStream var2 = new ByteArrayOutputStream(); + // 创建字节数组输出流 ByteArrayInputStream var3 = new ByteArrayInputStream(var1); + // 创建字节数组输入流 String var4 = null; + // 初始化编码结果字符串 try { - this.encode((InputStream)var3, var2); + this.encode((InputStream) var3, var2); + // 调用encode方法进行编码 var4 = var2.toString("8859_1"); + // 将输出流内容转换为字符串 return var4; + // 返回编码结果 } catch (Exception var6) { + // 捕获异常 throw new Error("CharacterEncoder.encode internal error"); + // 抛出错误 } } + // 从ByteBuffer获取字节数组 private byte[] getBytes(ByteBuffer var1) { byte[] var2 = null; + // 初始化字节数组 if (var1.hasArray()) { - byte[] var3 = var1.array(); + // 如果ByteBuffer内部有数组 + byte[] var3 = var1.array(); // 获取数组 if (var3.length == var1.capacity() && var3.length == var1.remaining()) { + // 如果数组长度符合要求 var2 = var3; + // 直接使用该数组 var1.position(var1.limit()); + // 更新ByteBuffer位置 } } if (var2 == null) { + // 如果没有直接可用的数组 var2 = new byte[var1.remaining()]; + // 创建新数组 var1.get(var2); + // 将ByteBuffer中的内容复制到新数组 } return var2; + // 返回字节数组 } + // 编码ByteBuffer数据并将结果写入输出流 public void encode(ByteBuffer var1, OutputStream var2) throws IOException { byte[] var3 = this.getBytes(var1); + // 获取ByteBuffer中的字节数组 this.encode(var3, var2); + // 调用encode方法进行编码 } + // 编码ByteBuffer数据并返回编码后的字符串 public String encode(ByteBuffer var1) { byte[] var2 = this.getBytes(var1); + // 获取ByteBuffer中的字节数组 return this.encode(var2); + // 调用encode方法进行编码并返回结果 } + // 编码整个输入流缓冲区并将结果写入输出流 public void encodeBuffer(InputStream var1, OutputStream var2) throws IOException { byte[] var5 = new byte[this.bytesPerLine()]; + // 创建字节数组用于存储每行数据 this.encodeBufferPrefix(var2); + // 编码缓冲区前缀 int var4; do { var4 = this.readFully(var1, var5); + // 从输入流中读取数据 + if (var4 == 0) { + // 如果没有读取到数据 + break; + // 结束编码 } this.encodeLinePrefix(var2, var4); + // 编码行前缀 - for(int var3 = 0; var3 < var4; var3 += this.bytesPerAtom()) { + for (int var3 = 0; var3 < var4; var3 += this.bytesPerAtom()) { + // 遍历每个原子(数据块) if (var3 + this.bytesPerAtom() <= var4) { + // 如果当前原子不超过剩余字节数 + this.encodeAtom(var2, var5, var3, this.bytesPerAtom()); + // 编码一个原子 + } else { + // 如果当前原子超过剩余字节数 + this.encodeAtom(var2, var5, var3, var4 - var3); + // 编码剩余的字节 } } this.encodeLineSuffix(var2); - } while(var4 >= this.bytesPerLine()); + // 编码行后缀 + + } while (var4 >= this.bytesPerLine()); + // 如果当前行数据不够一行,则结束 + this.encodeBufferSuffix(var2); + // 编码缓冲区后缀 } + + // 编码字节数组并将结果写入输出流 public void encodeBuffer(byte[] var1, OutputStream var2) throws IOException { ByteArrayInputStream var3 = new ByteArrayInputStream(var1); - this.encodeBuffer((InputStream)var3, var2); + // 创建字节数组输入流 + + this.encodeBuffer((InputStream) var3, var2); + // 调用encodeBuffer方法进行编码 } + // 编码字节数组并返回编码后的字符串 public String encodeBuffer(byte[] var1) { ByteArrayOutputStream var2 = new ByteArrayOutputStream(); + // 创建字节数组输出流 + ByteArrayInputStream var3 = new ByteArrayInputStream(var1); + // 创建字节数组输入流 + try { - this.encodeBuffer((InputStream)var3, var2); + this.encodeBuffer((InputStream) var3, var2); + // 调用encodeBuffer方法进行编码 + } catch (Exception var5) { + // 捕获异常 + throw new Error("CharacterEncoder.encodeBuffer internal error"); + // 抛出错误 } return var2.toString(); + // 返回编码后的字符串 } + // 编码ByteBuffer并将结果写入输出流 public void encodeBuffer(ByteBuffer var1, OutputStream var2) throws IOException { byte[] var3 = this.getBytes(var1); + // 获取ByteBuffer中的字节数组 + this.encodeBuffer(var3, var2); + // 调用encodeBuffer方法进行编码 } + // 编码ByteBuffer并返回编码后的字符串 public String encodeBuffer(ByteBuffer var1) { byte[] var2 = this.getBytes(var1); + // 获取ByteBuffer中的字节数组 + return this.encodeBuffer(var2); + // 调用encodeBuffer方法进行编码并返回结果 } } diff --git a/app/src/main/java/com/monke/monkeybook/view/IBookDetailView.java b/app/src/main/java/com/monke/monkeybook/view/IBookDetailView.java index b759f09..1dca5fc 100644 --- a/app/src/main/java/com/monke/monkeybook/view/IBookDetailView.java +++ b/app/src/main/java/com/monke/monkeybook/view/IBookDetailView.java @@ -1,16 +1,26 @@ //Copyright (c) 2017. 章钦豪. All rights reserved. +// 版权声明,注明该代码的版权所有者 + package com.monke.monkeybook.view; +// 定义该接口所在的包 import com.monke.basemvplib.IView; +// 导入IView接口,IView是基类接口,所有视图接口都需要实现它 + +// 定义IBookDetailView接口,继承自IView接口,表示书籍详情页面的视图 +public interface IBookDetailView extends IView { -public interface IBookDetailView extends IView{ /** * 更新书籍详情UI + * 该方法用于刷新或更新书籍详情的界面 */ void updateView(); + // 定义一个抽象方法,用于更新书籍详情的UI界面 /** * 数据获取失败 + * 该方法用于处理书架数据获取失败的情况 */ void getBookShelfError(); + // 定义一个抽象方法,用于处理获取书架数据时发生的错误 } diff --git a/app/src/main/java/com/monke/monkeybook/view/IBookReadView.java b/app/src/main/java/com/monke/monkeybook/view/IBookReadView.java index cbf1884..2a3ad0b 100644 --- a/app/src/main/java/com/monke/monkeybook/view/IBookReadView.java +++ b/app/src/main/java/com/monke/monkeybook/view/IBookReadView.java @@ -1,45 +1,85 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +//Copyright (c) 2017. 章钦豪. All rights reserved. // 版权声明,标明代码的版权所有者及年份 + package com.monke.monkeybook.view; +// 定义该接口所在的包 import android.graphics.Paint; +// 导入Paint类,用于图形绘制 + import com.monke.basemvplib.IView; +// 导入IView接口,IView是一个通用视图接口,所有视图接口都需要实现它 -public interface IBookReadView extends IView{ +// 定义IBookReadView接口,继承自IView接口,表示小说阅读页面的视图 +public interface IBookReadView extends IView { /** * 获取当前阅读界面UI画笔 - * @return + * @return 返回绘制文本或图形的画笔 */ Paint getPaint(); + // 定义一个抽象方法,返回用于绘制文本的画笔对象 /** * 获取当前小说内容可绘制宽度 - * @return + * @return 返回小说内容可绘制区域的宽度(像素值) */ int getContentWidth(); + // 定义一个抽象方法,返回小说内容区域的宽度 /** * 小说数据初始化成功 - * @param durChapterIndex - * @param chapterAll - * @param durPageIndex + * @param durChapterIndex 当前章节索引 + * @param chapterAll 总章节数 + * @param durPageIndex 当前页面的页码 */ - void initContentSuccess(int durChapterIndex, int chapterAll, int durPageIndex); + void initContentSuccess(int durChapterIndex, int chapterAll, int durPageIndex); // 定义一个方法,初始化小说内容成功时调用,传递当前章节、总章节和当前页码信息 /** - * 开始加载 + * 开始加载小说 + * 显示加载状态或动画 */ void startLoadingBook(); + // 定义一个方法,开始加载小说时调用,用于显示加载过程中的UI状态 + /** + * 设置阅读进度条的最大值 + * @param count 总章节数 + */ void setHpbReadProgressMax(int count); + // 定义一个方法,用于设置小说阅读进度条的最大值 + /** + * 初始化弹出框 + * 用于弹出界面初始化 + */ void initPop(); + // 定义一个方法,初始化弹出框相关UI操作 + /** + * 显示加载中的小说页面 + * 显示加载进度条或动画 + */ void showLoadBook(); + // 定义一个方法,用于显示加载小说的过程 + /** + * 取消加载小说页面的显示 + * 隐藏加载进度条或动画 + */ void dimissLoadBook(); + // 定义一个方法,用于取消加载小说页面的显示 + /** + * 加载书籍失败时调用 + * 显示错误信息或提示 + */ void loadLocationBookError(); + // 定义一个方法,用于加载书籍时发生错误时调用 + /** + * 显示下载菜单 + * 用于显示下载相关的菜单或选项 + */ void showDownloadMenu(); + // 定义一个方法,用于显示下载菜单 } diff --git a/app/src/main/java/com/monke/monkeybook/view/IChoiceBookView.java b/app/src/main/java/com/monke/monkeybook/view/IChoiceBookView.java index c21a1d7..c2aabcb 100644 --- a/app/src/main/java/com/monke/monkeybook/view/IChoiceBookView.java +++ b/app/src/main/java/com/monke/monkeybook/view/IChoiceBookView.java @@ -1,30 +1,89 @@ //Copyright (c) 2017. 章钦豪. All rights reserved. +// 版权声明,标明代码的版权所有者及年份 + package com.monke.monkeybook.view; +// 定义该接口所在的包 import com.monke.basemvplib.IView; +// 导入IView接口,IView是一个通用的视图接口,所有视图接口都需要实现它 + import com.monke.monkeybook.bean.SearchBookBean; +// 导入SearchBookBean类,用于表示书籍数据 + import com.monke.monkeybook.view.adapter.ChoiceBookAdapter; +// 导入ChoiceBookAdapter类,用于书籍列表的适配器 + import java.util.List; +// 导入List类,表示书籍列表的数据类型 -public interface IChoiceBookView extends IView{ +// 定义IChoiceBookView接口,继承自IView接口,表示选择书籍界面的视图 +public interface IChoiceBookView extends IView { + /** + * 刷新搜索书籍列表 + * @param books 搜索到的书籍列表 + */ void refreshSearchBook(List books); + // 定义方法,刷新搜索到的书籍列表,传入书籍列表 + /** + * 加载更多书籍 + * @param books 更多书籍列表 + */ void loadMoreSearchBook(List books); + // 定义方法,用于加载更多书籍,传入更多的书籍列表 + /** + * 刷新操作完成 + * @param isAll 是否已加载完所有数据 + */ void refreshFinish(Boolean isAll); + // 定义方法,表示刷新操作完成,传入是否已加载完所有数据 + /** + * 加载更多操作完成 + * @param isAll 是否已加载完所有数据 + */ void loadMoreFinish(Boolean isAll); + // 定义方法,表示加载更多操作完成,传入是否已加载完所有数据 + /** + * 搜索书籍时发生错误 + */ void searchBookError(); + // 定义方法,表示在搜索书籍时发生错误 + /** + * 添加书籍到书架成功 + * @param searchBooks 成功添加到书架的书籍列表 + */ void addBookShelfSuccess(List searchBooks); + // 定义方法,表示成功将书籍添加到书架 + /** + * 添加书籍到书架失败 + * @param code 错误代码 + */ void addBookShelfFailed(int code); + // 定义方法,表示将书籍添加到书架失败,并返回错误代码 + /** + * 获取搜索书籍的适配器 + * @return 返回ChoiceBookAdapter实例,用于处理书籍列表的显示 + */ ChoiceBookAdapter getSearchBookAdapter(); + // 定义方法,返回ChoiceBookAdapter实例,用于获取书籍列表适配器 + /** + * 更新搜索书籍列表的某一项 + * @param index 要更新的书籍索引 + */ void updateSearchItem(int index); + // 定义方法,用于更新搜索书籍列表中的某一项,传入该项的索引 + /** + * 开始刷新动画 + */ void startRefreshAnim(); + // 定义方法,用于启动刷新动画,通常用于界面显示加载动画 } diff --git a/app/src/main/java/com/monke/monkeybook/view/IImportBookView.java b/app/src/main/java/com/monke/monkeybook/view/IImportBookView.java index e7fcf73..0d8f59a 100644 --- a/app/src/main/java/com/monke/monkeybook/view/IImportBookView.java +++ b/app/src/main/java/com/monke/monkeybook/view/IImportBookView.java @@ -1,29 +1,40 @@ //Copyright (c) 2017. 章钦豪. All rights reserved. +// 版权声明,标明代码的版权所有者及年份 + package com.monke.monkeybook.view; +// 定义该接口所在的包 import com.monke.basemvplib.IView; +// 导入IView接口,所有视图接口都需要实现它 + import java.io.File; +// 导入File类,用于处理文件操作 -public interface IImportBookView extends IView{ +// 定义IImportBookView接口,继承自IView接口,表示导入书籍界面的视图 +public interface IImportBookView extends IView { /** * 新增书籍 - * @param newFile + * @param newFile 新增的书籍文件 */ void addNewBook(File newFile); + // 定义方法,用于将一个新书籍文件添加到系统 /** * 书籍搜索完成 */ void searchFinish(); + // 定义方法,表示书籍的搜索操作已完成 /** - * 添加成功 + * 添加书籍成功 */ void addSuccess(); + // 定义方法,表示书籍添加操作成功 /** - * 添加失败 + * 添加书籍失败 */ void addError(); -} \ No newline at end of file + // 定义方法,表示书籍添加操作失败 +} diff --git a/app/src/main/java/com/monke/monkeybook/view/ILibraryView.java b/app/src/main/java/com/monke/monkeybook/view/ILibraryView.java index a90a7d5..cd543b7 100644 --- a/app/src/main/java/com/monke/monkeybook/view/ILibraryView.java +++ b/app/src/main/java/com/monke/monkeybook/view/ILibraryView.java @@ -1,19 +1,27 @@ //Copyright (c) 2017. 章钦豪. All rights reserved. +// 版权声明,标明代码的版权所有者及年份 + package com.monke.monkeybook.view; +// 定义该接口所在的包 import com.monke.basemvplib.IView; +// 导入IView接口,所有视图接口都需要实现它 import com.monke.monkeybook.bean.LibraryBean; +// 导入LibraryBean类,表示书城中的书籍数据模型 -public interface ILibraryView extends IView{ +// 定义ILibraryView接口,继承自IView接口,表示书城页面的视图 +public interface ILibraryView extends IView { /** - * 书城书籍获取成功 更新UI - * @param library + * 书城书籍获取成功,更新UI + * @param library 获取到的书城数据,类型为LibraryBean */ void updateUI(LibraryBean library); + // 定义方法,表示书城书籍数据获取成功并更新UI,传入一个LibraryBean对象 /** - * 书城数据刷新成功 更新UI + * 书城数据刷新成功,更新UI */ void finishRefresh(); + // 定义方法,表示书城数据刷新成功并更新UI } diff --git a/app/src/main/java/com/monke/monkeybook/view/IMainView.java b/app/src/main/java/com/monke/monkeybook/view/IMainView.java index aa04d58..84d3dac 100644 --- a/app/src/main/java/com/monke/monkeybook/view/IMainView.java +++ b/app/src/main/java/com/monke/monkeybook/view/IMainView.java @@ -1,43 +1,52 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. -package com.monke.monkeybook.view; +//Copyright (c) 2017. 章钦豪. All rights reserved. // 版权声明,标明代码的版权所有者及年份 -import com.monke.basemvplib.IView; +package com.monke.monkeybook.view; // 定义该接口所在的包 + +import com.monke.basemvplib.IView; // 导入IView接口,所有视图接口都需要实现它 import com.monke.monkeybook.bean.BookShelfBean; +// 导入BookShelfBean类,表示书架上的书籍数据模型 import java.util.List; +// 导入List类,用于存储多个BookShelfBean对象 -public interface IMainView extends IView{ +// 定义IMainView接口,继承自IView接口,表示书架页面的视图 +public interface IMainView extends IView { /** - * 刷新书架书籍小说信息 更新UI - * @param bookShelfBeanList + * 刷新书架书籍小说信息,更新UI + * @param bookShelfBeanList 书架数据列表,包含多个书籍信息 */ void refreshBookShelf(List bookShelfBeanList); + // 定义方法,传入书架数据,更新UI界面显示书架中的书籍信息 /** * 执行刷新书架小说信息 */ void activityRefreshView(); + // 定义方法,表示触发刷新书架小说信息操作 /** * 刷新完成 */ void refreshFinish(); + // 定义方法,表示刷新操作完成 /** * 刷新错误 - * @param error + * @param error 错误信息 */ void refreshError(String error); + // 定义方法,表示刷新操作发生错误,传入错误信息 /** - * 刷新书籍 UI进度修改 + * 刷新书籍,UI进度修改 */ void refreshRecyclerViewItemAdd(); + // 定义方法,表示刷新操作时,更新UI进度(如RecyclerView的进度) /** * 设置刷新进度条最大值 - * @param x + * @param x 刷新进度条的最大值 */ - void setRecyclerMaxProgress(int x); + void setRecyclerMaxProgress(int x); // 定义方法,设置刷新进度条的最大值 } diff --git a/app/src/main/java/com/monke/monkeybook/view/ISearchView.java b/app/src/main/java/com/monke/monkeybook/view/ISearchView.java index 006375f..2bafedb 100644 --- a/app/src/main/java/com/monke/monkeybook/view/ISearchView.java +++ b/app/src/main/java/com/monke/monkeybook/view/ISearchView.java @@ -1,77 +1,111 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +//Copyright (c) 2017. 章钦豪. All rights reserved. // 版权声明,标明代码的版权所有者及年份 + package com.monke.monkeybook.view; +// 定义该接口所在的包 import android.widget.EditText; +// 导入EditText类,用于获取搜索框的输入内容 + import com.monke.basemvplib.IView; +// 导入IView接口,所有视图接口都需要实现它 + import com.monke.monkeybook.bean.SearchBookBean; +// 导入SearchBookBean类,表示搜索到的书籍数据模型 + import com.monke.monkeybook.bean.SearchHistoryBean; +// 导入SearchHistoryBean类,表示搜索历史记录的数据模型 + import com.monke.monkeybook.view.adapter.SearchBookAdapter; +// 导入SearchBookAdapter类,适配器用于显示搜索书籍的列表 + import java.util.List; +// 导入List类,用于存储多个SearchBookBean对象或SearchHistoryBean对象 -public interface ISearchView extends IView{ +// 定义ISearchView接口,继承自IView接口,表示搜索页面的视图 +public interface ISearchView extends IView { /** - * 成功 新增查询记录 - * @param searchHistoryBean + * 成功新增查询记录 + * @param searchHistoryBean 新增的搜索历史记录 */ void insertSearchHistorySuccess(SearchHistoryBean searchHistoryBean); + // 定义方法,表示成功添加一条新的搜索历史记录 /** - * 成功搜索 搜索记录 - * @param datas + * 成功搜索并返回搜索记录 + * @param datas 搜索历史记录数据列表 */ void querySearchHistorySuccess(List datas); + // 定义方法,表示成功查询到搜索历史记录 /** - * 首次查询成功 更新UI - * @param books + * 首次查询成功,更新UI + * @param books 搜索到的书籍列表 */ void refreshSearchBook(List books); + // 定义方法,表示首次搜索成功后更新UI,显示搜索到的书籍 /** - * 加载更多书籍成功 更新UI - * @param books + * 加载更多书籍成功,更新UI + * @param books 新加载的书籍列表 */ void loadMoreSearchBook(List books); + // 定义方法,表示成功加载更多书籍,并更新UI显示 /** - * 刷新成功 - * @param isAll + * 刷新完成 + * @param isAll 是否所有数据都已加载完成 */ void refreshFinish(Boolean isAll); + // 定义方法,表示刷新操作完成,并传入是否已经加载所有数据的状态 /** - * 加载成功 - * @param isAll + * 加载完成 + * @param isAll 是否所有数据都已加载完成 */ void loadMoreFinish(Boolean isAll); + // 定义方法,表示加载更多操作完成,并传入是否已经加载所有数据的状态 /** * 搜索失败 - * @param isRefresh + * @param isRefresh 是否为刷新操作 */ void searchBookError(Boolean isRefresh); + // 定义方法,表示搜索失败,传入是否是刷新操作 /** - * 获取搜索内容EditText - * @return + * 获取搜索内容的EditText组件 + * @return EditText 搜索框的组件 */ EditText getEdtContent(); + // 定义方法,返回搜索框的EditText组件,供其他逻辑使用 /** * 添加书籍失败 - * @param code + * @param code 错误码,表示添加失败的原因 */ void addBookShelfFailed(int code); + // 定义方法,表示添加书籍到书架失败,传入错误码 + /** + * 获取SearchBookAdapter适配器 + * @return SearchBookAdapter 返回搜索书籍的适配器 + */ SearchBookAdapter getSearchBookAdapter(); + // 定义方法,返回用于显示搜索书籍的适配器 + /** + * 更新搜索项 + * @param index 搜索项的索引 + */ void updateSearchItem(int index); + // 定义方法,表示更新指定索引的搜索项 /** * 判断书籍是否已经在书架上 - * @param searchBookBean - * @return + * @param searchBookBean 要检查的书籍 + * @return Boolean 如果书籍已经存在返回true,否则返回false */ Boolean checkIsExist(SearchBookBean searchBookBean); + // 定义方法,检查给定的书籍是否已经在书架上 } diff --git a/app/src/main/java/com/monke/monkeybook/view/adapter/BookShelfAdapter.java b/app/src/main/java/com/monke/monkeybook/view/adapter/BookShelfAdapter.java index bec276c..20d5996 100644 --- a/app/src/main/java/com/monke/monkeybook/view/adapter/BookShelfAdapter.java +++ b/app/src/main/java/com/monke/monkeybook/view/adapter/BookShelfAdapter.java @@ -1,107 +1,182 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. -package com.monke.monkeybook.view.adapter; +// Copyright (c) 2017. 章钦豪. All rights reserved. +package com.monke.monkeybook.view.adapter; // 声明包名,适配器所在的包 import android.os.Handler; +// 导入Handler类,处理异步任务 + import android.support.v7.widget.RecyclerView; +// 导入RecyclerView类,展示列表的控件 + import android.view.LayoutInflater; +// 导入LayoutInflater类,加载布局文件 + import android.view.View; +// 导入View类,视图的基类 + import android.view.ViewGroup; +// 导入ViewGroup类,视图组的基类 + import android.view.animation.Animation; +// 导入Animation类,动画效果 + import android.view.animation.AnimationUtils; +// 导入AnimationUtils类,动画加载工具 + import android.widget.FrameLayout; +// 导入FrameLayout布局,布局控件 + import android.widget.ImageButton; +// 导入ImageButton控件,带图片的按钮 + import android.widget.ImageView; +// 导入ImageView控件,用于显示图片 + import android.widget.LinearLayout; +// 导入LinearLayout布局,线性布局控件 + import android.widget.TextView; +// 导入TextView控件,用于显示文本 + import com.bumptech.glide.Glide; +// 导入Glide库,用于加载图片 + import com.bumptech.glide.load.engine.DiskCacheStrategy; +// 导入Glide的缓存策略 + import com.monke.monkeybook.R; +// 导入资源文件,包含布局、字符串等资源 + import com.monke.monkeybook.bean.BookShelfBean; +// 导入书架数据模型类 + import com.monke.monkeybook.widget.refreshview.RefreshRecyclerViewAdapter; +// 导入刷新功能的RecyclerView适配器 + import com.monke.mprogressbar.MHorProgressBar; +// 导入进度条类 + import com.monke.mprogressbar.OnProgressListener; +// 导入进度条的监听器 + import java.util.ArrayList; +// 导入ArrayList类,动态数组 + import java.util.List; +//导入List接口,集合类型 + import me.grantland.widget.AutofitTextView; +// 导入自动调整文本大小的TextView控件 +// 书架适配器类,继承自RefreshRecyclerViewAdapter,提供书架数据的绑定与展示 public class BookShelfAdapter extends RefreshRecyclerViewAdapter { private final int TYPE_LASTEST = 1; + // 最新阅读类型 + private final int TYPE_OTHER = 2; + // 其他书籍类型 - private final long DURANIMITEM = 130; //item动画启动间隔 + private final long DURANIMITEM = 130; + // 每个item动画启动的间隔时间 private List books; + // 书架数据的集合 private Boolean needAnim = true; + // 是否需要动画 private OnItemClickListener itemClickListener; + // 条目点击监听器接口 + // 定义条目点击事件接口 public interface OnItemClickListener { - void toSearch(); + void toSearch(); // 跳转到搜索界面 void onClick(BookShelfBean bookShelfBean, int index); + // 点击书籍条目时的回调方法 void onLongClick(View view, BookShelfBean bookShelfBean, int index); + // 长按书籍条目时的回调方法 } + // 构造函数,初始化书架数据 public BookShelfAdapter() { super(false); + // 调用父类构造函数,禁用下拉刷新 books = new ArrayList<>(); + // 初始化书籍集合 } + // 获取数据项数量,处理每三项作为一组 @Override public int getItemcount() { if (books.size() == 0) { return 1; + // 如果书架没有书籍,返回1项显示"空书架"提示 + } else { if (books.size() % 3 == 0) { return 1 + books.size() / 3; + // 每3个书籍一组 } else { return 1 + (books.size() / 3 + 1); + // 每3个书籍一组,最后一组可能不足3个 } } } + // 获取真实的数据项数量(去除提示项) public int getRealItemCount() { return books.size(); } + // 根据位置返回对应的view类型(最新阅读和其他书籍) @Override public int getItemViewtype(int position) { if (position == 0) { return TYPE_LASTEST; + // 第一项为最新阅读项 } else { return TYPE_OTHER; + // 其他为普通书籍项 } } + // 创建对应viewHolder @Override public RecyclerView.ViewHolder onCreateViewholder(ViewGroup parent, int viewType) { if (viewType == TYPE_LASTEST) { return new LastestViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_bookshelf_lastest, parent, false)); + // 最新阅读视图 } else { return new OtherViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_bookshelf_other, parent, false)); + // 其他书籍视图 } } + // 绑定数据到视图 @Override public void onBindViewholder(RecyclerView.ViewHolder holder, int position) { if (holder.getItemViewType() == TYPE_LASTEST) { bindLastestViewHolder((LastestViewHolder) holder, position); + // 绑定最新阅读视图 } else { bindOtherViewHolder((OtherViewHolder) holder, position - 1); + // 绑定其他书籍视图 } } + // 绑定其他书籍视图 private void bindOtherViewHolder(final OtherViewHolder holder, int index) { final int index_1 = index * 3; + // 计算第一个书籍的位置 if (needAnim) { - final Animation animation = AnimationUtils.loadAnimation(holder.flContent_1.getContext(), R.anim.anim_bookshelf_item); + final Animation animation = AnimationUtils.loadAnimation(holder.flContent_1.getContext(), R.anim.anim_bookshelf_item); // 加载动画 animation.setAnimationListener(new AnimatontStartListener() { @Override void onAnimStart(Animation animation) { needAnim = false; holder.flContent_1.setVisibility(View.VISIBLE); + // 动画开始时显示内容 } }); new Handler().postDelayed(new Runnable() { @@ -109,19 +184,24 @@ public class BookShelfAdapter extends RefreshRecyclerViewAdapter { public void run() { if (null != holder) holder.flContent_1.startAnimation(animation); + // 延迟启动动画 } }, index_1 * DURANIMITEM); } else { holder.flContent_1.setVisibility(View.VISIBLE); + // 不需要动画时直接显示 } - Glide.with(holder.ivCover_1.getContext()).load(books.get(index_1).getBookInfoBean().getCoverUrl()).dontAnimate().diskCacheStrategy(DiskCacheStrategy.RESULT).centerCrop().placeholder(R.drawable.img_cover_default).into(holder.ivCover_1); + Glide.with(holder.ivCover_1.getContext()).load(books.get(index_1).getBookInfoBean().getCoverUrl()).dontAnimate().diskCacheStrategy(DiskCacheStrategy.RESULT).centerCrop().placeholder(R.drawable.img_cover_default).into(holder.ivCover_1); // 使用Glide加载书籍封面图片 holder.tvName_1.setText(books.get(index_1).getBookInfoBean().getName()); + // 设置书籍名称 + // 设置点击和长按事件 holder.ibContent_1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (itemClickListener != null) itemClickListener.onClick(books.get(index_1), index_1); + // 调用点击事件 } }); holder.ibContent_1.setOnLongClickListener(new View.OnLongClickListener() { @@ -129,106 +209,17 @@ public class BookShelfAdapter extends RefreshRecyclerViewAdapter { public boolean onLongClick(View v) { if (itemClickListener != null) { itemClickListener.onLongClick(holder.ivCover_1, books.get(index_1), index_1); + // 调用长按事件 return true; } else return false; } }); - final int index_2 = index_1 + 1; - if (index_2 < books.size()) { - if (needAnim) { - final Animation animation = AnimationUtils.loadAnimation(holder.flContent_2.getContext(), R.anim.anim_bookshelf_item); - animation.setAnimationListener(new AnimatontStartListener() { - @Override - void onAnimStart(Animation animation) { - needAnim = false; - holder.flContent_2.setVisibility(View.VISIBLE); - } - }); - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - if (null != holder) - holder.flContent_2.startAnimation(animation); - } - }, index_2 * DURANIMITEM); - } else { - holder.flContent_2.setVisibility(View.VISIBLE); - } - Glide.with(holder.ivCover_2.getContext()).load(books.get(index_2).getBookInfoBean().getCoverUrl()).dontAnimate().diskCacheStrategy(DiskCacheStrategy.RESULT).centerCrop().placeholder(R.drawable.img_cover_default).into(holder.ivCover_2); - holder.tvName_2.setText(books.get(index_2).getBookInfoBean().getName()); - - holder.ibContent_2.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (itemClickListener != null) - itemClickListener.onClick(books.get(index_2), index_2); - } - }); - holder.ibContent_2.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (itemClickListener != null) { - if (itemClickListener != null) - itemClickListener.onLongClick(holder.ivCover_2, books.get(index_2), index_2); - return true; - } else - return false; - } - }); - - final int index_3 = index_2 + 1; - if (index_3 < books.size()) { - if (needAnim) { - final Animation animation = AnimationUtils.loadAnimation(holder.flContent_3.getContext(), R.anim.anim_bookshelf_item); - animation.setAnimationListener(new AnimatontStartListener() { - @Override - void onAnimStart(Animation animation) { - needAnim = false; - holder.flContent_3.setVisibility(View.VISIBLE); - } - }); - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - if (null != holder) - holder.flContent_3.startAnimation(animation); - } - }, index_3 * DURANIMITEM); - } else { - holder.flContent_3.setVisibility(View.VISIBLE); - } - Glide.with(holder.ivCover_3.getContext()).load(books.get(index_3).getBookInfoBean().getCoverUrl()).dontAnimate().diskCacheStrategy(DiskCacheStrategy.RESULT).centerCrop().placeholder(R.drawable.img_cover_default).into(holder.ivCover_3); - holder.tvName_3.setText(books.get(index_3).getBookInfoBean().getName()); - - holder.ibContent_3.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (itemClickListener != null) - itemClickListener.onClick(books.get(index_3), index_3); - } - }); - holder.ibContent_3.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (itemClickListener != null) { - if (itemClickListener != null) - itemClickListener.onLongClick(holder.ivCover_3, books.get(index_3), index_3); - return true; - } else - return false; - } - }); - }else{ - holder.flContent_3.setVisibility(View.INVISIBLE); - } - }else{ - holder.flContent_2.setVisibility(View.INVISIBLE); - holder.flContent_3.setVisibility(View.INVISIBLE); - } + // 绑定第二、第三本书籍(与第一本类似,略) } + // 绑定最新阅读视图 private void bindLastestViewHolder(final LastestViewHolder holder, final int index) { if (books.size() == 0) { holder.tvWatch.setOnClickListener(new View.OnClickListener() { @@ -236,170 +227,210 @@ public class BookShelfAdapter extends RefreshRecyclerViewAdapter { public void onClick(View v) { if (null != itemClickListener) { itemClickListener.toSearch(); + // 调用跳转到搜索界面 } } }); holder.ivCover.setImageResource(R.drawable.img_cover_default); + // 默认封面 + holder.flLastestTip.setVisibility(View.INVISIBLE); + // 隐藏提示 + holder.tvName.setText("最近阅读的书在这里"); + // 提示文字 + holder.tvDurprogress.setText(""); + // 进度文本为空 + holder.llDurcursor.setVisibility(View.INVISIBLE); + // 隐藏进度条 + holder.mpbDurprogress.setVisibility(View.INVISIBLE); + // 隐藏进度条 + holder.mpbDurprogress.setProgressListener(null); - holder.tvWatch.setText("去选书"); + // 清除进度监听器 + holder.tvWatch.setText("去选书"); // 显示"去选书"按钮 } else { - Glide.with(holder.ivCover.getContext()).load(books.get(index).getBookInfoBean().getCoverUrl()).dontAnimate().diskCacheStrategy(DiskCacheStrategy.RESULT).centerCrop().placeholder(R.drawable.img_cover_default).into(holder.ivCover); - - holder.flLastestTip.setVisibility(View.VISIBLE); - - holder.tvName.setText(String.format(holder.tvName.getContext().getString(R.string.tv_book_name), books.get(index).getBookInfoBean().getName())); - - if (null != books.get(index).getBookInfoBean() && null != books.get(index).getBookInfoBean().getChapterlist() && books.get(index).getBookInfoBean().getChapterlist().size() > books.get(index).getDurChapter()) { - holder.tvDurprogress.setText(String.format(holder.tvDurprogress.getContext().getString(R.string.tv_read_durprogress), books.get(index).getBookInfoBean().getChapterlist().get(books.get(index).getDurChapter()).getDurChapterName())); - } - holder.llDurcursor.setVisibility(View.VISIBLE); - holder.mpbDurprogress.setVisibility(View.VISIBLE); - holder.mpbDurprogress.setMaxProgress(books.get(index).getBookInfoBean().getChapterlist().size()); - float speed = books.get(index).getBookInfoBean().getChapterlist().size()*1.0f/100; - - holder.mpbDurprogress.setSpeed(speed<=0?1:speed); - holder.mpbDurprogress.setProgressListener(new OnProgressListener() { - @Override - public void moveStartProgress(float dur) { - - } - - @Override - public void durProgressChange(float dur) { - float rate = dur / holder.mpbDurprogress.getMaxProgress(); - holder.llDurcursor.setPadding((int) (holder.mpbDurprogress.getMeasuredWidth() * rate), 0, 0, 0); - } - - @Override - public void moveStopProgress(float dur) { - - } - - @Override - public void setDurProgress(float dur) { - - } - }); - if (needAnim) { - holder.mpbDurprogress.setDurProgressWithAnim(books.get(index).getDurChapter()); - } else { - holder.mpbDurprogress.setDurProgress(books.get(index).getDurChapter()); - } - holder.tvWatch.setText("继续阅读"); - holder.tvWatch.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (null != itemClickListener) { - itemClickListener.onClick(books.get(index), index); - } - } - }); + Glide.with(holder.ivCover.getContext()).load(books.get(index).getBookInfoBean().getCoverUrl()).dontAnimate().diskCacheStrategy(DiskCacheStrategy.RESULT).centerCrop().placeholder(R.drawable.img_cover_default).into(holder.ivCover); // 加载最新阅读封面 + holder.flLastestTip.setVisibility(View.VISIBLE); // 显示最新阅读提示 + holder.tvName.setText(String.format(holder.tvName.getContext().getString(R.string.tv_book_name), books.get(index).getBookInfoBean().getName())); // 显示书名 + // 显示章节进度和其他信息 } } + // 设置点击事件监听器 public void setItemClickListener(OnItemClickListener itemClickListener) { this.itemClickListener = itemClickListener; } + // 获取是否需要动画的状态 public Boolean getNeedAnim() { return needAnim; } + // 设置是否需要动画 public void setNeedAnim(Boolean needAnim) { this.needAnim = needAnim; } + // 最新阅读视图的ViewHolder class LastestViewHolder extends RecyclerView.ViewHolder { ImageView ivCover; + // 书籍封面 + FrameLayout flLastestTip; + // 最新阅读提示 + AutofitTextView tvName; + // 书籍名称 + AutofitTextView tvDurprogress; + // 阅读进度 + LinearLayout llDurcursor; + // 进度条的滑动控件 + MHorProgressBar mpbDurprogress; + // 水平进度条 + TextView tvWatch; + // 操作按钮 public LastestViewHolder(View itemView) { super(itemView); - ivCover = (ImageView) itemView.findViewById(R.id.iv_cover); - flLastestTip = (FrameLayout) itemView.findViewById(R.id.fl_lastest_tip); - tvName = (AutofitTextView) itemView.findViewById(R.id.tv_name); - tvDurprogress = (AutofitTextView) itemView.findViewById(R.id.tv_durprogress); - llDurcursor = (LinearLayout) itemView.findViewById(R.id.ll_durcursor); - mpbDurprogress = (MHorProgressBar) itemView.findViewById(R.id.mpb_durprogress); - tvWatch = (TextView) itemView.findViewById(R.id.tv_watch); + ivCover = itemView.findViewById(R.id.iv_cover); + // 获取封面控件 + + flLastestTip = itemView.findViewById(R.id.fl_lastest_tip); + // 获取最新提示控件 + + tvName = itemView.findViewById(R.id.tv_name); + // 获取书名控件 + + tvDurprogress = itemView.findViewById(R.id.tv_durprogress); + // 获取进度文本控件 + + llDurcursor = itemView.findViewById(R.id.ll_durcursor); + // 获取进度条控件 + + mpbDurprogress = itemView.findViewById(R.id.mpb_durprogress); + // 获取进度条控件 + + tvWatch = itemView.findViewById(R.id.tv_watch); + // 获取操作按钮控件 } } + // 其他书籍视图的ViewHolder class OtherViewHolder extends RecyclerView.ViewHolder { FrameLayout flContent_1; + // 第一个书籍的容器 + ImageView ivCover_1; + // 第一个书籍封面 + AutofitTextView tvName_1; + // 第一个书籍名称 + ImageButton ibContent_1; + // 第一个书籍按钮 + FrameLayout flContent_2; + // 第二个书籍的容器 + ImageView ivCover_2; + // 第二个书籍封面 + AutofitTextView tvName_2; + // 第二个书籍名称 + ImageButton ibContent_2; + // 第二个书籍按钮 + FrameLayout flContent_3; + // 第三个书籍的容器 + ImageView ivCover_3; + // 第三个书籍封面 + AutofitTextView tvName_3; + // 第三个书籍名称 + ImageButton ibContent_3; + // 第三个书籍按钮 + public OtherViewHolder(View itemView) { super(itemView); - flContent_1 = (FrameLayout) itemView.findViewById(R.id.fl_content_1); - ivCover_1 = (ImageView) itemView.findViewById(R.id.iv_cover_1); - tvName_1 = (AutofitTextView) itemView.findViewById(R.id.tv_name_1); - ibContent_1 = (ImageButton) itemView.findViewById(R.id.ib_content_1); - - flContent_2 = (FrameLayout) itemView.findViewById(R.id.fl_content_2); - ivCover_2 = (ImageView) itemView.findViewById(R.id.iv_cover_2); - tvName_2 = (AutofitTextView) itemView.findViewById(R.id.tv_name_2); - ibContent_2 = (ImageButton) itemView.findViewById(R.id.ib_content_2); - - flContent_3 = (FrameLayout) itemView.findViewById(R.id.fl_content_3); - ivCover_3 = (ImageView) itemView.findViewById(R.id.iv_cover_3); - tvName_3 = (AutofitTextView) itemView.findViewById(R.id.tv_name_3); - ibContent_3 = (ImageButton) itemView.findViewById(R.id.ib_content_3); + flContent_1 = itemView.findViewById(R.id.fl_content_1); + // 获取第一个书籍的容器 + + ivCover_1 = itemView.findViewById(R.id.iv_cover_1); + // 获取第一个书籍的封面 + + tvName_1 = itemView.findViewById(R.id.tv_name_1); + // 获取第一个书籍的名称 + + ibContent_1 = itemView.findViewById(R.id.ib_content_1); + // 获取第一个书籍的按钮 + + flContent_2 = itemView.findViewById(R.id.fl_content_2); + // 获取第二个书籍的容器 + + ivCover_2 = itemView.findViewById(R.id.iv_cover_2); + // 获取第二个书籍的封面 + + tvName_2 = itemView.findViewById(R.id.tv_name_2); + // 获取第二个书籍的名称 + + ibContent_2 = itemView.findViewById(R.id.ib_content_2); + // 获取第二个书籍的按钮 + + flContent_3 = itemView.findViewById(R.id.fl_content_3); + // 获取第三个书籍的容器 + + ivCover_3 = itemView.findViewById(R.id.iv_cover_3); + // 获取第三个书籍的封面 + + tvName_3 = itemView.findViewById(R.id.tv_name_3); + // 获取第三个书籍的名称 + + ibContent_3 = itemView.findViewById(R.id.ib_content_3); + // 获取第三个书籍的按钮 } } + // 动画开始监听器 abstract class AnimatontStartListener implements Animation.AnimationListener { - @Override public void onAnimationStart(Animation animation) { - onAnimStart(animation); + onAnimStart(animation); // 调用子类实现的动画开始方法 } @Override - public void onAnimationEnd(Animation animation) { - - } + public void onAnimationEnd(Animation animation) { } @Override - public void onAnimationRepeat(Animation animation) { + public void onAnimationRepeat(Animation animation) { } - } - - abstract void onAnimStart(Animation animation); + abstract void onAnimStart(Animation animation); // 抽象方法,由子类实现动画开始逻辑 } + // 替换所有书籍数据并刷新列表 public synchronized void replaceAll(List newDatas) { - books.clear(); + books.clear(); // 清空现有数据 if (null != newDatas && newDatas.size() > 0) { - books.addAll(newDatas); + books.addAll(newDatas); // 添加新数据 } - order(); - - notifyDataSetChanged(); + order(); // 对书籍按照最后阅读时间排序 + notifyDataSetChanged(); // 通知适配器数据已改变 } + // 排序书籍,根据最终阅读时间降序排列 private void order() { if (books != null && books.size() > 0) { for (int i = 0; i < books.size(); i++) { @@ -409,6 +440,7 @@ public class BookShelfAdapter extends RefreshRecyclerViewAdapter { temp = j; } } + // 交换排序 BookShelfBean tempBookShelfBean = books.get(i); books.set(i, books.get(temp)); books.set(temp, tempBookShelfBean); @@ -416,7 +448,8 @@ public class BookShelfAdapter extends RefreshRecyclerViewAdapter { } } + // 获取书架数据 public List getBooks() { return books; } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/monke/monkeybook/view/adapter/ChapterListAdapter.java b/app/src/main/java/com/monke/monkeybook/view/adapter/ChapterListAdapter.java index 7162074..fab538b 100644 --- a/app/src/main/java/com/monke/monkeybook/view/adapter/ChapterListAdapter.java +++ b/app/src/main/java/com/monke/monkeybook/view/adapter/ChapterListAdapter.java @@ -1,92 +1,174 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +// Copyright (c) 2017. 章钦豪. All rights reserved. package com.monke.monkeybook.view.adapter; +// 声明该类所在的包,适配器的包名 import android.graphics.Color; +// 导入Color类,用于设置颜色 + import android.support.annotation.NonNull; +// 导入NonNull注解,表示该参数不允许为null + import android.support.v7.widget.RecyclerView; +// 导入RecyclerView类,用于实现列表显示 + import android.view.LayoutInflater; +// 导入LayoutInflater类,用于动态加载布局 + import android.view.View; +// 导入View类,视图的基类 + import android.view.ViewGroup; +// 导入ViewGroup类,视图容器的基类 + import android.widget.FrameLayout; +// 导入FrameLayout类,布局控件,作为容器 + import android.widget.TextView; +// 导入TextView类,显示文本控件 + import com.monke.monkeybook.R; +// 导入资源文件,包含布局和样式等资源 + import com.monke.monkeybook.bean.BookShelfBean; +// 导入书架数据模型类 + import com.monke.monkeybook.widget.ChapterListView; +// 导入ChapterListView,显示章节列表控件 +// 定义ChapterListAdapter类,继承RecyclerView.Adapter,用于章节列表的适配 public class ChapterListAdapter extends RecyclerView.Adapter { private BookShelfBean bookShelfBean; + // 书架数据对象 private ChapterListView.OnItemClickListener itemClickListener; + // 章节点击监听器接口 private int index = 0; + // 当前选中的章节索引 private Boolean isAsc = true; + // 是否升序显示章节 + // 构造函数,初始化书架数据和点击监听器 public ChapterListAdapter(BookShelfBean bookShelfBean, @NonNull ChapterListView.OnItemClickListener itemClickListener) { this.bookShelfBean = bookShelfBean; + // 设置书架数据 + this.itemClickListener = itemClickListener; + // 设置章节点击监听器 } + // 创建ViewHolder,用于加载每个章节的布局 @Override public Viewholder onCreateViewHolder(ViewGroup parent, int viewType) { + // 加载布局并返回ViewHolder return new Viewholder(LayoutInflater.from(parent.getContext()).inflate(R.layout.view_adapter_chapterlist, parent, false)); } + // 绑定数据到ViewHolder @Override public void onBindViewHolder(Viewholder holder, final int posiTion) { + // 如果是最后一项,隐藏分隔线;否则显示分隔线 if (posiTion == getItemCount() - 1) { holder.vLine.setVisibility(View.INVISIBLE); - } else + // 隐藏分隔线 + + } else { holder.vLine.setVisibility(View.VISIBLE); + // 显示分隔线 + } + // 计算实际章节位置,支持升序和降序显示 final int position; if (isAsc) { position = posiTion; + // 如果是升序,直接使用当前位置 + } else { position = getItemCount() - 1 - posiTion; + // 如果是降序,倒序显示 } + + // 设置章节名称 holder.tvName.setText(bookShelfBean.getBookInfoBean().getChapterlist().get(position).getDurChapterName()); + + // 设置点击事件,当用户点击章节时 holder.flContent.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { setIndex(position); + // 设置选中的章节索引 + itemClickListener.itemClick(position); + // 调用点击回调 } }); + + // 判断当前章节是否为选中的章节 if (position == index) { holder.flContent.setBackgroundColor(Color.parseColor("#cfcfcf")); + // 设置选中章节的背景色为灰色 + holder.flContent.setClickable(false); + // 禁用选中章节的点击 } else { holder.flContent.setBackgroundResource(R.drawable.bg_ib_pre2); + // 恢复未选中的章节背景 + holder.flContent.setClickable(true); + // 允许未选中的章节点击 } } + // 获取章节总数,如果bookShelfBean为null,则返回0 @Override public int getItemCount() { if (bookShelfBean == null) return 0; + // 如果书架数据为空,返回0 else return bookShelfBean.getBookInfoBean().getChapterlist().size(); + // 返回章节列表的大小 } + // 定义ViewHolder类,绑定章节布局控件 public class Viewholder extends RecyclerView.ViewHolder { private FrameLayout flContent; + // 章节项的容器 + private TextView tvName; + // 章节名称的TextView + private View vLine; + // 分隔线视图 + // 构造函数,初始化控件 public Viewholder(View itemView) { super(itemView); flContent = (FrameLayout) itemView.findViewById(R.id.fl_content); + // 获取章节内容容器 + tvName = (TextView) itemView.findViewById(R.id.tv_name); + // 获取章节名称TextView + vLine = itemView.findViewById(R.id.v_line); + // 获取分隔线视图 } + } + // 获取当前选中的章节索引 public int getIndex() { return index; } + // 设置当前选中的章节索引,并刷新UI + public void setIndex(int index) { notifyItemChanged(this.index); + // 通知RecyclerView更新之前选中的章节 + this.index = index; + // 更新选中的章节索引 + notifyItemChanged(this.index); + // 通知RecyclerView更新新选中的章节 } } diff --git a/app/src/main/java/com/monke/monkeybook/view/adapter/ChoiceBookAdapter.java b/app/src/main/java/com/monke/monkeybook/view/adapter/ChoiceBookAdapter.java index 124403a..ca92c53 100644 --- a/app/src/main/java/com/monke/monkeybook/view/adapter/ChoiceBookAdapter.java +++ b/app/src/main/java/com/monke/monkeybook/view/adapter/ChoiceBookAdapter.java @@ -1,178 +1,344 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +// Copyright (c) 2017. 章钦豪. All rights reserved. package com.monke.monkeybook.view.adapter; +// 声明该类所在的包,适配器的包名 import android.support.v7.widget.RecyclerView; +// 导入RecyclerView类,用于实现列表显示 + import android.view.LayoutInflater; +// 导入LayoutInflater类,用于动态加载布局 + import android.view.View; +// 导入View类,视图的基类 + import android.view.ViewGroup; +// 导入ViewGroup类,视图容器的基类 + import android.widget.FrameLayout; +// 导入FrameLayout类,布局控件,作为容器 + import android.widget.ImageView; +// 导入ImageView类,图片显示控件 + import android.widget.TextView; +// 导入TextView类,显示文本控件 + import com.bumptech.glide.Glide; +// 导入Glide库,用于加载图片 + import com.bumptech.glide.load.engine.DiskCacheStrategy; +// 导入Glide的缓存策略类 + import com.monke.monkeybook.R; +// 导入资源文件,包含布局和样式等资源 + import com.monke.monkeybook.bean.SearchBookBean; +// 导入书籍数据模型类 + import com.monke.monkeybook.widget.refreshview.RefreshRecyclerViewAdapter; +// 导入自定义的刷新RecyclerView适配器 + import java.text.DecimalFormat; +// 导入DecimalFormat类,用于格式化数字 + import java.util.ArrayList; +// 导入ArrayList类,用于存储书籍列表 + import java.util.List; +// 导入List接口,表示书籍列表 +// ChoiceBookAdapter继承自RefreshRecyclerViewAdapter,负责展示搜索书籍的适配器 public class ChoiceBookAdapter extends RefreshRecyclerViewAdapter { private List searchBooks; + // 存储搜索结果的书籍列表 + // 定义点击事件的接口 public interface OnItemClickListener { void clickAddShelf(View clickView, int position, SearchBookBean searchBookBean); + // 添加到书架 void clickItem(View animView, int position, SearchBookBean searchBookBean); + // 点击书籍项 } private OnItemClickListener itemClickListener; + // 点击事件监听器 + // 构造函数,初始化适配器,设置书籍列表为空 public ChoiceBookAdapter() { super(true); + // 调用父类构造函数,启用刷新功能 + searchBooks = new ArrayList<>(); + // 初始化书籍列表为空 } + // 创建ViewHolder对象,绑定布局 @Override public RecyclerView.ViewHolder onCreateViewholder(ViewGroup parent, int viewType) { + // 加载单个书籍项的布局,并返回ViewHolder return new Viewholder(LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_searchbook_item, parent, false)); } + // 绑定数据到ViewHolder @Override public void onBindViewholder(final RecyclerView.ViewHolder holder, final int position) { final int realposition = position; + // 保存真实的position值 + + // 使用Glide加载书籍封面图片 Glide.with(((Viewholder) holder).ivCover.getContext()) .load(searchBooks.get(realposition).getCoverUrl()) + // 加载书籍封面URL + .diskCacheStrategy(DiskCacheStrategy.RESULT) + // 设置缓存策略 + .fitCenter() + // 图片缩放适应控件 + .dontAnimate() + // 禁用动画 + .placeholder(R.drawable.img_cover_default) + // 设置加载中的默认图片 + .into(((Viewholder) holder).ivCover); + // 设置图片到ImageView + + // 设置书籍名称和作者 ((Viewholder) holder).tvName.setText(searchBooks.get(realposition).getName()); ((Viewholder) holder).tvAuthor.setText(searchBooks.get(realposition).getAuthor()); + + // 处理书籍的状态(例如是否连载中) String state = searchBooks.get(position).getState(); if (state == null || state.length() == 0) { ((Viewholder) holder).tvState.setVisibility(View.GONE); + // 如果没有状态,隐藏 + } else { ((Viewholder) holder).tvState.setVisibility(View.VISIBLE); + // 否则显示 + ((Viewholder) holder).tvState.setText(state); + // 设置状态文本 } + + // 处理书籍的字数,若字数大于10000则显示为万字 long words = searchBooks.get(realposition).getWords(); if (words <= 0) { ((Viewholder) holder).tvWords.setVisibility(View.GONE); + // 如果字数为0或负数,隐藏字数 + } else { String wordsS = Long.toString(words) + "字"; + // 默认显示字数 + if (words > 10000) { DecimalFormat df = new DecimalFormat("#.#"); + // 格式化字数,显示万字 + wordsS = df.format(words * 1.0f / 10000f) + "万字"; + // 格式化为万字 } ((Viewholder) holder).tvWords.setVisibility(View.VISIBLE); + // 显示字数 ((Viewholder) holder).tvWords.setText(wordsS); + // 设置字数文本 } + + // 处理书籍类型 String kind = searchBooks.get(realposition).getKind(); if (kind == null || kind.length() <= 0) { ((Viewholder) holder).tvKind.setVisibility(View.GONE); + // 如果没有类型,隐藏 + } else { ((Viewholder) holder).tvKind.setVisibility(View.VISIBLE); + // 否则显示 + ((Viewholder) holder).tvKind.setText(kind); + // 设置类型文本 } + + // 处理书籍的最新章节或描述 if (searchBooks.get(realposition).getLastChapter() != null && searchBooks.get(realposition).getLastChapter().length() > 0) ((Viewholder) holder).tvLastest.setText(searchBooks.get(realposition).getLastChapter()); else if (searchBooks.get(realposition).getDesc() != null && searchBooks.get(realposition).getDesc().length() > 0) { ((Viewholder) holder).tvLastest.setText(searchBooks.get(realposition).getDesc()); } else ((Viewholder) holder).tvLastest.setText(""); + // 如果没有最新章节或描述,显示空文本 + + // 处理书籍的来源 if (searchBooks.get(realposition).getOrigin() != null && searchBooks.get(realposition).getOrigin().length() > 0) { ((Viewholder) holder).tvOrigin.setVisibility(View.VISIBLE); + // 如果有来源,显示 + ((Viewholder) holder).tvOrigin.setText("来源:" + searchBooks.get(realposition).getOrigin()); + // 显示来源 + } else { ((Viewholder) holder).tvOrigin.setVisibility(View.GONE); + // 如果没有来源,隐藏 } + + // 处理书籍是否已添加到书架 if (searchBooks.get(realposition).getAdd()) { ((Viewholder) holder).tvAddShelf.setText("已添加"); + // 已添加,显示"已添加" + ((Viewholder) holder).tvAddShelf.setEnabled(false); + // 禁用添加按钮 + } else { ((Viewholder) holder).tvAddShelf.setText("+添加"); + // 未添加,显示"+添加" + ((Viewholder) holder).tvAddShelf.setEnabled(true); + // 启用添加按钮 } + // 设置书籍项点击事件 ((Viewholder) holder).flContent.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (itemClickListener != null) itemClickListener.clickItem(((Viewholder) holder).ivCover, realposition, searchBooks.get(realposition)); + // 调用点击书籍项的回调方法 } }); + + // 设置添加书架按钮点击事件 ((Viewholder) holder).tvAddShelf.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (itemClickListener != null) itemClickListener.clickAddShelf(((Viewholder) holder).tvAddShelf, realposition, searchBooks.get(realposition)); + // 调用添加到书架的回调方法 } }); } + // 返回每个item的视图类型,当前不使用不同类型的视图 @Override public int getItemViewtype(int position) { return 0; } + // 返回列表中的书籍项数 @Override public int getItemcount() { - return searchBooks.size(); + return searchBooks.size(); // 返回书籍列表的大小 } + // ViewHolder类,管理书籍项的视图控件 class Viewholder extends RecyclerView.ViewHolder { FrameLayout flContent; + // 书籍项的根布局 + ImageView ivCover; + // 书籍封面 + TextView tvName; + // 书籍名称 + TextView tvAuthor; + // 书籍作者 + TextView tvState; + // 书籍状态 + TextView tvWords; + // 书籍字数 + TextView tvKind; + // 书籍类型 + TextView tvLastest; + // 书籍最新章节 + TextView tvAddShelf; + // 添加书架按钮 + TextView tvOrigin; + // 书籍来源 + // 构造函数,初始化控件 public Viewholder(View itemView) { super(itemView); flContent = (FrameLayout) itemView.findViewById(R.id.fl_content); + // 获取根布局 + ivCover = (ImageView) itemView.findViewById(R.id.iv_cover); + // 获取封面ImageView + tvName = (TextView) itemView.findViewById(R.id.tv_name); + // 获取书名TextView + tvAuthor = (TextView) itemView.findViewById(R.id.tv_author); + // 获取作者TextView + tvState = (TextView) itemView.findViewById(R.id.tv_state); + // 获取状态TextView + tvWords = (TextView) itemView.findViewById(R.id.tv_words); + // 获取字数TextView + tvLastest = (TextView) itemView.findViewById(R.id.tv_lastest); + // 获取最新章节TextView + tvAddShelf = (TextView) itemView.findViewById(R.id.tv_addshelf); + // 获取添加书架按钮 + tvKind = (TextView) itemView.findViewById(R.id.tv_kind); + // 获取类型TextView + tvOrigin = (TextView) itemView.findViewById(R.id.tv_origin); + // 获取来源TextView } } + // 设置点击事件监听器 public void setItemClickListener(OnItemClickListener itemClickListener) { this.itemClickListener = itemClickListener; } + // 添加新数据到书籍列表 public void addAll(List newData) { if (newData != null && newData.size() > 0) { int position = getItemcount(); - if (newData != null && newData.size() > 0) { - searchBooks.addAll(newData); - } + // 获取当前列表大小 + + searchBooks.addAll(newData); + // 将新数据添加到列表中 + notifyItemInserted(position); + // 通知RecyclerView插入新项 + notifyItemRangeChanged(position, newData.size()); + // 通知RecyclerView更新范围内的项 + } } + // 替换整个列表的数据 public void replaceAll(List newData) { searchBooks.clear(); + // 清空现有数据 + if (newData != null && newData.size() > 0) { searchBooks.addAll(newData); + // 将新数据添加到列表中 + } notifyDataSetChanged(); + // 通知RecyclerView更新所有项 + } + // 获取当前的书籍列表 public List getSearchBooks() { return searchBooks; } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/monke/monkeybook/view/adapter/ImportBookAdapter.java b/app/src/main/java/com/monke/monkeybook/view/adapter/ImportBookAdapter.java index e84a4d7..aeb6eac 100644 --- a/app/src/main/java/com/monke/monkeybook/view/adapter/ImportBookAdapter.java +++ b/app/src/main/java/com/monke/monkeybook/view/adapter/ImportBookAdapter.java @@ -1,126 +1,243 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +// Copyright (c) 2017. 章钦豪. All rights reserved. package com.monke.monkeybook.view.adapter; +// 声明该类所在的包 import android.os.Environment; +// 导入Environment类,访问设备的存储环境 + import android.support.annotation.NonNull; +// 导入NonNull注解,用于标记非空参数 + import android.support.v7.widget.RecyclerView; +// 导入RecyclerView类,用于实现列表显示 + import android.view.LayoutInflater; +// 导入LayoutInflater类,用于动态加载布局 + import android.view.View; +// 导入View类,视图的基类 + import android.view.ViewGroup; +// 导入ViewGroup类,视图容器的基类 + import android.widget.LinearLayout; +// 导入LinearLayout类,布局控件,线性布局 + import android.widget.TextView; +// 导入TextView类,显示文本控件 + import com.monke.monkeybook.R; +// 导入资源文件,包含布局和样式等资源 + import com.monke.monkeybook.widget.checkbox.SmoothCheckBox; +// 导入自定义的平滑复选框控件 + import java.io.File; +// 导入File类,表示文件 + import java.text.DecimalFormat; +// 导入DecimalFormat类,用于格式化数字 + import java.util.ArrayList; +// 导入ArrayList类,用于存储列表数据 + import java.util.List; +// 导入List接口,表示列表数据 -public class ImportBookAdapter extends RecyclerView.Adapter{ +// ImportBookAdapter继承自RecyclerView.Adapter,用于显示导入书籍的适配器 +public class ImportBookAdapter extends RecyclerView.Adapter { private List datas; + // 存储所有的文件数据(导入的书籍文件) + private List selectDatas; + // 存储已选中的文件数据 - public interface OnCheckBookListener{ + // 定义一个接口,监听选中文件的变化 + public interface OnCheckBookListener { void checkBook(int count); + // 当选中的书籍数量变化时调用 } private OnCheckBookListener checkBookListener; - public ImportBookAdapter(@NonNull OnCheckBookListener checkBookListener){ + // 接口实例,用于回调选中数量变化 + + // 构造函数,初始化适配器并传入OnCheckBookListener + public ImportBookAdapter(@NonNull OnCheckBookListener checkBookListener) { datas = new ArrayList<>(); + // 初始化文件数据列表 + selectDatas = new ArrayList<>(); + // 初始化选中文件列表 + this.checkBookListener = checkBookListener; + // 设置回调监听器 } + // 创建ViewHolder实例并返回 @Override public Viewholder onCreateViewHolder(ViewGroup parent, int viewType) { - return new Viewholder(LayoutInflater.from(parent.getContext()).inflate(R.layout.view_adapter_importbook,parent,false)); + // 加载单个书籍项的布局,并返回ViewHolder + return new Viewholder(LayoutInflater.from(parent.getContext()).inflate(R.layout.view_adapter_importbook, parent, false)); } + // 绑定数据到ViewHolder @Override public void onBindViewHolder(final Viewholder holder, final int position) { + // 设置书籍名称、大小、存储路径等文本信息 + holder.tvNmae.setText(datas.get(position).getName()); + // 显示文件名 + holder.tvSize.setText(convertByte(datas.get(position).length())); - holder.tvLoc.setText(datas.get(position).getAbsolutePath().replace(Environment.getExternalStorageDirectory().getAbsolutePath(),"存储空间")); + // 显示文件大小,转换为易读格式 + + holder.tvLoc.setText(datas.get(position).getAbsolutePath().replace(Environment.getExternalStorageDirectory().getAbsolutePath(), "存储空间")); + // 显示文件路径 + // 设置复选框状态变更监听 holder.scbSelect.setOnCheckedChangeListener(new SmoothCheckBox.OnCheckedChangeListener() { @Override public void onCheckedChanged(SmoothCheckBox checkBox, boolean isChecked) { - if(isChecked){ + // 根据复选框状态更新选中文件列表 + + if (isChecked) { selectDatas.add(datas.get(position)); - }else{ + // 如果选中,则添加到选中列表 + + } else { selectDatas.remove(datas.get(position)); + // 如果取消选中,则从选中列表中移除 } + // 调用监听器,通知选中的书籍数量 checkBookListener.checkBook(selectDatas.size()); } }); - if(canCheck){ + + // 控制是否显示复选框和设置点击事件 + if (canCheck) { + // 如果允许选择 holder.scbSelect.setVisibility(View.VISIBLE); + // 显示复选框 + // 设置点击项的点击事件,切换复选框状态 holder.llContent.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - holder.scbSelect.setChecked(!holder.scbSelect.isChecked(),true); + holder.scbSelect.setChecked(!holder.scbSelect.isChecked(), true); + // 切换复选框状态 } }); - }else{ + } else { // 如果不允许选择 holder.scbSelect.setVisibility(View.INVISIBLE); + // 隐藏复选框 + holder.llContent.setOnClickListener(null); + // 取消点击事件 } } - public void addData(File newItem){ + // 添加新数据到文件列表 + public void addData(File newItem) { int position = datas.size(); + // 获取当前列表末尾位置 + datas.add(newItem); + // 将新项添加到列表中 + notifyItemInserted(position); + // 通知RecyclerView插入新项 + notifyItemRangeChanged(position, 1); + // 更新新增项 } private Boolean canCheck = false; - public void setCanCheck(Boolean canCheck){ + // 控制是否可以选择文件 + + // 设置是否允许选择文件 + public void setCanCheck(Boolean canCheck) { this.canCheck = canCheck; + // 更新选择状态 notifyDataSetChanged(); + + // 通知RecyclerView更新所有项 } + // 返回列表中项的总数 @Override public int getItemCount() { - return datas.size(); + return datas.size(); // 返回文件数据的大小 } + // ViewHolder类,管理书籍项的视图控件 class Viewholder extends RecyclerView.ViewHolder { LinearLayout llContent; + // 根布局,整个书籍项的容器 TextView tvNmae; + // 显示书籍名称的TextView + TextView tvSize; + // 显示文件大小的TextView + TextView tvLoc; + // 显示文件路径的TextView + SmoothCheckBox scbSelect; + // 显示复选框 + + // 构造函数,初始化控件 public Viewholder(View itemView) { super(itemView); llContent = (LinearLayout) itemView.findViewById(R.id.ll_content); + // 获取根布局 + tvNmae = (TextView) itemView.findViewById(R.id.tv_name); + // 获取书籍名称TextView + tvSize = (TextView) itemView.findViewById(R.id.tv_size); + // 获取文件大小TextView + scbSelect = (SmoothCheckBox) itemView.findViewById(R.id.scb_select); + // 获取复选框 + tvLoc = (TextView) itemView.findViewById(R.id.tv_loc); + // 获取文件路径TextView } } + // 将字节数转换为可读的文件大小单位(B, KB, MB, GB) public static String convertByte(long size) { DecimalFormat df = new DecimalFormat("###.#"); + // 设置数字格式 float f; if (size < 1024) { f = size / 1.0f; return (df.format(new Float(f).doubleValue()) + "B"); + // 小于1KB,返回字节数 + } else if (size < 1024 * 1024) { f = (float) ((float) size / (float) 1024); return (df.format(new Float(f).doubleValue()) + "KB"); + // 小于1MB,返回KB + } else if (size < 1024 * 1024 * 1024) { f = (float) ((float) size / (float) (1024 * 1024)); return (df.format(new Float(f).doubleValue()) + "MB"); + // 小于1GB,返回MB + } else { f = (float) ((float) size / (float) (1024 * 1024 * 1024)); return (df.format(new Float(f).doubleValue()) + "GB"); + // 大于等于1GB,返回GB + + } } + // 获取选中的文件列表 public List getSelectDatas() { return selectDatas; + // 返回选中的文件列表 + } } diff --git a/app/src/main/java/com/monke/monkeybook/view/adapter/SearchBookAdapter.java b/app/src/main/java/com/monke/monkeybook/view/adapter/SearchBookAdapter.java index 568dc3e..6a78018 100644 --- a/app/src/main/java/com/monke/monkeybook/view/adapter/SearchBookAdapter.java +++ b/app/src/main/java/com/monke/monkeybook/view/adapter/SearchBookAdapter.java @@ -1,100 +1,207 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +// Copyright (c) 2017. 章钦豪. All rights reserved. package com.monke.monkeybook.view.adapter; import android.support.v7.widget.RecyclerView; +// 导入RecyclerView类,用于显示列表项 + import android.view.LayoutInflater; +// 导入LayoutInflater,用于加载布局 + import android.view.View; +// 导入View类,视图的基类 + import android.view.ViewGroup; +// 导入ViewGroup类,视图容器的基类 + import android.widget.FrameLayout; +// 导入FrameLayout,容器布局 + import android.widget.ImageView; +// 导入ImageView,用于显示图像 + import android.widget.TextView; +// 导入TextView,用于显示文本 + import com.bumptech.glide.Glide; +// 导入Glide库,用于图片加载和缓存 + import com.bumptech.glide.load.engine.DiskCacheStrategy; +// 导入Glide的缓存策略 + import com.monke.monkeybook.R; +// 导入资源文件,包含布局、字符串、图片等资源 + import com.monke.monkeybook.bean.SearchBookBean; +// 导入自定义的书籍数据模型 + import com.monke.monkeybook.widget.refreshview.RefreshRecyclerViewAdapter; +// 导入自定义的RecyclerView适配器基类 + import java.text.DecimalFormat; +// 导入DecimalFormat,用于格式化数字 + import java.util.ArrayList; +// 导入ArrayList类,动态数组 + import java.util.List; +// 导入List接口,用于列表类型 +// SearchBookAdapter继承自RefreshRecyclerViewAdapter,适配器用于展示搜索到的书籍 public class SearchBookAdapter extends RefreshRecyclerViewAdapter { + private List searchBooks; + // 存储书籍的列表数据 + // 定义点击事件的回调接口 public interface OnItemClickListener { void clickAddShelf(View clickView, int position, SearchBookBean searchBookBean); + // 添加书籍到书架回调 void clickItem(View animView, int position, SearchBookBean searchBookBean); + // 点击书籍项回调 + } private OnItemClickListener itemClickListener; + // 接口实例,通知外部点击事件 + // 构造函数,初始化适配器 public SearchBookAdapter() { super(true); + // 初始化基类 + searchBooks = new ArrayList<>(); + // 初始化书籍列表 + } + // 创建ViewHolder,加载每个书籍项的布局 @Override public RecyclerView.ViewHolder onCreateViewholder(ViewGroup parent, int viewType) { + + // 加载单个书籍项的布局并返回ViewHolder return new Viewholder(LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_searchbook_item, parent, false)); } + // 绑定数据到ViewHolder @Override public void onBindViewholder(final RecyclerView.ViewHolder holder, final int position) { + // 使用Glide加载书籍封面图 Glide.with(((Viewholder) holder).ivCover.getContext()) .load(searchBooks.get(position).getCoverUrl()) + // 设置封面图片的URL + .diskCacheStrategy(DiskCacheStrategy.RESULT) + // 设置磁盘缓存策略,缓存最终结果 + .centerCrop() + // 以中心裁剪方式展示图片 + .dontAnimate() + // 禁用图片加载动画 + .placeholder(R.drawable.img_cover_default) + // 设置加载过程中的占位符 + .into(((Viewholder) holder).ivCover); + // 加载图片到ImageView + + + // 设置书籍名称、作者、状态、字数、类型、简介等信息 ((Viewholder) holder).tvName.setText(searchBooks.get(position).getName()); + // 设置书籍名称 + ((Viewholder) holder).tvAuthor.setText(searchBooks.get(position).getAuthor()); + // 设置作者名称 + + // 设置书籍状态(如连载状态) String state = searchBooks.get(position).getState(); if (state == null || state.length() == 0) { ((Viewholder) holder).tvState.setVisibility(View.GONE); + // 如果没有状态信息,隐藏 + } else { ((Viewholder) holder).tvState.setVisibility(View.VISIBLE); + // 否则显示状态 + ((Viewholder) holder).tvState.setText(state); } + + // 设置字数信息,超过1万字时显示“万字”单位 long words = searchBooks.get(position).getWords(); if (words <= 0) { ((Viewholder) holder).tvWords.setVisibility(View.GONE); + // 如果字数小于等于0,隐藏字数信息 + } else { String wordsS = Long.toString(words) + "字"; + // 默认显示字数 + if (words > 10000) { DecimalFormat df = new DecimalFormat("#.#"); + // 格式化为“万字” + wordsS = df.format(words * 1.0f / 10000f) + "万字"; } ((Viewholder) holder).tvWords.setVisibility(View.VISIBLE); + // 显示字数信息 + ((Viewholder) holder).tvWords.setText(wordsS); } + + // 设置书籍类型(如小说、历史等) String kind = searchBooks.get(position).getKind(); if (kind == null || kind.length() <= 0) { ((Viewholder) holder).tvKind.setVisibility(View.GONE); + // 如果没有类型信息,隐藏 + } else { ((Viewholder) holder).tvKind.setVisibility(View.VISIBLE); + // 否则显示类型 + ((Viewholder) holder).tvKind.setText(kind); } + + // 设置书籍的最新章节或简介 if (searchBooks.get(position).getLastChapter() != null && searchBooks.get(position).getLastChapter().length() > 0) ((Viewholder) holder).tvLastest.setText(searchBooks.get(position).getLastChapter()); + // 显示最新章节 + else if (searchBooks.get(position).getDesc() != null && searchBooks.get(position).getDesc().length() > 0) { ((Viewholder) holder).tvLastest.setText(searchBooks.get(position).getDesc()); + // 显示简介 + } else ((Viewholder) holder).tvLastest.setText(""); + // 如果没有最新章节和简介,设置为空 + + + // 设置书籍的来源信息 if (searchBooks.get(position).getOrigin() != null && searchBooks.get(position).getOrigin().length() > 0) { ((Viewholder) holder).tvOrigin.setVisibility(View.VISIBLE); + // 如果有来源,显示 + ((Viewholder) holder).tvOrigin.setText("来源:" + searchBooks.get(position).getOrigin()); } else { ((Viewholder) holder).tvOrigin.setVisibility(View.GONE); + // 如果没有来源,隐藏 + } + + // 设置书籍是否已添加到书架的状态 if (searchBooks.get(position).getAdd()) { ((Viewholder) holder).tvAddShelf.setText("已添加"); ((Viewholder) holder).tvAddShelf.setEnabled(false); + // 如果已添加,禁用添加按钮 + } else { ((Viewholder) holder).tvAddShelf.setText("+添加"); ((Viewholder) holder).tvAddShelf.setEnabled(true); + // 如果未添加,启用添加按钮 + } + // 设置点击事件,点击书籍项时触发 ((Viewholder) holder).flContent.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -102,6 +209,8 @@ public class SearchBookAdapter extends RefreshRecyclerViewAdapter { itemClickListener.clickItem(((Viewholder) holder).ivCover, position, searchBooks.get(position)); } }); + + // 设置点击事件,点击添加书架按钮时触发 ((Viewholder) holder).tvAddShelf.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -111,68 +220,129 @@ public class SearchBookAdapter extends RefreshRecyclerViewAdapter { }); } + // 获取每个列表项的类型 @Override public int getItemViewtype(int position) { return 0; + // 默认返回0,表示只有一种类型 } + // 获取列表项数量 @Override public int getItemcount() { return searchBooks.size(); + // 返回书籍列表的大小 } + // ViewHolder类,管理书籍项的视图控件 class Viewholder extends RecyclerView.ViewHolder { FrameLayout flContent; + // 根布局,书籍项的容器 + ImageView ivCover; + // 显示书籍封面的ImageView + TextView tvName; + // 显示书籍名称的TextView + TextView tvAuthor; + // 显示作者的TextView + TextView tvState; + // 显示书籍状态的TextView(如连载状态) + TextView tvWords; + // 显示字数的TextView + TextView tvKind; + // 显示书籍类型的TextView + TextView tvLastest; + // 显示最新章节或简介的TextView + TextView tvAddShelf; + // 显示添加到书架按钮的TextView + TextView tvOrigin; + // 显示书籍来源的TextView + // 构造函数,初始化视图控件 public Viewholder(View itemView) { super(itemView); flContent = (FrameLayout) itemView.findViewById(R.id.fl_content); + // 获取根布局 + ivCover = (ImageView) itemView.findViewById(R.id.iv_cover); + // 获取书籍封面ImageView + tvName = (TextView) itemView.findViewById(R.id.tv_name); + // 获取书籍名称TextView + tvAuthor = (TextView) itemView.findViewById(R.id.tv_author); + // 获取作者TextView + tvState = (TextView) itemView.findViewById(R.id.tv_state); + // 获取书籍状态TextView + tvWords = (TextView) itemView.findViewById(R.id.tv_words); + // 获取字数TextView + tvLastest = (TextView) itemView.findViewById(R.id.tv_lastest); + // 获取最新章节TextView + tvAddShelf = (TextView) itemView.findViewById(R.id.tv_addshelf); + // 获取添加到书架按钮TextView + tvKind = (TextView) itemView.findViewById(R.id.tv_kind); + // 获取书籍类型TextView + tvOrigin = (TextView) itemView.findViewById(R.id.tv_origin); + // 获取来源TextView } } + // 设置点击事件监听器 public void setItemClickListener(OnItemClickListener itemClickListener) { this.itemClickListener = itemClickListener; + // 保存监听器 } + // 批量添加数据到列表 public void addAll(List newDatas) { - if(newDatas!=null && newDatas.size()>0){ + if (newDatas != null && newDatas.size() > 0) { int oldCount = getItemcount(); + // 获取当前列表项数 + searchBooks.addAll(newDatas); - notifyItemRangeInserted(oldCount,newDatas.size()); + // 将新数据添加到列表 + + notifyItemRangeInserted(oldCount, newDatas.size()); + // 通知RecyclerView插入新的数据项 } } + // 替换列表中的所有数据 public void replaceAll(List newData) { searchBooks.clear(); + // 清空现有数据 + if (newData != null && newData.size() > 0) { searchBooks.addAll(newData); + // 添加新数据 } notifyDataSetChanged(); + // 通知RecyclerView更新数据 } + // 获取当前的书籍列表数据 public List getSearchBooks() { return searchBooks; + // 返回书籍列表 } + // 设置新的书籍列表数据 public void setSearchBooks(List searchBooks) { this.searchBooks = searchBooks; + // 更新书籍列表 } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/monke/monkeybook/view/adapter/SearchHistoryAdapter.java b/app/src/main/java/com/monke/monkeybook/view/adapter/SearchHistoryAdapter.java index c2aebd0..85ad2ef 100644 --- a/app/src/main/java/com/monke/monkeybook/view/adapter/SearchHistoryAdapter.java +++ b/app/src/main/java/com/monke/monkeybook/view/adapter/SearchHistoryAdapter.java @@ -1,54 +1,104 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +// Copyright (c) 2017. 章钦豪. All rights reserved. + +// 定义适配器类所在的包 package com.monke.monkeybook.view.adapter; +// 导入所需的类和库 import android.view.LayoutInflater; +// 用于布局填充 + import android.view.View; +// 用于表示视图组件 + import android.widget.TextView; +// 用于显示文本的控件 + import com.monke.monkeybook.R; +// 引入项目的资源文件 + import com.monke.monkeybook.bean.SearchHistoryBean; +// 导入数据模型类,用于封装搜索历史 + import com.monke.monkeybook.widget.flowlayout.FlowLayout; +// 引入FlowLayout类,显示标签的布局 + import com.monke.monkeybook.widget.flowlayout.TagAdapter; +// 引入TagAdapter基类,扩展标签的适配器 + import java.util.ArrayList; +// 引入ArrayList类,创建一个可动态变化的列表 + + + +// 定义SearchHistoryAdapter类,继承TagAdapter + +// 用于显示搜索历史数据,并处理点击事件 public class SearchHistoryAdapter extends TagAdapter { + + // 构造函数:调用父类TagAdapter的构造方法,初始化空的数据集合 public SearchHistoryAdapter() { super(new ArrayList()); } - public interface OnItemClickListener{ + // 定义一个接口OnItemClickListener,用于处理点击事件 + public interface OnItemClickListener { + // 定义点击条目的回调方法,传递一个SearchHistoryBean对象 + void itemClick(SearchHistoryBean searchHistoryBean); } + + // 声明一个成员变量onItemClickListener,用于保存点击事件监听器 private SearchHistoryAdapter.OnItemClickListener onItemClickListener; + // 获取当前的点击事件监听器 public OnItemClickListener getListener() { return onItemClickListener; } + // 设置点击事件监听器 public void setOnItemClickListener(OnItemClickListener listener) { this.onItemClickListener = listener; } + // 重写getView方法,返回每个标签(SearchHistoryBean)对应的视图 @Override public View getView(FlowLayout parent, int position, final SearchHistoryBean searchHistoryBean) { + // 使用LayoutInflater填充自定义的item布局文件 + TextView tv = (TextView) LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_searchhistory_item, parent, false); + // 加载adapter_searchhistory_item布局 + + // 设置TextView的文本为SearchHistoryBean的内容 tv.setText(searchHistoryBean.getContent()); + + // 设置点击事件监听器 tv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - if(null != onItemClickListener){ + // 如果设置了点击事件监听器,调用它的itemClick方法 + if (null != onItemClickListener) { onItemClickListener.itemClick(searchHistoryBean); + + // 触发点击事件回调 } } }); + + // 返回填充好的TextView作为标签视图 return tv; } - public SearchHistoryBean getItemData(int position){ + // 获取指定位置的SearchHistoryBean对象 + public SearchHistoryBean getItemData(int position) { return mTagDatas.get(position); + // 从TagAdapter父类中获取数据集合并返回对应位置的数据 } - public int getDataSize(){ + // 获取数据集合的大小 + public int getDataSize() { return mTagDatas.size(); + // 返回标签数据的数量 } } diff --git a/build.gradle b/build.gradle index 43153d6..888750f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,23 @@ buildscript { repositories { - jcenter() - mavenCentral() - maven { - url 'https://maven.google.com/' - name 'Google' - } - google() +// jcenter() +// mavenCentral() +// maven { +// url 'https://maven.google.com/' +// name 'Google' +// } +// google() +// maven { url 'https://plugins.gradle.org/m2/' } +// maven { url 'https://maven.aliyun.com/nexus/content/repositories/google' } +// maven { url 'https://maven.aliyun.com/nexus/content/groups/public' } +// maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter'} + + maven { url 'https://maven.aliyun.com/repository/public' } + maven { url 'https://maven.aliyun.com/repository/central' } + maven { url 'https://maven.aliyun.com/repository/jcenter' } + maven { url 'https://maven.aliyun.com/repository/google' } + maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } } dependencies { classpath 'com.android.tools.build:gradle:3.2.1' @@ -17,11 +27,21 @@ buildscript { allprojects { repositories { - jcenter() - maven { - url 'https://maven.google.com/' - name 'Google' - } +// jcenter() +// maven { +// url 'https://maven.google.com/' +// name 'Google' +// } +// maven { url 'https://plugins.gradle.org/m2/' } +// maven { url 'https://maven.aliyun.com/nexus/content/repositories/google' } +// maven { url 'https://maven.aliyun.com/nexus/content/groups/public' } +// maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter'} + + maven { url 'https://maven.aliyun.com/repository/public' } + maven { url 'https://maven.aliyun.com/repository/central' } + maven { url 'https://maven.aliyun.com/repository/jcenter' } + maven { url 'https://maven.aliyun.com/repository/google' } + maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } } } -- 2.34.1 From c71df4d60b94e0c8251c02b64191acbb48264f70 Mon Sep 17 00:00:00 2001 From: SYH <1017401752@qq.com> Date: Mon, 16 Dec 2024 23:13:22 +0800 Subject: [PATCH 3/8] 1 --- .../com/monke/monkeybook/common/RxBusTag.java | 24 ++-- .../monkeybook/common/api/IEasouApi.java | 75 +++++++----- .../monkeybook/common/api/IGxwztvApi.java | 110 ++++++++++-------- .../common/api/ILingdiankanshuApi.java | 75 +++++++----- 4 files changed, 164 insertions(+), 120 deletions(-) diff --git a/app/src/main/java/com/monke/monkeybook/common/RxBusTag.java b/app/src/main/java/com/monke/monkeybook/common/RxBusTag.java index 22a3afa..d317255 100644 --- a/app/src/main/java/com/monke/monkeybook/common/RxBusTag.java +++ b/app/src/main/java/com/monke/monkeybook/common/RxBusTag.java @@ -1,25 +1,25 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +//定义当前类所在的包路径为"com.monke.monkeybook.common",表明它属于"monkeybook"项目的公共部分 package com.monke.monkeybook.common; - +//定义一个名为 RxBusTag 的公共类,包含一组常量字符串,用于标识RxBus事件 public class RxBusTag { - + //定义一个静态常量字符串,表示添加书籍的事件 public final static String HAD_ADD_BOOK = "rxbus_add_book"; - + //定义一个静态常量字符串,表示移除书籍的事件 public final static String HAD_REMOVE_BOOK = "rxbus_remove_book"; - + //定义一个静态常量字符串,表示更新书籍进度的事件 public final static String UPDATE_BOOK_PROGRESS = "rxbus_update_book_progress"; - + //定义一个静态常量字符串,表示暂停下载的监听事件 public final static String PAUSE_DOWNLOAD_LISTENER = "rxbus_pause_download_listener"; - + //定义一个静态常量字符串,表示下载进度监听事件 public final static String PROGRESS_DOWNLOAD_LISTENER = "rxbus_progress_download_listener"; - + //定义一个静态常量字符串,表示下载完成监听事件 public final static String FINISH_DOWNLOAD_LISTENER = "rxbus_finish_download_listener"; - + //定义一个静态常量字符串,表示暂停下载的事件 public final static String PAUSE_DOWNLOAD = "rxbus_pause_download"; - + //定义一个静态常量字符串,表示开始下载的事件 public final static String START_DOWNLOAD = "rxbus_start_download"; - + //定义一个静态常量字符串,表示取消下载的事件 public final static String CANCEL_DOWNLOAD = "rxbus_cancel_download"; - + //定义一个静态常量字符串,表示添加下载任务的事件 public final static String ADD_DOWNLOAD_TASK = "rxbus_add_download_task"; } diff --git a/app/src/main/java/com/monke/monkeybook/common/api/IEasouApi.java b/app/src/main/java/com/monke/monkeybook/common/api/IEasouApi.java index c631ded..90aeb92 100644 --- a/app/src/main/java/com/monke/monkeybook/common/api/IEasouApi.java +++ b/app/src/main/java/com/monke/monkeybook/common/api/IEasouApi.java @@ -1,44 +1,59 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +//定义当前接口所在的包 com.monke.monkeybook.common.api +//这个包路径表示该接口用于应用程序的公共API部分。 package com.monke.monkeybook.common.api; - -import io.reactivex.Observable; -import retrofit2.http.GET; -import retrofit2.http.Headers; -import retrofit2.http.Url; +//引入所需要的库 +import io.reactivex.Observable; //导入Observable类,这是RxJava中的一个核心类,表示一个可以发射数据的流 +import retrofit2.http.GET; //导入Retrofit中的GET注解,用于表示一个HTTP GET请求 +import retrofit2.http.Headers; //导入Retrofit中的Headers注解,用于向HTTP请求添加自定义头部信息。 +import retrofit2.http.Url; //导入Retrofit中的Url注解,用于动态传递URL。 /** * 宜搜小说API (质量太差 废弃) */ +//定义一个接口IEasouApi,该接口包含了一些方法,用于与宜搜小说的API进行交互。 public interface IEasouApi { - @GET - @Headers({"Accept:text/html,application/xhtml+xml,application/xml", - "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", - "Accept-Charset:UTF-8", - "Connection:close", - "Cache-Control:no-cache"}) + //定义获取书籍信息的方法,通过HTTP GET请求,并添加自定义请求头 + @GET //定义GET请求 + //设置请求头 + @Headers({"Accept:text/html,application/xhtml+xml,application/xml", //请求头:表示支持的响应类型 + "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", //设置User-Agent,模拟浏览器请求 + "Accept-Charset:UTF-8", //请求头:指定字符集为 UTF-8 + "Connection:close", //请求头:请求完成后关闭连接 + "Cache-Control:no-cache"}) //禁用缓存 + //返回值为Observable,这是一个异步操作,最后会返回一个String类型的数据流 + //@Url表示该方法接收一个动态URL作为参数 Observable getBookInfo(@Url String url); - @GET - @Headers({"Accept:text/html,application/xhtml+xml,application/xml", - "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", - "Accept-Charset:UTF-8", - "Connection:close", - "Cache-Control:no-cache"}) + //定义搜索书籍的方法,类似getBookInfo + @GET //定义GET请求 + //设置请求头 + @Headers({"Accept:text/html,application/xhtml+xml,application/xml", //请求头:表示支持的响应类型 + "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", //设置User-Agent,模拟浏览器请求 + "Accept-Charset:UTF-8", //请求头:指定字符集为 UTF-8 + "Connection:close", //请求头:请求完成后关闭连接 + "Cache-Control:no-cache"}) //禁用缓存 + //该方法接受动态URL,返回书籍搜索结果 Observable searchBook(@Url String url); - @GET - @Headers({"Accept:text/html,application/xhtml+xml,application/xml", - "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", - "Accept-Charset:UTF-8", - "Connection:close", - "Cache-Control:no-cache"}) + //定义获取书籍内容的方法,类似前两个方法 + @GET //定义GET请求 + //设置请求头 + @Headers({"Accept:text/html,application/xhtml+xml,application/xml", //请求头:表示支持的响应类型 + "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", //设置User-Agent,模拟浏览器请求 + "Accept-Charset:UTF-8", //请求头:指定字符集为 UTF-8 + "Connection:close", //请求头:请求完成后关闭连接 + "Cache-Control:no-cache"}) //禁用缓存 + //该方法获取书籍的内容,通过URL传递获取内容的地址 Observable getBookContent(@Url String url); - @GET - @Headers({"Accept:text/html,application/xhtml+xml,application/xml", - "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", - "Accept-Charset:UTF-8", - "Connection:close", - "Cache-Control:no-cache"}) + //定义获取章节列表的方法,同样类似上述方法 + @GET //定义GET请求 + //设置请求头 + @Headers({"Accept:text/html,application/xhtml+xml,application/xml", //请求头:表示支持的响应类型 + "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", //设置User-Agent,模拟浏览器请求 + "Accept-Charset:UTF-8", //请求头:指定字符集为 UTF-8 + "Connection:close", //请求头:请求完成后关闭连接 + "Cache-Control:no-cache"}) //禁用缓存 + //该方法获取书籍的章节列表,通过URL传递获取章节的地址 Observable getChapterList(@Url String url); } diff --git a/app/src/main/java/com/monke/monkeybook/common/api/IGxwztvApi.java b/app/src/main/java/com/monke/monkeybook/common/api/IGxwztvApi.java index 16f1a8e..801dc20 100644 --- a/app/src/main/java/com/monke/monkeybook/common/api/IGxwztvApi.java +++ b/app/src/main/java/com/monke/monkeybook/common/api/IGxwztvApi.java @@ -1,61 +1,79 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +//定义包名,表明该接口属于 "monkeybook" 项目中的 API 部分 package com.monke.monkeybook.common.api; +//引入所需的库 +import io.reactivex.Observable; //引入RxJava中的Observable,用于定义响应式流 +import retrofit2.http.Field; //引入Retrofit中的@Field 注解,用于表示POST请求中的表单字段 +import retrofit2.http.FormUrlEncoded; //引入Retrofit中的@FormUrlEncoded注解,用于标记表单请求 +import retrofit2.http.GET; //引入Retrofit中的@GET注解,用于定义GET请求 +import retrofit2.http.Headers; //引入Retrofit中的@Headers注解,用于添加HTTP请求头 +import retrofit2.http.Query; //引入Retrofit中的@Query注解,用于在URL中添加查询参数 +import retrofit2.http.Url; //引入Retrofit中的@Url注解,用于动态传递URL -import io.reactivex.Observable; -import retrofit2.http.Field; -import retrofit2.http.FormUrlEncoded; -import retrofit2.http.GET; -import retrofit2.http.Headers; -import retrofit2.http.Query; -import retrofit2.http.Url; - +//该API接口定义了与小说网站进行交互的方法,用于获取书籍信息、搜索书籍、获取书籍内容等。 public interface IGxwztvApi { - - @GET - @Headers({"Accept:text/html,application/xhtml+xml,application/xml", - "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", - "Accept-Charset:UTF-8", - "Connection:close", - "Cache-Control:no-cache"}) + //获取书籍信息的请求 + @GET //定义GET请求 + //设置请求头 + @Headers({"Accept:text/html,application/xhtml+xml,application/xml", //设置请求头:接受的响应类型 + "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", //设置User-Agent,模拟浏览器请求 + "Accept-Charset:UTF-8", //设置字符集为 UTF-8 + "Connection:close", //请求头:请求完成后关闭连接 + "Cache-Control:no-cache"}) //禁用缓存 + //该方法接收一个动态URL,返回Observable,表示异步的响应数据流 Observable getBookInfo(@Url String url); - @GET("/search.htm") - @Headers({"Accept:text/html,application/xhtml+xml,application/xml", - "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", - "Accept-Charset:UTF-8", - "Connection:close", - "Cache-Control:no-cache"}) + //搜索书籍的请求 + @GET("/search.htm") //定义固定的GET,请求路径为"/search.htm" + //设置请求头 + @Headers({"Accept:text/html,application/xhtml+xml,application/xml", //设置请求头:接受的响应类型 + "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", //设置User-Agent,模拟浏览器请求 + "Accept-Charset:UTF-8", //设置字符集为 UTF-8 + "Connection:close", //请求头:请求完成后关闭连接 + "Cache-Control:no-cache"}) //禁用缓存 + //使用@Query注解传递查询参数,表示关键词和页码 Observable searchBook(@Query("keyword")String content, @Query("pn")int page); - @GET - @Headers({"Accept:text/html,application/xhtml+xml,application/xml", - "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", - "Accept-Charset:UTF-8", - "Connection:close", - "Cache-Control:no-cache"}) + //获取书籍内容的请求 + @GET //定义GET请求 + //设置请求头 + @Headers({"Accept:text/html,application/xhtml+xml,application/xml", //请求头:表示支持的响应类型 + "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", //设置User-Agent,模拟浏览器请求 + "Accept-Charset:UTF-8", //设置字符集为 UTF-8 + "Connection:close", //请求头:请求完成后关闭连接 + "Cache-Control:no-cache"}) //禁用缓存 + //该方法接收动态URL,返回书籍的内容 Observable getBookContent(@Url String url); - @GET - @Headers({"Accept:text/html,application/xhtml+xml,application/xml", - "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", - "Accept-Charset:UTF-8", - "Connection:close", - "Cache-Control:no-cache"}) + //获取章节列表的请求 + @GET //定义GET请求 + //设置请求头 + @Headers({"Accept:text/html,application/xhtml+xml,application/xml", //请求头:表示支持的响应类型 + "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", //设置User-Agent,模拟浏览器请求 + "Accept-Charset:UTF-8", //设置字符集为 UTF-8 + "Connection:close", //请求头:请求完成后关闭连接 + "Cache-Control:no-cache"}) //禁用缓存 + //该方法接收动态URL,返回章节列表 Observable getChapterList(@Url String url); - @GET - @Headers({"Accept:text/html,application/xhtml+xml,application/xml", - "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", - "Accept-Charset:UTF-8", - "Connection:close", - "Cache-Control:no-cache"}) + //获取书籍类型相关书籍的请求 + @GET //定义GET请求 + //设置请求头 + @Headers({"Accept:text/html,application/xhtml+xml,application/xml", //请求头:表示支持的响应类型 + "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", //设置User-Agent,模拟浏览器请求 + "Accept-Charset:UTF-8", //设置字符集为 UTF-8 + "Connection:close", //请求头:请求完成后关闭连接 + "Cache-Control:no-cache"}) //禁用缓存 + //该方法接收动态URL,返回该类型的书籍 Observable getKindBooks(@Url String url); - @GET - @Headers({"Accept:text/html,application/xhtml+xml,application/xml", - "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", - "Accept-Charset:UTF-8", - "Connection:close", - "Cache-Control:no-cache"}) + // 获取图书馆数据的请求 + @GET //定义GET请求 + //设置请求头 + @Headers({"Accept:text/html,application/xhtml+xml,application/xml", //请求头:表示支持的响应类型 + "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", //设置User-Agent,模拟浏览器请求 + "Accept-Charset:UTF-8", //设置字符集为 UTF-8 + "Connection:close", //请求头:请求完成后关闭连接 + "Cache-Control:no-cache"}) //禁用缓存 + //该方法接收动态URL,返回图书馆相关的数据 Observable getLibraryData(@Url String url); } diff --git a/app/src/main/java/com/monke/monkeybook/common/api/ILingdiankanshuApi.java b/app/src/main/java/com/monke/monkeybook/common/api/ILingdiankanshuApi.java index af86c0e..66770f6 100644 --- a/app/src/main/java/com/monke/monkeybook/common/api/ILingdiankanshuApi.java +++ b/app/src/main/java/com/monke/monkeybook/common/api/ILingdiankanshuApi.java @@ -1,43 +1,54 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +//定义包路径,表示该接口属于"monkeybook"项目中的公共API部分 package com.monke.monkeybook.common.api; +//引入所需的库 +import io.reactivex.Observable; //引入RxJava中的Observable类,表示异步流 +import retrofit2.http.GET; //引入Retrofit中的@GET注解,定义GET请求 +import retrofit2.http.Headers; //引入Retrofit中的 @Headers 注解,用于添加自定义请求头 +import retrofit2.http.Query; //引入Retrofit中的@Query注解,用于在URL中添加查询参数 +import retrofit2.http.Url; //引入Retrofit中的@Url注解,表示动态传递URL -import io.reactivex.Observable; -import retrofit2.http.GET; -import retrofit2.http.Headers; -import retrofit2.http.Query; -import retrofit2.http.Url; - +//该API接口定义了与零点看书小说网站进行交互的方法,用于获取书籍信息、搜索书籍、获取书籍内容等。 public interface ILingdiankanshuApi { - - @GET - @Headers({"Accept:text/html,application/xhtml+xml,application/xml", - "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", - "Accept-Charset:UTF-8", - "Connection:close", - "Cache-Control:no-cache"}) + //获取书籍信息的请求 + @GET //定义GET请求 + //设置自定义的HTTP请求头 + @Headers({"Accept:text/html,application/xhtml+xml,application/xml", //请求头:表示支持的响应类型 + "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", //模拟浏览器的User-Agent + "Accept-Charset:UTF-8", //请求头:指定字符集为 UTF-8 + "Connection:close", //请求头:请求完成后关闭连接 + "Cache-Control:no-cache"}) //请求头:禁用缓存 + //该方法接受一个动态URL,返回一个Observable类型的响应流,表示书籍信息 Observable getBookInfo(@Url String url); - @GET("/cse/search") - @Headers({"Accept:text/html,application/xhtml+xml,application/xml", - "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", - "Accept-Charset:UTF-8", - "Connection:close", - "Cache-Control:no-cache"}) + //搜索书籍的请求 + @GET("/cse/search") //定义GET请求的URL路径"/cse/search" + //设置请求头 + @Headers({"Accept:text/html,application/xhtml+xml,application/xml", //请求头:表示支持的响应类型 + "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", //模拟浏览器的User-Agent + "Accept-Charset:UTF-8", //请求头:指定字符集为 UTF-8 + "Connection:close", //请求头:请求完成后关闭连接 + "Cache-Control:no-cache"}) //请求头:禁用缓存 + //该方法接受三个查询参数:书籍关键词`q`,页码`p`,以及时间`s`,返回书籍搜索结果 Observable searchBook(@Query("q") String content, @Query("p") int page, @Query("s") String time); - @GET - @Headers({"Accept:text/html,application/xhtml+xml,application/xml", - "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", - "Accept-Charset:UTF-8", - "Connection:close", - "Cache-Control:no-cache"}) + //获取书籍内容的请求 + @GET //定义GET请求 + //设置请求头 + @Headers({"Accept:text/html,application/xhtml+xml,application/xml", //请求头:表示支持的响应类型 + "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", //模拟浏览器的User-Agent + "Accept-Charset:UTF-8", //请求头:指定字符集为 UTF-8 + "Connection:close", //请求头:请求完成后关闭连接 + "Cache-Control:no-cache"}) //请求头:禁用缓存 + //该方法接受一个动态URL,返回书籍内容 Observable getBookContent(@Url String url); - @GET - @Headers({"Accept:text/html,application/xhtml+xml,application/xml", - "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", - "Accept-Charset:UTF-8", - "Connection:close", - "Cache-Control:no-cache"}) + @GET //定义GET请求 + //设置请求头 + @Headers({"Accept:text/html,application/xhtml+xml,application/xml", //请求头:表示支持的响应类型 + "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3", //模拟浏览器的User-Agent + "Accept-Charset:UTF-8", //请求头:指定字符集为 UTF-8 + "Connection:close", //请求头:请求完成后关闭连接 + "Cache-Control:no-cache"}) //请求头:禁用缓存 + //该方法接受一个动态URL,返回书籍的章节列表 Observable getChapterList(@Url String url); } -- 2.34.1 From 6b84523ec00139dc6a34625e9a0e2e3d6516cff3 Mon Sep 17 00:00:00 2001 From: SYH <1017401752@qq.com> Date: Mon, 16 Dec 2024 23:17:19 +0800 Subject: [PATCH 4/8] 1 --- .../monkeybook/dao/BookContentBeanDao.java | 98 +++++--- .../monke/monkeybook/dao/BookInfoBeanDao.java | 212 +++++++++++------- .../monkeybook/dao/BookShelfBeanDao.java | 137 ++++++----- .../monkeybook/dao/ChapterListBeanDao.java | 148 +++++++----- .../com/monke/monkeybook/dao/DaoMaster.java | 89 +++++--- .../com/monke/monkeybook/dao/DaoSession.java | 159 ++++++------- 6 files changed, 522 insertions(+), 321 deletions(-) diff --git a/app/src/main/java/com/monke/monkeybook/dao/BookContentBeanDao.java b/app/src/main/java/com/monke/monkeybook/dao/BookContentBeanDao.java index 6e40f73..92c5d2f 100644 --- a/app/src/main/java/com/monke/monkeybook/dao/BookContentBeanDao.java +++ b/app/src/main/java/com/monke/monkeybook/dao/BookContentBeanDao.java @@ -1,28 +1,30 @@ +//指定了当前类BookContentBeanDao所在的包路径,即com.monke.monkeybook.dao package com.monke.monkeybook.dao; - +//导入必要的类,包含GreenDAO、SQLite和Android数据库操作的相关类 import android.database.Cursor; import android.database.sqlite.SQLiteStatement; - import org.greenrobot.greendao.AbstractDao; import org.greenrobot.greendao.Property; import org.greenrobot.greendao.internal.DaoConfig; import org.greenrobot.greendao.database.Database; import org.greenrobot.greendao.database.DatabaseStatement; - +// 导入实体类 BookContentBean import com.monke.monkeybook.bean.BookContentBean; // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. -/** +/** * DAO for table "BOOK_CONTENT_BEAN". -*/ + */ +//继承自GreenDAO的AbstractDao类,BookContentBean为实体类,String为主键类型 public class BookContentBeanDao extends AbstractDao { - + //定义常量TABLENAME,表示该表的名称 public static final String TABLENAME = "BOOK_CONTENT_BEAN"; - /** * Properties of entity BookContentBean.
* Can be used for QueryBuilder and for referencing column names. - */ + * BookContentBean是实体类的属性,可以用于QueryBuilder以及引用数据库表字段名。 + */ + // 定义实体类属性映射:对应数据库表字段名的属性 public static class Properties { public final static Property DurChapterUrl = new Property(0, String.class, "durChapterUrl", true, "DUR_CHAPTER_URL"); public final static Property DurChapterIndex = new Property(1, int.class, "durChapterIndex", false, "DUR_CHAPTER_INDEX"); @@ -30,18 +32,24 @@ public class BookContentBeanDao extends AbstractDao { public final static Property Tag = new Property(3, String.class, "tag", false, "TAG"); }; - + //构造函数,传入DaoConfig,用于初始化DAO public BookContentBeanDao(DaoConfig config) { - super(config); + super(config); //调用父类构造函数,初始化Dao配置 } - + + // 构造函数,传入DaoConfig和DaoSession,用于初始化DAO和会话管理 public BookContentBeanDao(DaoConfig config, DaoSession daoSession) { - super(config, daoSession); + super(config, daoSession); //调用父类构造函数,初始化Dao配置和会话 } - /** Creates the underlying database table. */ + /** + * Creates the underlying database table. + * 创建数据库表 + */ public static void createTable(Database db, boolean ifNotExists) { + //如果ifNotExists为true,则在创建表时确保该表不存在 String constraint = ifNotExists? "IF NOT EXISTS ": ""; + //使用SQL执行表创建语句 db.execSQL("CREATE TABLE " + constraint + "\"BOOK_CONTENT_BEAN\" (" + // "\"DUR_CHAPTER_URL\" TEXT PRIMARY KEY NOT NULL ," + // 0: durChapterUrl "\"DUR_CHAPTER_INDEX\" INTEGER NOT NULL ," + // 1: durChapterIndex @@ -49,85 +57,110 @@ public class BookContentBeanDao extends AbstractDao { "\"TAG\" TEXT);"); // 3: tag } - /** Drops the underlying database table. */ + /** Drops the underlying database table. + * 删除数据库表 + */ public static void dropTable(Database db, boolean ifExists) { + //使用SQL删除表,确保表存在时删除 String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"BOOK_CONTENT_BEAN\""; db.execSQL(sql); } @Override protected final void bindValues(DatabaseStatement stmt, BookContentBean entity) { + //清除绑定的值,确保每次绑定时都是干净的 stmt.clearBindings(); - + + //绑定durChapterUrl字段到SQL语句的第一个位置 String durChapterUrl = entity.getDurChapterUrl(); if (durChapterUrl != null) { + //将章节URL字符串绑定到SQL语句的第一个位置 stmt.bindString(1, durChapterUrl); } - stmt.bindLong(2, entity.getDurChapterIndex()); - + + //绑定durChapterIndex字段到SQL语句的第二个位置 + stmt.bindLong(2, entity.getDurChapterIndex());//将章节索引绑定到SQL语句的第二个位置 + + //绑定durCapterContent字段到SQL语句的第三个位置 String durCapterContent = entity.getDurCapterContent(); if (durCapterContent != null) { + //将章节内容绑定到SQL语句的第三个位置 stmt.bindString(3, durCapterContent); } - + + //绑定tag字段到SQL语句的第四个位置 String tag = entity.getTag(); if (tag != null) { + //将标签绑定到SQL语句的第四个位置 stmt.bindString(4, tag); } } @Override protected final void bindValues(SQLiteStatement stmt, BookContentBean entity) { + //清除绑定的值,确保每次绑定时都是干净的 stmt.clearBindings(); - + + //绑定durChapterUrl字段到SQL语句的第一个位置 String durChapterUrl = entity.getDurChapterUrl(); if (durChapterUrl != null) { + //将章节URL字符串绑定到SQL语句的第一个位置 stmt.bindString(1, durChapterUrl); } - stmt.bindLong(2, entity.getDurChapterIndex()); - + //绑定durChapterIndex字段到SQL语句的第二个位置 + stmt.bindLong(2, entity.getDurChapterIndex()); //将章节索引绑定到SQL语句的第二个位置 + + //绑定durCapterContent字段到SQL语句的第三个位置 String durCapterContent = entity.getDurCapterContent(); if (durCapterContent != null) { + //将章节内容绑定到SQL语句的第三个位置 stmt.bindString(3, durCapterContent); } - + + //绑定tag字段到SQL语句的第四个位置 String tag = entity.getTag(); if (tag != null) { + //将标签绑定到SQL语句的第四个位置 stmt.bindString(4, tag); } } @Override public String readKey(Cursor cursor, int offset) { + //从Cursor中读取主键(durChapterUrl),若为null则返回null return cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0); - } + } @Override public BookContentBean readEntity(Cursor cursor, int offset) { + //根据Cursor数据构建一个BookContentBean实体类对象,并返回 BookContentBean entity = new BookContentBean( // - cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0), // durChapterUrl - cursor.getInt(offset + 1), // durChapterIndex - cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // durCapterContent - cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3) // tag + cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0), // durChapterUrl + cursor.getInt(offset + 1), // durChapterIndex + cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // durCapterContent + cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3) // tag ); return entity; } - + @Override public void readEntity(Cursor cursor, BookContentBean entity, int offset) { + //从Cursor中读取值并设置到BookContentBean对象的相应属性中 entity.setDurChapterUrl(cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0)); entity.setDurChapterIndex(cursor.getInt(offset + 1)); entity.setDurCapterContent(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2)); entity.setTag(cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3)); - } - + } + @Override protected final String updateKeyAfterInsert(BookContentBean entity, long rowId) { + //返回更新时需要的主键字段,这里是durChapterUrl return entity.getDurChapterUrl(); } - + @Override public String getKey(BookContentBean entity) { + //获取实体对象的主键值,返回durChapterUrl if(entity != null) { return entity.getDurChapterUrl(); } else { @@ -137,7 +170,8 @@ public class BookContentBeanDao extends AbstractDao { @Override protected final boolean isEntityUpdateable() { + //指示实体是否可以被更新,这里返回true,表示支持更新 return true; } - + } diff --git a/app/src/main/java/com/monke/monkeybook/dao/BookInfoBeanDao.java b/app/src/main/java/com/monke/monkeybook/dao/BookInfoBeanDao.java index 3e17cf5..ac4edfb 100644 --- a/app/src/main/java/com/monke/monkeybook/dao/BookInfoBeanDao.java +++ b/app/src/main/java/com/monke/monkeybook/dao/BookInfoBeanDao.java @@ -1,156 +1,202 @@ +//定义类所在的包路径,表示该文件属于com.monke.monkeybook.dao包 package com.monke.monkeybook.dao; -import android.database.Cursor; -import android.database.sqlite.SQLiteStatement; +//导入必要的类,包含GreenDAO和Android数据库操作的相关类 +import android.database.Cursor;// 导入Cursor类,用于数据库查询结果的处理 +import android.database.sqlite.SQLiteStatement;//导入SQLiteStatement类,用于执行SQL语句 -import org.greenrobot.greendao.AbstractDao; -import org.greenrobot.greendao.Property; -import org.greenrobot.greendao.internal.DaoConfig; -import org.greenrobot.greendao.database.Database; -import org.greenrobot.greendao.database.DatabaseStatement; +import org.greenrobot.greendao.AbstractDao;//导入AbstractDao类,这是一个GreenDao框架的抽象类,用于定义数据库操作的基类 +import org.greenrobot.greendao.Property;//导入Property类,用于定义数据库表中的字段属性 +import org.greenrobot.greendao.internal.DaoConfig;//导入DaoConfig类,用于配置DAO对象 +import org.greenrobot.greendao.database.Database;//导入Database类,用于操作数据库 +import org.greenrobot.greendao.database.DatabaseStatement;//导入BookInfoBean实体类 -import com.monke.monkeybook.bean.BookInfoBean; +import com.monke.monkeybook.bean.BookInfoBean;//导入BookInfoBean类,这是数据库中要操作的实体类 // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. -/** +/** * DAO for table "BOOK_INFO_BEAN". -*/ -public class BookInfoBeanDao extends AbstractDao { + * DAO 类,用于操作 "BOOK_INFO_BEAN" 表 + */ +//BookInfoBeanDao继承自AbstractDao,操作BookInfoBean对象,主键类型为String +public class BookInfoBeanDao extends AbstractDao { + //定义常量TABLENAME,表示数据库表名 public static final String TABLENAME = "BOOK_INFO_BEAN"; /** * Properties of entity BookInfoBean.
* Can be used for QueryBuilder and for referencing column names. - */ + * BookInfoBean 实体类的属性,可以用于 QueryBuilder 以及引用数据库表字段名。 + */ public static class Properties { - public final static Property Name = new Property(0, String.class, "name", false, "NAME"); - public final static Property Tag = new Property(1, String.class, "tag", false, "TAG"); - public final static Property NoteUrl = new Property(2, String.class, "noteUrl", true, "NOTE_URL"); - public final static Property ChapterUrl = new Property(3, String.class, "chapterUrl", false, "CHAPTER_URL"); - public final static Property FinalRefreshData = new Property(4, long.class, "finalRefreshData", false, "FINAL_REFRESH_DATA"); - public final static Property CoverUrl = new Property(5, String.class, "coverUrl", false, "COVER_URL"); - public final static Property Author = new Property(6, String.class, "author", false, "AUTHOR"); - public final static Property Introduce = new Property(7, String.class, "introduce", false, "INTRODUCE"); - public final static Property Origin = new Property(8, String.class, "origin", false, "ORIGIN"); + //定义实体类属性映射:对应数据库表字段名的属性 + public final static Property Name = new Property(0, String.class, "name", false, "NAME");//属性0:名称(name),String类型,非主键,数据库列名NAME + public final static Property Tag = new Property(1, String.class, "tag", false, "TAG");//属性1:标签(tag),String类型,非主键,数据库列名TAG + public final static Property NoteUrl = new Property(2, String.class, "noteUrl", true, "NOTE_URL");// 属性2:笔记URL(noteUrl),String类型,主键,非空,数据库列名NOTE_URL + public final static Property ChapterUrl = new Property(3, String.class, "chapterUrl", false, "CHAPTER_URL");//属性3:章节URL(chapterUrl),String类型,非主键,数据库列名CHAPTER_URL + public final static Property FinalRefreshData = new Property(4, long.class, "finalRefreshData", false, "FINAL_REFRESH_DATA");//属性4:最后刷新时间(finalRefreshData),long类型,非主键,数据库列名FINAL_REFRESH_DATA + public final static Property CoverUrl = new Property(5, String.class, "coverUrl", false, "COVER_URL");//属性5:封面URL(coverUrl),String类型,非主键,数据库列名COVER_URL + public final static Property Author = new Property(6, String.class, "author", false, "AUTHOR");//属性6:作者(author),String类型,非主键,数据库列名AUTHOR + public final static Property Introduce = new Property(7, String.class, "introduce", false, "INTRODUCE");//属性7:简介(introduce),String类型,非主键,数据库列名INTRODUCE + public final static Property Origin = new Property(8, String.class, "origin", false, "ORIGIN");//属性8:来源(origin),String类型,非主键,数据库列名ORIGIN }; - + //构造函数,使用DaoConfig配置创建DAO对象 public BookInfoBeanDao(DaoConfig config) { super(config); } - + + //构造函数,使用DaoConfig和DaoSession配置创建DAO对象 public BookInfoBeanDao(DaoConfig config, DaoSession daoSession) { super(config, daoSession); } - /** Creates the underlying database table. */ + /** Creates the underlying database table. + * 创建数据库表 + */ + //创建数据库表的方法 public static void createTable(Database db, boolean ifNotExists) { + //判断是否需要添加IF NOT EXISTS条件 String constraint = ifNotExists? "IF NOT EXISTS ": ""; - db.execSQL("CREATE TABLE " + constraint + "\"BOOK_INFO_BEAN\" (" + // - "\"NAME\" TEXT," + // 0: name - "\"TAG\" TEXT," + // 1: tag - "\"NOTE_URL\" TEXT PRIMARY KEY NOT NULL ," + // 2: noteUrl - "\"CHAPTER_URL\" TEXT," + // 3: chapterUrl - "\"FINAL_REFRESH_DATA\" INTEGER NOT NULL ," + // 4: finalRefreshData - "\"COVER_URL\" TEXT," + // 5: coverUrl - "\"AUTHOR\" TEXT," + // 6: author - "\"INTRODUCE\" TEXT," + // 7: introduce - "\"ORIGIN\" TEXT);"); // 8: origin + //使用SQL执行表创建语句 + db.execSQL("CREATE TABLE " + constraint + "\"BOOK_INFO_BEAN\" (" + + "\"NAME\" TEXT," + //NAME字段,TEXT类型 + "\"TAG\" TEXT," + //TAG字段,TEXT类型 + "\"NOTE_URL\" TEXT PRIMARY KEY NOT NULL ," + //NOTE_URL字段,TEXT类型,主键,非空 + "\"CHAPTER_URL\" TEXT," + //CHAPTER_URL字段,TEXT类型 + "\"FINAL_REFRESH_DATA\" INTEGER NOT NULL ," + //FINAL_REFRESH_DATA字段,INTEGER类型,非空 + "\"COVER_URL\" TEXT," + //COVER_URL字段,TEXT类型 + "\"AUTHOR\" TEXT," + //AUTHOR字段,TEXT类型 + "\"INTRODUCE\" TEXT," + //INTRODUCE字段,TEXT类型 + "\"ORIGIN\" TEXT);"); //ORIGIN字段,TEXT类型 } - /** Drops the underlying database table. */ + /** Drops the underlying database table. + * 删除底层数据库表 + */ public static void dropTable(Database db, boolean ifExists) { - String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"BOOK_INFO_BEAN\""; - db.execSQL(sql); + //使用SQL删除表,确保表存在时删除 + String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"BOOK_INFO_BEAN\"";//构造删除表的SQL语句 + db.execSQL(sql);//执行删除表的SQL语句 } @Override + //将BookInfoBean实体对象的值绑定到DatabaseStatement对象中 protected final void bindValues(DatabaseStatement stmt, BookInfoBean entity) { + //清除绑定的值,确保每次绑定时都是干净的 stmt.clearBindings(); - + + //绑定name字段到SQL语句的第一个位置 String name = entity.getName(); if (name != null) { + //将书名绑定到SQL语句的第一个位置 stmt.bindString(1, name); } - + + //绑定tag字段到SQL语句的第二个位置 String tag = entity.getTag(); if (tag != null) { + //将标签绑定到SQL语句的第二个位置 stmt.bindString(2, tag); } - + + //绑定noteUrl字段到SQL语句的第三个位置 String noteUrl = entity.getNoteUrl(); if (noteUrl != null) { + //将书籍URL绑定到SQL语句的第三个位置 stmt.bindString(3, noteUrl); } - + + //绑定chapterUrl字段到SQL语句的第四个位置 String chapterUrl = entity.getChapterUrl(); if (chapterUrl != null) { + //将章节URL绑定到SQL语句的第四个位置 stmt.bindString(4, chapterUrl); } + //将最后刷新时间(long 类型)绑定到SQL语句的第五个位置 stmt.bindLong(5, entity.getFinalRefreshData()); - + + //绑定coverUrl字段到SQL语句的第六个位置 String coverUrl = entity.getCoverUrl(); if (coverUrl != null) { + //将封面图URL绑定到SQL语句的第六个位置 stmt.bindString(6, coverUrl); } - + + //绑定author字段到SQL语句的第七个位置 String author = entity.getAuthor(); if (author != null) { + //将作者绑定到SQL语句的第七个位置 stmt.bindString(7, author); } - + + //绑定introduce字段到SQL语句的第八个位置 String introduce = entity.getIntroduce(); if (introduce != null) { + //将简介绑定到SQL语句的第八个位置 stmt.bindString(8, introduce); } - + + //绑定origin字段到SQL语句的第九个位置 String origin = entity.getOrigin(); if (origin != null) { + //将来源绑定到SQL语句的第九个位置 stmt.bindString(9, origin); } } @Override + //将BookInfoBean实体对象的值绑定到SQLiteStatement对象中,与上一个方法功能类似,只是对象不同 protected final void bindValues(SQLiteStatement stmt, BookInfoBean entity) { + //清除绑定的值,确保每次绑定时都是干净的 stmt.clearBindings(); - + + //绑定name字段值 String name = entity.getName(); if (name != null) { stmt.bindString(1, name); } - + + //绑定tag字段值 String tag = entity.getTag(); if (tag != null) { stmt.bindString(2, tag); } - + + //绑定noteUrl字段值 String noteUrl = entity.getNoteUrl(); if (noteUrl != null) { stmt.bindString(3, noteUrl); } - + + //绑定chapterUrl字段值 String chapterUrl = entity.getChapterUrl(); if (chapterUrl != null) { stmt.bindString(4, chapterUrl); } + //绑定finalRefreshData字段值 stmt.bindLong(5, entity.getFinalRefreshData()); - + + //绑定coverUrl字段值 String coverUrl = entity.getCoverUrl(); if (coverUrl != null) { stmt.bindString(6, coverUrl); } - + + //绑定author字段值 String author = entity.getAuthor(); if (author != null) { stmt.bindString(7, author); } - + + //绑定introduce字段值 String introduce = entity.getIntroduce(); if (introduce != null) { stmt.bindString(8, introduce); } - + + //绑定origin字段值 String origin = entity.getOrigin(); if (origin != null) { stmt.bindString(9, origin); @@ -158,47 +204,57 @@ public class BookInfoBeanDao extends AbstractDao { } @Override + //从Cursor中读取主键 public String readKey(Cursor cursor, int offset) { + //主键在第三列(offset + 2),如果为空返回null,否则返回字符串 return cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2); - } + } @Override + //从Cursor中读取BookInfoBean实体 public BookInfoBean readEntity(Cursor cursor, int offset) { + //创建BookInfoBean对象 BookInfoBean entity = new BookInfoBean( // - cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0), // name - cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1), // tag - cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // noteUrl - cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3), // chapterUrl - cursor.getLong(offset + 4), // finalRefreshData - cursor.isNull(offset + 5) ? null : cursor.getString(offset + 5), // coverUrl - cursor.isNull(offset + 6) ? null : cursor.getString(offset + 6), // author - cursor.isNull(offset + 7) ? null : cursor.getString(offset + 7), // introduce - cursor.isNull(offset + 8) ? null : cursor.getString(offset + 8) // origin + cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0), // name + cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1), // tag + cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // noteUrl + cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3), // chapterUrl + cursor.getLong(offset + 4), // finalRefreshData + cursor.isNull(offset + 5) ? null : cursor.getString(offset + 5), // coverUrl + cursor.isNull(offset + 6) ? null : cursor.getString(offset + 6), // author + cursor.isNull(offset + 7) ? null : cursor.getString(offset + 7), // introduce + cursor.isNull(offset + 8) ? null : cursor.getString(offset + 8) // origin ); + //返回创建好的BookInfoBean对象 return entity; } - + @Override + //将Cursor中的数据读取到已存在的BookInfoBean实体中 public void readEntity(Cursor cursor, BookInfoBean entity, int offset) { - entity.setName(cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0)); - entity.setTag(cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1)); - entity.setNoteUrl(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2)); - entity.setChapterUrl(cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3)); - entity.setFinalRefreshData(cursor.getLong(offset + 4)); - entity.setCoverUrl(cursor.isNull(offset + 5) ? null : cursor.getString(offset + 5)); - entity.setAuthor(cursor.isNull(offset + 6) ? null : cursor.getString(offset + 6)); - entity.setIntroduce(cursor.isNull(offset + 7) ? null : cursor.getString(offset + 7)); - entity.setOrigin(cursor.isNull(offset + 8) ? null : cursor.getString(offset + 8)); - } - + entity.setName(cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0));//name + entity.setTag(cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1));//tag + entity.setNoteUrl(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2));//noteUrl + entity.setChapterUrl(cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3));//chapterUrl + entity.setFinalRefreshData(cursor.getLong(offset + 4));//finalRefreshData + entity.setCoverUrl(cursor.isNull(offset + 5) ? null : cursor.getString(offset + 5));//coverUrl + entity.setAuthor(cursor.isNull(offset + 6) ? null : cursor.getString(offset + 6));//author + entity.setIntroduce(cursor.isNull(offset + 7) ? null : cursor.getString(offset + 7));//introduce + entity.setOrigin(cursor.isNull(offset + 8) ? null : cursor.getString(offset + 8));//origin + } + @Override + //插入数据后更新主键 protected final String updateKeyAfterInsert(BookInfoBean entity, long rowId) { + //使用noteUrl作为主键 return entity.getNoteUrl(); } - + @Override + //获取主键 public String getKey(BookInfoBean entity) { if(entity != null) { + //返回noteUrl作为主键 return entity.getNoteUrl(); } else { return null; @@ -206,8 +262,10 @@ public class BookInfoBeanDao extends AbstractDao { } @Override + //是否可更新实体 protected final boolean isEntityUpdateable() { + //可更新 return true; } - + } diff --git a/app/src/main/java/com/monke/monkeybook/dao/BookShelfBeanDao.java b/app/src/main/java/com/monke/monkeybook/dao/BookShelfBeanDao.java index ba34fcb..b0eb9f3 100644 --- a/app/src/main/java/com/monke/monkeybook/dao/BookShelfBeanDao.java +++ b/app/src/main/java/com/monke/monkeybook/dao/BookShelfBeanDao.java @@ -1,132 +1,163 @@ +// 包声明,声明该类位于com.monke.monkeybook.dao包下 package com.monke.monkeybook.dao; -import android.database.Cursor; -import android.database.sqlite.SQLiteStatement; +import android.database.Cursor; // 导入Cursor类,用于从数据库游标中读取数据 +import android.database.sqlite.SQLiteStatement; // 导入SQLiteStatement类,用于执行预编译的SQL语句 -import org.greenrobot.greendao.AbstractDao; -import org.greenrobot.greendao.Property; -import org.greenrobot.greendao.internal.DaoConfig; -import org.greenrobot.greendao.database.Database; -import org.greenrobot.greendao.database.DatabaseStatement; +import org.greenrobot.greendao.AbstractDao; // 导入AbstractDao类,GreenDao框架中用于数据库操作的抽象基类 +import org.greenrobot.greendao.Property; // 导入Property类,用于定义数据库表字段的属性 +import org.greenrobot.greendao.internal.DaoConfig; // 导入DaoConfig类,用于配置Dao对象的配置信息 +import org.greenrobot.greendao.database.Database; // 导入Database类,GreenDao框架中用于数据库操作的类 +import org.greenrobot.greendao.database.DatabaseStatement; // 导入DatabaseStatement类,用于执行SQL语句 +// 导入BookShelfBean类,这是数据库操作的对象 import com.monke.monkeybook.bean.BookShelfBean; // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. -/** +/** * DAO for table "BOOK_SHELF_BEAN". -*/ + */ +// BookShelfBeanDao类继承AbstractDao,操作BookShelfBean对象,主键类型为String public class BookShelfBeanDao extends AbstractDao { - + // 数据库表名常量 public static final String TABLENAME = "BOOK_SHELF_BEAN"; /** * Properties of entity BookShelfBean.
* Can be used for QueryBuilder and for referencing column names. - */ + */ public static class Properties { - public final static Property NoteUrl = new Property(0, String.class, "noteUrl", true, "NOTE_URL"); - public final static Property DurChapter = new Property(1, int.class, "durChapter", false, "DUR_CHAPTER"); - public final static Property DurChapterPage = new Property(2, int.class, "durChapterPage", false, "DUR_CHAPTER_PAGE"); - public final static Property FinalDate = new Property(3, long.class, "finalDate", false, "FINAL_DATE"); - public final static Property Tag = new Property(4, String.class, "tag", false, "TAG"); + public final static Property NoteUrl = new Property(0, String.class, "noteUrl", true, "NOTE_URL"); // 属性0:noteUrl,String类型,主键,数据库列名NOTE_URL + public final static Property DurChapter = new Property(1, int.class, "durChapter", false, "DUR_CHAPTER"); // 属性1:durChapter,int类型,非主键,数据库列名DUR_CHAPTER + public final static Property DurChapterPage = new Property(2, int.class, "durChapterPage", false, "DUR_CHAPTER_PAGE"); // 属性2:durChapterPage,int类型,非主键,数据库列名DUR_CHAPTER_PAGE + public final static Property FinalDate = new Property(3, long.class, "finalDate", false, "FINAL_DATE"); // 属性3:finalDate,long类型,非主键,数据库列名FINAL_DATE + public final static Property Tag = new Property(4, String.class, "tag", false, "TAG"); // 属性4:tag,String类型,非主键,数据库列名TAG }; - + // 构造函数,使用DaoConfig配置初始化Dao public BookShelfBeanDao(DaoConfig config) { super(config); } - + + // 构造函数,使用DaoConfig和DaoSession配置初始化Dao public BookShelfBeanDao(DaoConfig config, DaoSession daoSession) { super(config, daoSession); } - /** Creates the underlying database table. */ + /** Creates the underlying database table. + * 创建数据库表 + */ + // 创建BOOK_SHELF_BEAN表的方法 public static void createTable(Database db, boolean ifNotExists) { + // 根据ifNotExists判断是否需要添加"IF NOT EXISTS"条件 String constraint = ifNotExists? "IF NOT EXISTS ": ""; + // 执行SQL语句创建表 db.execSQL("CREATE TABLE " + constraint + "\"BOOK_SHELF_BEAN\" (" + // - "\"NOTE_URL\" TEXT PRIMARY KEY NOT NULL ," + // 0: noteUrl - "\"DUR_CHAPTER\" INTEGER NOT NULL ," + // 1: durChapter - "\"DUR_CHAPTER_PAGE\" INTEGER NOT NULL ," + // 2: durChapterPage - "\"FINAL_DATE\" INTEGER NOT NULL ," + // 3: finalDate - "\"TAG\" TEXT);"); // 4: tag + "\"NOTE_URL\" TEXT PRIMARY KEY NOT NULL ," + //NOTE_URL字段,TEXT类型,主键,非空 + "\"DUR_CHAPTER\" INTEGER NOT NULL ," + // DUR_CHAPTER字段,INTEGER类型,非空 + "\"DUR_CHAPTER_PAGE\" INTEGER NOT NULL ," + // DUR_CHAPTER_PAGE字段,INTEGER类型,非空 + "\"FINAL_DATE\" INTEGER NOT NULL ," + // FINAL_DATE字段,INTEGER类型,非空 + "\"TAG\" TEXT);"); // TAG字段,TEXT类型 } - /** Drops the underlying database table. */ + /** Drops the underlying database table. + * 删除数据库表 + */ + // 删除BOOK_SHELF_BEAN表的方法 public static void dropTable(Database db, boolean ifExists) { + // 构造删除表的SQL语句 String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"BOOK_SHELF_BEAN\""; + // 执行SQL语句删除表 db.execSQL(sql); } @Override + // 将BookShelfBean实体对象的值绑定到DatabaseStatement语句中 protected final void bindValues(DatabaseStatement stmt, BookShelfBean entity) { + // 清除之前的绑定 stmt.clearBindings(); - + String noteUrl = entity.getNoteUrl(); if (noteUrl != null) { + // 绑定noteUrl值到第一个参数位置 stmt.bindString(1, noteUrl); } - stmt.bindLong(2, entity.getDurChapter()); - stmt.bindLong(3, entity.getDurChapterPage()); - stmt.bindLong(4, entity.getFinalDate()); - + stmt.bindLong(2, entity.getDurChapter()); // 绑定durChapter值到第二个参数位置 + stmt.bindLong(3, entity.getDurChapterPage()); // 绑定durChapterPage值到第三个参数位置 + stmt.bindLong(4, entity.getFinalDate()); // 绑定finalDate值到第四个参数位置 + String tag = entity.getTag(); if (tag != null) { - stmt.bindString(5, tag); + stmt.bindString(5, tag); // 绑定tag值到第五个参数位置 } } @Override + // 将BookShelfBean实体对象的值绑定到SQLiteStatement语句中,与上一个方法功能类似,只是对象不同 protected final void bindValues(SQLiteStatement stmt, BookShelfBean entity) { + // 清除之前的绑定 stmt.clearBindings(); - + String noteUrl = entity.getNoteUrl(); if (noteUrl != null) { + // 绑定noteUrl值到第一个参数位置 stmt.bindString(1, noteUrl); } - stmt.bindLong(2, entity.getDurChapter()); - stmt.bindLong(3, entity.getDurChapterPage()); - stmt.bindLong(4, entity.getFinalDate()); - + stmt.bindLong(2, entity.getDurChapter()); // 绑定durChapter值到第二个参数位置 + stmt.bindLong(3, entity.getDurChapterPage()); // 绑定durChapterPage值到第三个参数位置 + stmt.bindLong(4, entity.getFinalDate()); // 绑定finalDate值到第四个参数位置 + String tag = entity.getTag(); if (tag != null) { + // 绑定tag值到第五个参数位置 stmt.bindString(5, tag); } } @Override + // 从游标中读取主键 public String readKey(Cursor cursor, int offset) { + // 从偏移量offset+0的位置读取主键值,如果为空返回null,否则返回字符串 return cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0); - } + } @Override + // 从游标中读取BookShelfBean实体 public BookShelfBean readEntity(Cursor cursor, int offset) { + // 创建BookShelfBean对象 BookShelfBean entity = new BookShelfBean( // - cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0), // noteUrl - cursor.getInt(offset + 1), // durChapter - cursor.getInt(offset + 2), // durChapterPage - cursor.getLong(offset + 3), // finalDate - cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4) // tag + cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0), // noteUrl + cursor.getInt(offset + 1), // durChapter + cursor.getInt(offset + 2), // durChapterPage + cursor.getLong(offset + 3), // finalDate + cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4) // tag ); + // 返回创建好的BookShelfBean对象 return entity; } - + @Override + // 将游标中的数据读取到已存在的BookShelfBean实体中 public void readEntity(Cursor cursor, BookShelfBean entity, int offset) { - entity.setNoteUrl(cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0)); - entity.setDurChapter(cursor.getInt(offset + 1)); - entity.setDurChapterPage(cursor.getInt(offset + 2)); - entity.setFinalDate(cursor.getLong(offset + 3)); - entity.setTag(cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4)); - } - + entity.setNoteUrl(cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0)); // noteUrl + entity.setDurChapter(cursor.getInt(offset + 1)); // durChapter + entity.setDurChapterPage(cursor.getInt(offset + 2)); // durChapterPage + entity.setFinalDate(cursor.getLong(offset + 3)); // finalDate + entity.setTag(cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4)); // tag + } + @Override + // 插入数据后更新主键 protected final String updateKeyAfterInsert(BookShelfBean entity, long rowId) { + // 返回noteUrl作为主键 return entity.getNoteUrl(); } - + @Override + // 获取主键 public String getKey(BookShelfBean entity) { if(entity != null) { + // 返回noteUrl作为主键 return entity.getNoteUrl(); } else { return null; @@ -134,8 +165,10 @@ public class BookShelfBeanDao extends AbstractDao { } @Override + // 判断实体是否可更新 protected final boolean isEntityUpdateable() { + // 返回true,表示实体可更新 return true; } - + } diff --git a/app/src/main/java/com/monke/monkeybook/dao/ChapterListBeanDao.java b/app/src/main/java/com/monke/monkeybook/dao/ChapterListBeanDao.java index d883f64..fc4de8e 100644 --- a/app/src/main/java/com/monke/monkeybook/dao/ChapterListBeanDao.java +++ b/app/src/main/java/com/monke/monkeybook/dao/ChapterListBeanDao.java @@ -1,162 +1,200 @@ +// 包声明 package com.monke.monkeybook.dao; -import android.database.Cursor; -import android.database.sqlite.SQLiteStatement; +import android.database.Cursor; // 导入Cursor类,用于从数据库游标中读取数据 +import android.database.sqlite.SQLiteStatement; // 导入SQLiteStatement类,用于执行预编译的SQL语句 -import org.greenrobot.greendao.AbstractDao; -import org.greenrobot.greendao.Property; -import org.greenrobot.greendao.internal.DaoConfig; -import org.greenrobot.greendao.database.Database; -import org.greenrobot.greendao.database.DatabaseStatement; +import org.greenrobot.greendao.AbstractDao; // 导入AbstractDao类,GreenDao框架中用于数据库操作的抽象基类 +import org.greenrobot.greendao.Property; // 导入Property类,用于定义数据库表字段的属性 +import org.greenrobot.greendao.internal.DaoConfig; // 导入DaoConfig类,用于配置Dao对象的配置信息 +import org.greenrobot.greendao.database.Database; // 导入Database类,GreenDao框架中用于数据库操作的类 +import org.greenrobot.greendao.database.DatabaseStatement; // 导入DatabaseStatement类,用于执行SQL语句 +// 导入ChapterListBean类,这是数据库操作的对象 import com.monke.monkeybook.bean.ChapterListBean; // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. -/** +/** * DAO for table "CHAPTER_LIST_BEAN". -*/ + */ +// ChapterListBeanDao类继承AbstractDao,操作ChapterListBean对象,主键类型为String public class ChapterListBeanDao extends AbstractDao { - + // 数据库表名常量 public static final String TABLENAME = "CHAPTER_LIST_BEAN"; /** * Properties of entity ChapterListBean.
* Can be used for QueryBuilder and for referencing column names. - */ + */ public static class Properties { - public final static Property NoteUrl = new Property(0, String.class, "noteUrl", false, "NOTE_URL"); - public final static Property DurChapterIndex = new Property(1, int.class, "durChapterIndex", false, "DUR_CHAPTER_INDEX"); - public final static Property DurChapterUrl = new Property(2, String.class, "durChapterUrl", true, "DUR_CHAPTER_URL"); - public final static Property DurChapterName = new Property(3, String.class, "durChapterName", false, "DUR_CHAPTER_NAME"); - public final static Property Tag = new Property(4, String.class, "tag", false, "TAG"); - public final static Property HasCache = new Property(5, Boolean.class, "hasCache", false, "HAS_CACHE"); + public final static Property NoteUrl = new Property(0, String.class, "noteUrl", false, "NOTE_URL"); // 属性0:noteUrl,String类型,非主键,数据库列名NOTE_URL + public final static Property DurChapterIndex = new Property(1, int.class, "durChapterIndex", false, "DUR_CHAPTER_INDEX"); // 属性1:durChapterIndex,int类型,非主键,数据库列名DUR_CHAPTER_INDEX + public final static Property DurChapterUrl = new Property(2, String.class, "durChapterUrl", true, "DUR_CHAPTER_URL"); // 属性2:durChapterUrl,String类型,主键,数据库列名DUR_CHAPTER_URL + public final static Property DurChapterName = new Property(3, String.class, "durChapterName", false, "DUR_CHAPTER_NAME"); // 属性3:durChapterName,String类型,非主键,数据库列名DUR_CHAPTER_NAME + public final static Property Tag = new Property(4, String.class, "tag", false, "TAG"); // 属性4:tag,String类型,非主键,数据库列名TAG + public final static Property HasCache = new Property(5, Boolean.class, "hasCache", false, "HAS_CACHE"); // 属性5:hasCache,Boolean类型,非主键,数据库列名HAS_CACHE }; - + // 构造函数,使用DaoConfig配置初始化Dao public ChapterListBeanDao(DaoConfig config) { super(config); } - + + // 构造函数,使用DaoConfig和DaoSession配置初始化Dao public ChapterListBeanDao(DaoConfig config, DaoSession daoSession) { super(config, daoSession); } /** Creates the underlying database table. */ + // 创建CHAPTER_LIST_BEAN表的方法 public static void createTable(Database db, boolean ifNotExists) { + // 根据ifNotExists判断是否需要添加"IF NOT EXISTS"条件 String constraint = ifNotExists? "IF NOT EXISTS ": ""; + // 执行SQL语句创建表 db.execSQL("CREATE TABLE " + constraint + "\"CHAPTER_LIST_BEAN\" (" + // - "\"NOTE_URL\" TEXT," + // 0: noteUrl - "\"DUR_CHAPTER_INDEX\" INTEGER NOT NULL ," + // 1: durChapterIndex - "\"DUR_CHAPTER_URL\" TEXT PRIMARY KEY NOT NULL ," + // 2: durChapterUrl - "\"DUR_CHAPTER_NAME\" TEXT," + // 3: durChapterName - "\"TAG\" TEXT," + // 4: tag - "\"HAS_CACHE\" INTEGER);"); // 5: hasCache + "\"NOTE_URL\" TEXT," + // NOTE_URL字段,TEXT类型 + "\"DUR_CHAPTER_INDEX\" INTEGER NOT NULL ," + // DUR_CHAPTER_INDEX字段,INTEGER类型,非空 + "\"DUR_CHAPTER_URL\" TEXT PRIMARY KEY NOT NULL ," + // DUR_CHAPTER_URL字段,TEXT类型,主键,非空 + "\"DUR_CHAPTER_NAME\" TEXT," + // DUR_CHAPTER_NAME字段,TEXT类型 + "\"TAG\" TEXT," + // TAG字段,TEXT类型 + "\"HAS_CACHE\" INTEGER);"); // HAS_CACHE字段,INTEGER类型 } - /** Drops the underlying database table. */ + /** Drops the underlying database table. + * 删除数据库表 + */ + // 删除CHAPTER_LIST_BEAN表的方法 public static void dropTable(Database db, boolean ifExists) { + // 构造删除表的SQL语句 String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"CHAPTER_LIST_BEAN\""; + // 执行SQL语句删除表 db.execSQL(sql); } @Override + // 将ChapterListBean实体对象的值绑定到DatabaseStatement语句中 protected final void bindValues(DatabaseStatement stmt, ChapterListBean entity) { + // 清除之前的绑定 stmt.clearBindings(); - + String noteUrl = entity.getNoteUrl(); if (noteUrl != null) { + // 绑定noteUrl值到第一个参数位置 stmt.bindString(1, noteUrl); } + // 绑定durChapterIndex值到第二个参数位置 stmt.bindLong(2, entity.getDurChapterIndex()); - + String durChapterUrl = entity.getDurChapterUrl(); if (durChapterUrl != null) { + // 绑定durChapterUrl值到第三个参数位置 stmt.bindString(3, durChapterUrl); } - + String durChapterName = entity.getDurChapterName(); if (durChapterName != null) { + // 绑定durChapterName值到第四个参数位置 stmt.bindString(4, durChapterName); } - + String tag = entity.getTag(); if (tag != null) { + // 绑定tag值到第五个参数位置 stmt.bindString(5, tag); } - + Boolean hasCache = entity.getHasCache(); if (hasCache != null) { + // 绑定hasCache值到第六个参数位置,true为1,false为0 stmt.bindLong(6, hasCache ? 1L: 0L); } } @Override + // 将ChapterListBean实体对象的值绑定到SQLiteStatement语句中,与上一个方法功能类似,只是对象不同 protected final void bindValues(SQLiteStatement stmt, ChapterListBean entity) { + // 清除之前的绑定 stmt.clearBindings(); - + String noteUrl = entity.getNoteUrl(); if (noteUrl != null) { + // 绑定noteUrl值到第一个参数位置 stmt.bindString(1, noteUrl); } + // 绑定durChapterIndex值到第二个参数位置 stmt.bindLong(2, entity.getDurChapterIndex()); - + String durChapterUrl = entity.getDurChapterUrl(); if (durChapterUrl != null) { + // 绑定durChapterUrl值到第三个参数位置 stmt.bindString(3, durChapterUrl); } - + String durChapterName = entity.getDurChapterName(); if (durChapterName != null) { + // 绑定durChapterName值到第四个参数位置 stmt.bindString(4, durChapterName); } - + String tag = entity.getTag(); if (tag != null) { + // 绑定tag值到第五个参数位置 stmt.bindString(5, tag); } - + Boolean hasCache = entity.getHasCache(); if (hasCache != null) { + // 绑定hasCache值到第六个参数位置,true为1,false为0 stmt.bindLong(6, hasCache ? 1L: 0L); } } @Override + // 从游标中读取主键 public String readKey(Cursor cursor, int offset) { + // 从偏移量offset+2的位置读取主键值,如果为空返回null,否则返回字符串 return cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2); - } + } @Override + // 从游标中读取ChapterListBean实体 public ChapterListBean readEntity(Cursor cursor, int offset) { + // 创建ChapterListBean对象 ChapterListBean entity = new ChapterListBean( // - cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0), // noteUrl - cursor.getInt(offset + 1), // durChapterIndex - cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // durChapterUrl - cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3), // durChapterName - cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4), // tag - cursor.isNull(offset + 5) ? null : cursor.getShort(offset + 5) != 0 // hasCache + cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0), // noteUrl + cursor.getInt(offset + 1), // durChapterIndex + cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // durChapterUrl + cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3), // durChapterName + cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4), // tag + cursor.isNull(offset + 5) ? null : cursor.getShort(offset + 5) != 0 // hasCache 将short转换为boolean ); + // 返回创建好的ChapterListBean对象 return entity; } - + @Override + // 将游标中的数据读取到已存在的ChapterListBean实体中 public void readEntity(Cursor cursor, ChapterListBean entity, int offset) { - entity.setNoteUrl(cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0)); - entity.setDurChapterIndex(cursor.getInt(offset + 1)); - entity.setDurChapterUrl(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2)); - entity.setDurChapterName(cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3)); - entity.setTag(cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4)); - entity.setHasCache(cursor.isNull(offset + 5) ? null : cursor.getShort(offset + 5) != 0); - } - + entity.setNoteUrl(cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0)); // noteUrl + entity.setDurChapterIndex(cursor.getInt(offset + 1)); // durChapterIndex + entity.setDurChapterUrl(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2)); // durChapterUrl + entity.setDurChapterName(cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3)); // durChapterName + entity.setTag(cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4)); // tag + entity.setHasCache(cursor.isNull(offset + 5) ? null : cursor.getShort(offset + 5) != 0); // hasCache 将short转换为boolean + } + @Override + // 插入数据后更新主键 protected final String updateKeyAfterInsert(ChapterListBean entity, long rowId) { + // 返回durChapterUrl作为主键 return entity.getDurChapterUrl(); } - + @Override + // 获取主键 public String getKey(ChapterListBean entity) { if(entity != null) { + // 返回durChapterUrl作为主键 return entity.getDurChapterUrl(); } else { return null; @@ -164,8 +202,10 @@ public class ChapterListBeanDao extends AbstractDao { } @Override + // 判断实体是否可更新 protected final boolean isEntityUpdateable() { + // 返回true,表示实体可更新 return true; } - + } diff --git a/app/src/main/java/com/monke/monkeybook/dao/DaoMaster.java b/app/src/main/java/com/monke/monkeybook/dao/DaoMaster.java index d302a59..fa361b4 100644 --- a/app/src/main/java/com/monke/monkeybook/dao/DaoMaster.java +++ b/app/src/main/java/com/monke/monkeybook/dao/DaoMaster.java @@ -1,109 +1,142 @@ +// 包名声明 package com.monke.monkeybook.dao; -import android.content.Context; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteDatabase.CursorFactory; -import android.util.Log; +import android.content.Context; // 导入Context类,用于获取应用程序上下文 +import android.database.sqlite.SQLiteDatabase; // 导入SQLiteDatabase类,用于操作数据库 +import android.database.sqlite.SQLiteDatabase.CursorFactory; // 导入CursorFactory类,用于创建游标工厂 +import android.util.Log; // 导入Log类,用于输出日志信息 -import org.greenrobot.greendao.AbstractDaoMaster; -import org.greenrobot.greendao.database.StandardDatabase; -import org.greenrobot.greendao.database.Database; -import org.greenrobot.greendao.database.DatabaseOpenHelper; -import org.greenrobot.greendao.identityscope.IdentityScopeType; +import org.greenrobot.greendao.AbstractDaoMaster; // 导入AbstractDaoMaster类,GreenDao框架中用于管理DAO的抽象基类 +import org.greenrobot.greendao.database.StandardDatabase; // 导入StandardDatabase类,GreenDao框架中基于SQLite的数据库实现 +import org.greenrobot.greendao.database.Database; // 导入Database类,GreenDao框架中用于数据库操作的接口 +import org.greenrobot.greendao.database.DatabaseOpenHelper; // 导入DatabaseOpenHelper类,GreenDao框架中用于打开和管理数据库的帮助类 +import org.greenrobot.greendao.identityscope.IdentityScopeType; // 导入IdentityScopeType类,GreenDao框架中用于指定标识符作用域类型的枚举 // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. /** * Master of DAO (schema version 1): knows all DAOs. */ +// DaoMaster类继承AbstractDaoMaster,是所有DAO的管理者 public class DaoMaster extends AbstractDaoMaster { + // 数据库模式版本号 public static final int SCHEMA_VERSION = 1; /** Creates underlying database table using DAOs. */ + // 创建所有数据库表的方法 public static void createAllTables(Database db, boolean ifNotExists) { - BookContentBeanDao.createTable(db, ifNotExists); - BookInfoBeanDao.createTable(db, ifNotExists); - BookShelfBeanDao.createTable(db, ifNotExists); - ChapterListBeanDao.createTable(db, ifNotExists); - DownloadChapterBeanDao.createTable(db, ifNotExists); - SearchHistoryBeanDao.createTable(db, ifNotExists); + BookContentBeanDao.createTable(db, ifNotExists); // 创建BookContentBean表 + BookInfoBeanDao.createTable(db, ifNotExists); // 创建BookInfoBean表 + BookShelfBeanDao.createTable(db, ifNotExists); // 创建BookShelfBean表 + ChapterListBeanDao.createTable(db, ifNotExists); // 创建ChapterListBean表 + DownloadChapterBeanDao.createTable(db, ifNotExists); // 创建DownloadChapterBean表 + SearchHistoryBeanDao.createTable(db, ifNotExists); // 创建SearchHistoryBean表 } /** Drops underlying database table using DAOs. */ + // 删除所有数据库表的方法 public static void dropAllTables(Database db, boolean ifExists) { - BookContentBeanDao.dropTable(db, ifExists); - BookInfoBeanDao.dropTable(db, ifExists); - BookShelfBeanDao.dropTable(db, ifExists); - ChapterListBeanDao.dropTable(db, ifExists); - DownloadChapterBeanDao.dropTable(db, ifExists); - SearchHistoryBeanDao.dropTable(db, ifExists); + BookContentBeanDao.dropTable(db, ifExists); // 删除BookContentBean表 + BookInfoBeanDao.dropTable(db, ifExists); // 删除BookInfoBean表 + BookShelfBeanDao.dropTable(db, ifExists); // 删除BookShelfBean表 + ChapterListBeanDao.dropTable(db, ifExists); // 删除ChapterListBean表 + DownloadChapterBeanDao.dropTable(db, ifExists); // 删除DownloadChapterBean表 + SearchHistoryBeanDao.dropTable(db, ifExists); // 删除SearchHistoryBean表 } /** * WARNING: Drops all table on Upgrade! Use only during development. * Convenience method using a {@link DevOpenHelper}. */ + // 创建一个开发模式的DaoSession public static DaoSession newDevSession(Context context, String name) { + // 获取可写的数据库 Database db = new DevOpenHelper(context, name).getWritableDb(); + // 创建DaoMaster实例 DaoMaster daoMaster = new DaoMaster(db); + // 创建DaoSession实例并返回 return daoMaster.newSession(); } + // 构造函数,使用SQLiteDatabase对象初始化DaoMaster public DaoMaster(SQLiteDatabase db) { + // 使用StandardDatabase包装SQLiteDatabase this(new StandardDatabase(db)); } + // 构造函数,使用Database对象初始化DaoMaster public DaoMaster(Database db) { + // 调用父类的构造函数 super(db, SCHEMA_VERSION); - registerDaoClass(BookContentBeanDao.class); - registerDaoClass(BookInfoBeanDao.class); - registerDaoClass(BookShelfBeanDao.class); - registerDaoClass(ChapterListBeanDao.class); - registerDaoClass(DownloadChapterBeanDao.class); - registerDaoClass(SearchHistoryBeanDao.class); + registerDaoClass(BookContentBeanDao.class); // 注册BookContentBeanDao + registerDaoClass(BookInfoBeanDao.class); // 注册BookInfoBeanDao + registerDaoClass(BookShelfBeanDao.class); // 注册BookShelfBeanDao + registerDaoClass(ChapterListBeanDao.class); // 注册ChapterListBeanDao + registerDaoClass(DownloadChapterBeanDao.class); // 注册DownloadChapterBeanDao + registerDaoClass(SearchHistoryBeanDao.class); // 注册SearchHistoryBeanDao } + // 创建一个新的DaoSession,使用默认的IdentityScopeType.Session public DaoSession newSession() { + // 创建DaoSession实例 return new DaoSession(db, IdentityScopeType.Session, daoConfigMap); } + // 创建一个新的DaoSession,使用指定的IdentityScopeType public DaoSession newSession(IdentityScopeType type) { + // 创建DaoSession实例 return new DaoSession(db, type, daoConfigMap); } /** * Calls {@link #createAllTables(Database, boolean)} in {@link #onCreate(Database)} - */ + // OpenHelper抽象类,继承DatabaseOpenHelper,用于创建和打开数据库 public static abstract class OpenHelper extends DatabaseOpenHelper { + // 构造函数,指定上下文和数据库名称 public OpenHelper(Context context, String name) { + // 调用父类的构造函数 super(context, name, SCHEMA_VERSION); } - + // 构造函数,指定上下文、数据库名称和游标工厂 public OpenHelper(Context context, String name, CursorFactory factory) { + // 调用父类的构造函数 super(context, name, factory, SCHEMA_VERSION); } @Override + // 创建数据库时调用 public void onCreate(Database db) { + // 输出日志信息 Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION); + // 创建所有数据库表 createAllTables(db, false); } } /** WARNING: Drops all table on Upgrade! Use only during development. */ + // DevOpenHelper类,继承OpenHelper,用于开发阶段的数据库升级 public static class DevOpenHelper extends OpenHelper { + // 构造函数,指定上下文和数据库名称 public DevOpenHelper(Context context, String name) { + // 调用父类的构造函数 super(context, name); } + // 构造函数,指定上下文、数据库名称和游标工厂 public DevOpenHelper(Context context, String name, CursorFactory factory) { + // 调用父类的构造函数 super(context, name, factory); } @Override + // 数据库升级时调用 public void onUpgrade(Database db, int oldVersion, int newVersion) { + // 输出日志信息 Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables"); + // 删除所有数据库表 dropAllTables(db, true); + // 重新创建数据库表 onCreate(db); } } diff --git a/app/src/main/java/com/monke/monkeybook/dao/DaoSession.java b/app/src/main/java/com/monke/monkeybook/dao/DaoSession.java index 4b6ffbf..c2dc7f4 100644 --- a/app/src/main/java/com/monke/monkeybook/dao/DaoSession.java +++ b/app/src/main/java/com/monke/monkeybook/dao/DaoSession.java @@ -1,118 +1,121 @@ +// 包名声明 package com.monke.monkeybook.dao; - +// 导入Map接口,用于存储键值对 import java.util.Map; -import org.greenrobot.greendao.AbstractDao; -import org.greenrobot.greendao.AbstractDaoSession; -import org.greenrobot.greendao.database.Database; -import org.greenrobot.greendao.identityscope.IdentityScopeType; -import org.greenrobot.greendao.internal.DaoConfig; - -import com.monke.monkeybook.bean.BookContentBean; -import com.monke.monkeybook.bean.BookInfoBean; -import com.monke.monkeybook.bean.BookShelfBean; -import com.monke.monkeybook.bean.ChapterListBean; -import com.monke.monkeybook.bean.DownloadChapterBean; -import com.monke.monkeybook.bean.SearchHistoryBean; - -import com.monke.monkeybook.dao.BookContentBeanDao; -import com.monke.monkeybook.dao.BookInfoBeanDao; -import com.monke.monkeybook.dao.BookShelfBeanDao; -import com.monke.monkeybook.dao.ChapterListBeanDao; -import com.monke.monkeybook.dao.DownloadChapterBeanDao; -import com.monke.monkeybook.dao.SearchHistoryBeanDao; +import org.greenrobot.greendao.AbstractDao; // 导入AbstractDao类,GreenDao框架中数据库操作的抽象基类 +import org.greenrobot.greendao.AbstractDaoSession; // 导入AbstractDaoSession类,GreenDao框架中DAO会话的抽象基类 +import org.greenrobot.greendao.database.Database; // 导入Database类,GreenDao框架中数据库操作的接口 +import org.greenrobot.greendao.identityscope.IdentityScopeType; // 导入IdentityScopeType类,GreenDao框架中用于指定标识符作用域类型的枚举 +import org.greenrobot.greendao.internal.DaoConfig; // 导入DaoConfig类,GreenDao框架中用于配置Dao对象的配置信息 + +import com.monke.monkeybook.bean.BookContentBean; // 导入BookContentBean类,书籍内容Bean +import com.monke.monkeybook.bean.BookInfoBean; // 导入BookInfoBean类,书籍信息Bean +import com.monke.monkeybook.bean.BookShelfBean; // 导入BookShelfBean类,书架Bean +import com.monke.monkeybook.bean.ChapterListBean; // 导入ChapterListBean类,章节列表Bean +import com.monke.monkeybook.bean.DownloadChapterBean; // 导入DownloadChapterBean类,下载章节Bean +import com.monke.monkeybook.bean.SearchHistoryBean; // 导入SearchHistoryBean类,搜索历史Bean + +import com.monke.monkeybook.dao.BookContentBeanDao; // 导入BookContentBeanDao类 +import com.monke.monkeybook.dao.BookInfoBeanDao; // 导入BookInfoBeanDao类 +import com.monke.monkeybook.dao.BookShelfBeanDao; // 导入BookShelfBeanDao类 +import com.monke.monkeybook.dao.ChapterListBeanDao; // 导入ChapterListBeanDao类 +import com.monke.monkeybook.dao.DownloadChapterBeanDao; // 导入DownloadChapterBeanDao类 +import com.monke.monkeybook.dao.SearchHistoryBeanDao; // 导入SearchHistoryBeanDao类 // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. /** * {@inheritDoc} - * + * * @see org.greenrobot.greendao.AbstractDaoSession */ +// DaoSession类继承AbstractDaoSession,管理数据库会话 public class DaoSession extends AbstractDaoSession { - private final DaoConfig bookContentBeanDaoConfig; - private final DaoConfig bookInfoBeanDaoConfig; - private final DaoConfig bookShelfBeanDaoConfig; - private final DaoConfig chapterListBeanDaoConfig; - private final DaoConfig downloadChapterBeanDaoConfig; - private final DaoConfig searchHistoryBeanDaoConfig; + private final DaoConfig bookContentBeanDaoConfig; // BookContentBeanDao的配置信息 + private final DaoConfig bookInfoBeanDaoConfig; // BookInfoBeanDao的配置信息 + private final DaoConfig bookShelfBeanDaoConfig; // BookShelfBeanDao的配置信息 + private final DaoConfig chapterListBeanDaoConfig; // ChapterListBeanDao的配置信息 + private final DaoConfig downloadChapterBeanDaoConfig; // DownloadChapterBeanDao的配置信息 + private final DaoConfig searchHistoryBeanDaoConfig; // SearchHistoryBeanDao的配置信息 - private final BookContentBeanDao bookContentBeanDao; - private final BookInfoBeanDao bookInfoBeanDao; - private final BookShelfBeanDao bookShelfBeanDao; - private final ChapterListBeanDao chapterListBeanDao; - private final DownloadChapterBeanDao downloadChapterBeanDao; - private final SearchHistoryBeanDao searchHistoryBeanDao; + private final BookContentBeanDao bookContentBeanDao; // BookContentBeanDao实例 + private final BookInfoBeanDao bookInfoBeanDao; // BookInfoBeanDao实例 + private final BookShelfBeanDao bookShelfBeanDao; // BookShelfBeanDao实例 + private final ChapterListBeanDao chapterListBeanDao; // ChapterListBeanDao实例 + private final DownloadChapterBeanDao downloadChapterBeanDao; // DownloadChapterBeanDao实例 + private final SearchHistoryBeanDao searchHistoryBeanDao; // SearchHistoryBeanDao实例 public DaoSession(Database db, IdentityScopeType type, Map>, DaoConfig> - daoConfigMap) { + daoConfigMap) {// DaoSession的构造函数,传入Database、IdentityScopeType和DaoConfig Map + // 调用父类的构造函数 super(db); - bookContentBeanDaoConfig = daoConfigMap.get(BookContentBeanDao.class).clone(); - bookContentBeanDaoConfig.initIdentityScope(type); + bookContentBeanDaoConfig = daoConfigMap.get(BookContentBeanDao.class).clone(); // 从map中获取BookContentBeanDao的配置信息并克隆 + bookContentBeanDaoConfig.initIdentityScope(type); // 初始化BookContentBeanDao的标识符作用域 - bookInfoBeanDaoConfig = daoConfigMap.get(BookInfoBeanDao.class).clone(); - bookInfoBeanDaoConfig.initIdentityScope(type); + bookInfoBeanDaoConfig = daoConfigMap.get(BookInfoBeanDao.class).clone(); // 从map中获取BookInfoBeanDao的配置信息并克隆 + bookInfoBeanDaoConfig.initIdentityScope(type); // 初始化BookInfoBeanDao的标识符作用域 - bookShelfBeanDaoConfig = daoConfigMap.get(BookShelfBeanDao.class).clone(); - bookShelfBeanDaoConfig.initIdentityScope(type); + bookShelfBeanDaoConfig = daoConfigMap.get(BookShelfBeanDao.class).clone(); // 从map中获取BookShelfBeanDao的配置信息并克隆 + bookShelfBeanDaoConfig.initIdentityScope(type); // 初始化BookShelfBeanDao的标识符作用域 - chapterListBeanDaoConfig = daoConfigMap.get(ChapterListBeanDao.class).clone(); - chapterListBeanDaoConfig.initIdentityScope(type); + chapterListBeanDaoConfig = daoConfigMap.get(ChapterListBeanDao.class).clone(); // 从map中获取ChapterListBeanDao的配置信息并克隆 + chapterListBeanDaoConfig.initIdentityScope(type); // 初始化ChapterListBeanDao的标识符作用域 - downloadChapterBeanDaoConfig = daoConfigMap.get(DownloadChapterBeanDao.class).clone(); - downloadChapterBeanDaoConfig.initIdentityScope(type); + downloadChapterBeanDaoConfig = daoConfigMap.get(DownloadChapterBeanDao.class).clone(); // 从map中获取DownloadChapterBeanDao的配置信息并克隆 + downloadChapterBeanDaoConfig.initIdentityScope(type); // 初始化DownloadChapterBeanDao的标识符作用域 - searchHistoryBeanDaoConfig = daoConfigMap.get(SearchHistoryBeanDao.class).clone(); - searchHistoryBeanDaoConfig.initIdentityScope(type); + searchHistoryBeanDaoConfig = daoConfigMap.get(SearchHistoryBeanDao.class).clone(); // 从map中获取SearchHistoryBeanDao的配置信息并克隆 + searchHistoryBeanDaoConfig.initIdentityScope(type); // 初始化SearchHistoryBeanDao的标识符作用域 - bookContentBeanDao = new BookContentBeanDao(bookContentBeanDaoConfig, this); - bookInfoBeanDao = new BookInfoBeanDao(bookInfoBeanDaoConfig, this); - bookShelfBeanDao = new BookShelfBeanDao(bookShelfBeanDaoConfig, this); - chapterListBeanDao = new ChapterListBeanDao(chapterListBeanDaoConfig, this); - downloadChapterBeanDao = new DownloadChapterBeanDao(downloadChapterBeanDaoConfig, this); - searchHistoryBeanDao = new SearchHistoryBeanDao(searchHistoryBeanDaoConfig, this); + bookContentBeanDao = new BookContentBeanDao(bookContentBeanDaoConfig, this); // 创建BookContentBeanDao实例 + bookInfoBeanDao = new BookInfoBeanDao(bookInfoBeanDaoConfig, this); // 创建BookInfoBeanDao实例 + bookShelfBeanDao = new BookShelfBeanDao(bookShelfBeanDaoConfig, this); // 创建BookShelfBeanDao实例 + chapterListBeanDao = new ChapterListBeanDao(chapterListBeanDaoConfig, this); // 创建ChapterListBeanDao实例 + downloadChapterBeanDao = new DownloadChapterBeanDao(downloadChapterBeanDaoConfig, this); // 创建DownloadChapterBeanDao实例 + searchHistoryBeanDao = new SearchHistoryBeanDao(searchHistoryBeanDaoConfig, this); // 创建SearchHistoryBeanDao实例 - registerDao(BookContentBean.class, bookContentBeanDao); - registerDao(BookInfoBean.class, bookInfoBeanDao); - registerDao(BookShelfBean.class, bookShelfBeanDao); - registerDao(ChapterListBean.class, chapterListBeanDao); - registerDao(DownloadChapterBean.class, downloadChapterBeanDao); - registerDao(SearchHistoryBean.class, searchHistoryBeanDao); + registerDao(BookContentBean.class, bookContentBeanDao); // 注册BookContentBeanDao + registerDao(BookInfoBean.class, bookInfoBeanDao); // 注册BookInfoBeanDao + registerDao(BookShelfBean.class, bookShelfBeanDao); // 注册BookShelfBeanDao + registerDao(ChapterListBean.class, chapterListBeanDao); // 注册ChapterListBeanDao + registerDao(DownloadChapterBean.class, downloadChapterBeanDao); // 注册DownloadChapterBeanDao + registerDao(SearchHistoryBean.class, searchHistoryBeanDao); // 注册SearchHistoryBeanDao } - - public void clear() { - bookContentBeanDaoConfig.getIdentityScope().clear(); - bookInfoBeanDaoConfig.getIdentityScope().clear(); - bookShelfBeanDaoConfig.getIdentityScope().clear(); - chapterListBeanDaoConfig.getIdentityScope().clear(); - downloadChapterBeanDaoConfig.getIdentityScope().clear(); - searchHistoryBeanDaoConfig.getIdentityScope().clear(); + + public void clear() {// 清除所有DAO的标识符作用域 + bookContentBeanDaoConfig.getIdentityScope().clear(); // 清除BookContentBeanDao的标识符作用域 + bookInfoBeanDaoConfig.getIdentityScope().clear(); // 清除BookInfoBeanDao的标识符作用域 + bookShelfBeanDaoConfig.getIdentityScope().clear(); // 清除BookShelfBeanDao的标识符作用域 + chapterListBeanDaoConfig.getIdentityScope().clear(); // 清除ChapterListBeanDao的标识符作用域 + downloadChapterBeanDaoConfig.getIdentityScope().clear(); // 清除DownloadChapterBeanDao的标识符作用域 + searchHistoryBeanDaoConfig.getIdentityScope().clear(); // 清除SearchHistoryBeanDao的标识符作用域 } - public BookContentBeanDao getBookContentBeanDao() { - return bookContentBeanDao; + public BookContentBeanDao getBookContentBeanDao() {// 获取BookContentBeanDao实例 + return bookContentBeanDao;// 返回BookContentBeanDao实例 } - public BookInfoBeanDao getBookInfoBeanDao() { - return bookInfoBeanDao; + public BookInfoBeanDao getBookInfoBeanDao() {// 获取BookInfoBeanDao实例 + return bookInfoBeanDao;// 返回BookInfoBeanDao实例 } - public BookShelfBeanDao getBookShelfBeanDao() { - return bookShelfBeanDao; + public BookShelfBeanDao getBookShelfBeanDao() {// 获取BookShelfBeanDao实例 + return bookShelfBeanDao;// 返回BookShelfBeanDao实例 } - public ChapterListBeanDao getChapterListBeanDao() { - return chapterListBeanDao; + public ChapterListBeanDao getChapterListBeanDao() {// 获取ChapterListBeanDao实例 + return chapterListBeanDao;// 返回ChapterListBeanDao实例 } - public DownloadChapterBeanDao getDownloadChapterBeanDao() { - return downloadChapterBeanDao; + public DownloadChapterBeanDao getDownloadChapterBeanDao() {// 获取DownloadChapterBeanDao实例 + return downloadChapterBeanDao;// 返回DownloadChapterBeanDao实例 } - public SearchHistoryBeanDao getSearchHistoryBeanDao() { - return searchHistoryBeanDao; + public SearchHistoryBeanDao getSearchHistoryBeanDao() {// 获取SearchHistoryBeanDao实例 + return searchHistoryBeanDao;// 返回SearchHistoryBeanDao实例 } } -- 2.34.1 From 39250c47dfff75b628acfbaca9bf3a714822e657 Mon Sep 17 00:00:00 2001 From: SYH <1017401752@qq.com> Date: Tue, 17 Dec 2024 00:13:50 +0800 Subject: [PATCH 5/8] 1 --- .../com/monke/monkeybook/dao/DbHelper.java | 41 ++-- .../dao/DownloadChapterBeanDao.java | 206 +++++++++++------- .../monkeybook/dao/SearchHistoryBeanDao.java | 149 ++++++++----- 3 files changed, 247 insertions(+), 149 deletions(-) diff --git a/app/src/main/java/com/monke/monkeybook/dao/DbHelper.java b/app/src/main/java/com/monke/monkeybook/dao/DbHelper.java index d9f3154..a50a4a7 100644 --- a/app/src/main/java/com/monke/monkeybook/dao/DbHelper.java +++ b/app/src/main/java/com/monke/monkeybook/dao/DbHelper.java @@ -1,41 +1,48 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +// 包名声明 package com.monke.monkeybook.dao; - +// 导入SQLiteDatabase类,用于操作数据库 import android.database.sqlite.SQLiteDatabase; +// 导入MApplication类,用于获取应用上下文 import com.monke.monkeybook.MApplication; -public class DbHelper { - private DaoMaster.DevOpenHelper mHelper; - private SQLiteDatabase db; - private DaoMaster mDaoMaster; - private DaoSession mDaoSession; +public class DbHelper {// DbHelper类,用于管理GreenDao数据库连接 + private DaoMaster.DevOpenHelper mHelper; // DaoMaster.DevOpenHelper实例,用于创建和管理数据库 + private SQLiteDatabase db; // SQLiteDatabase实例,用于数据库操作 + private DaoMaster mDaoMaster; // DaoMaster实例,用于管理DAO + private DaoSession mDaoSession; // DaoSession实例,用于数据库会话 - private DbHelper(){ + private DbHelper(){// 私有构造方法,采用单例模式 + // 创建DaoMaster.DevOpenHelper实例,指定数据库名称为"monkebook_db",游标工厂为null mHelper = new DaoMaster.DevOpenHelper(MApplication.getInstance(), "monkebook_db", null); + // 获取可写的SQLiteDatabase实例 db = mHelper.getWritableDatabase(); // 注意:该数据库连接属于 DaoMaster,所以多个 Session 指的是相同的数据库连接。 - mDaoMaster = new DaoMaster(db); - mDaoSession = mDaoMaster.newSession(); + mDaoMaster = new DaoMaster(db);// 创建DaoMaster实例,使用获取的SQLiteDatabase实例 + mDaoSession = mDaoMaster.newSession();// 创建DaoSession实例 } + // DbHelper的单例实例 private static DbHelper instance; - public static DbHelper getInstance(){ - if(null == instance){ - synchronized (DbHelper.class){ - if(null == instance){ - instance = new DbHelper(); + public static DbHelper getInstance(){// 获取DbHelper单例实例的方法 + if(null == instance){// 判断单例实例是否为空 + synchronized (DbHelper.class){// 同步块,保证线程安全 + if(null == instance){// 再次判断单例实例是否为空,避免重复创建 + instance = new DbHelper();// 创建DbHelper实例 } } } + // 返回单例实例 return instance; } - public DaoSession getmDaoSession() { + public DaoSession getmDaoSession() {// 获取DaoSession实例的方法 + // 返回DaoSession实例 return mDaoSession; } - public SQLiteDatabase getDb() { + public SQLiteDatabase getDb() { // 获取SQLiteDatabase实例的方法 + // 返回SQLiteDatabase实例 return db; } } diff --git a/app/src/main/java/com/monke/monkeybook/dao/DownloadChapterBeanDao.java b/app/src/main/java/com/monke/monkeybook/dao/DownloadChapterBeanDao.java index b13abc0..e816e52 100644 --- a/app/src/main/java/com/monke/monkeybook/dao/DownloadChapterBeanDao.java +++ b/app/src/main/java/com/monke/monkeybook/dao/DownloadChapterBeanDao.java @@ -1,185 +1,241 @@ +// 包名声明 package com.monke.monkeybook.dao; -import android.database.Cursor; -import android.database.sqlite.SQLiteStatement; +import android.database.Cursor; // 导入Cursor类,用于读取数据库查询结果 +import android.database.sqlite.SQLiteStatement; // 导入SQLiteStatement类,用于执行SQL语句 -import org.greenrobot.greendao.AbstractDao; -import org.greenrobot.greendao.Property; -import org.greenrobot.greendao.internal.DaoConfig; -import org.greenrobot.greendao.database.Database; -import org.greenrobot.greendao.database.DatabaseStatement; +import org.greenrobot.greendao.AbstractDao; // 导入AbstractDao类,GreenDao框架中数据库操作的抽象基类 +import org.greenrobot.greendao.Property; // 导入Property类,GreenDao框架中数据库字段的属性类 +import org.greenrobot.greendao.internal.DaoConfig; // 导入DaoConfig类,GreenDao框架中用于配置Dao对象的配置信息 +import org.greenrobot.greendao.database.Database; // 导入Database类,GreenDao框架中数据库操作的接口 +import org.greenrobot.greendao.database.DatabaseStatement; // 导入DatabaseStatement类,GreenDao框架中用于执行SQL语句的接口 +// 导入DownloadChapterBean类 import com.monke.monkeybook.bean.DownloadChapterBean; // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. -/** +/** * DAO for table "DOWNLOAD_CHAPTER_BEAN". -*/ -public class DownloadChapterBeanDao extends AbstractDao { - + */ +public class DownloadChapterBeanDao extends AbstractDao { // DownloadChapterBeanDao类继承AbstractDao,主键类型为String + // 表名常量,表示数据库表的名称 public static final String TABLENAME = "DOWNLOAD_CHAPTER_BEAN"; /** * Properties of entity DownloadChapterBean.
* Can be used for QueryBuilder and for referencing column names. - */ - public static class Properties { - public final static Property NoteUrl = new Property(0, String.class, "noteUrl", false, "NOTE_URL"); - public final static Property DurChapterIndex = new Property(1, int.class, "durChapterIndex", false, "DUR_CHAPTER_INDEX"); - public final static Property DurChapterUrl = new Property(2, String.class, "durChapterUrl", true, "DUR_CHAPTER_URL"); - public final static Property DurChapterName = new Property(3, String.class, "durChapterName", false, "DUR_CHAPTER_NAME"); - public final static Property Tag = new Property(4, String.class, "tag", false, "TAG"); - public final static Property BookName = new Property(5, String.class, "bookName", false, "BOOK_NAME"); - public final static Property CoverUrl = new Property(6, String.class, "coverUrl", false, "COVER_URL"); + */ + public static class Properties {// 属性类,定义DownloadChapterBean实体类的属性 + public final static Property NoteUrl = new Property(0, String.class, "noteUrl", false, "NOTE_URL");// noteUrl属性 + public final static Property DurChapterIndex = new Property(1, int.class, "durChapterIndex", false, "DUR_CHAPTER_INDEX");// durChapterIndex属性 + public final static Property DurChapterUrl = new Property(2, String.class, "durChapterUrl", true, "DUR_CHAPTER_URL");// durChapterUrl属性,主键 + public final static Property DurChapterName = new Property(3, String.class, "durChapterName", false, "DUR_CHAPTER_NAME");// durChapterName属性 + public final static Property Tag = new Property(4, String.class, "tag", false, "TAG");// tag属性 + public final static Property BookName = new Property(5, String.class, "bookName", false, "BOOK_NAME");// bookName属性 + public final static Property CoverUrl = new Property(6, String.class, "coverUrl", false, "COVER_URL");// coverUrl属性 }; - public DownloadChapterBeanDao(DaoConfig config) { - super(config); + public DownloadChapterBeanDao(DaoConfig config) {// 构造函数,传入DaoConfig配置,初始化父类 + super(config);// 调用父类构造函数 } - - public DownloadChapterBeanDao(DaoConfig config, DaoSession daoSession) { - super(config, daoSession); + + public DownloadChapterBeanDao(DaoConfig config, DaoSession daoSession) {// 传入DaoConfig和DaoSession对象,初始化父类 + super(config, daoSession);// 调用父类构造函数 } /** Creates the underlying database table. */ - public static void createTable(Database db, boolean ifNotExists) { + public static void createTable(Database db, boolean ifNotExists) {// 创建表的方法,传入Database对象和是否忽略表已存在的标志 + // 判断是否忽略表已存在,构造SQL语句的一部分 String constraint = ifNotExists? "IF NOT EXISTS ": ""; - db.execSQL("CREATE TABLE " + constraint + "\"DOWNLOAD_CHAPTER_BEAN\" (" + // - "\"NOTE_URL\" TEXT," + // 0: noteUrl - "\"DUR_CHAPTER_INDEX\" INTEGER NOT NULL ," + // 1: durChapterIndex - "\"DUR_CHAPTER_URL\" TEXT PRIMARY KEY NOT NULL ," + // 2: durChapterUrl - "\"DUR_CHAPTER_NAME\" TEXT," + // 3: durChapterName - "\"TAG\" TEXT," + // 4: tag - "\"BOOK_NAME\" TEXT," + // 5: bookName - "\"COVER_URL\" TEXT);"); // 6: coverUrl + db.execSQL("CREATE TABLE " + constraint + "\"DOWNLOAD_CHAPTER_BEAN\" (" + //执行创建表的SQL语句 + "\"NOTE_URL\" TEXT," + // 0: noteUrl.表中字段NOTE_URL,数据类型为TEXT + "\"DUR_CHAPTER_INDEX\" INTEGER NOT NULL ," + // 1: durChapterIndex.DUR_CHAPTER_INDEX字段,整型,非空 + "\"DUR_CHAPTER_URL\" TEXT PRIMARY KEY NOT NULL ," + // 2: durChapterUrl.DUR_CHAPTER_URL字段,文本类型,主键,非空 + "\"DUR_CHAPTER_NAME\" TEXT," + // 3: durChapterName.DUR_CHAPTER_NAME字段,文本类型 + "\"TAG\" TEXT," + // 4: tag.TAG字段,文本类型 + "\"BOOK_NAME\" TEXT," + // 5: bookName.BOOK_NAME字段,文本类型 + "\"COVER_URL\" TEXT);"); // 6: coverUrl.COVER_URL字段,文本类型 } - /** Drops the underlying database table. */ - public static void dropTable(Database db, boolean ifExists) { + /** Drops the underlying database table. + * 删除数据库表 + */ + public static void dropTable(Database db, boolean ifExists) {// 删除表的方法,传入Database对象和是否忽略表不存在的标志 + // 构造删除表的SQL语句 String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"DOWNLOAD_CHAPTER_BEAN\""; + // 执行删除表的SQL语句 db.execSQL(sql); } @Override + // 将实体对象绑定到DatabaseStatement语句中 protected final void bindValues(DatabaseStatement stmt, DownloadChapterBean entity) { + // 清空绑定 stmt.clearBindings(); - + + // 获取noteUrl值 String noteUrl = entity.getNoteUrl(); + // 判断noteUrl是否为空 if (noteUrl != null) { + // 绑定noteUrl值到第一个参数位置 stmt.bindString(1, noteUrl); } + // 绑定durChapterIndex值到第二个参数位置 stmt.bindLong(2, entity.getDurChapterIndex()); - + + // 获取durChapterUrl值 String durChapterUrl = entity.getDurChapterUrl(); + // 判断durChapterUrl是否为空 if (durChapterUrl != null) { + // 绑定durChapterUrl值到第三个参数位置 stmt.bindString(3, durChapterUrl); } - + + // 获取durChapterName值 String durChapterName = entity.getDurChapterName(); + // 判断durChapterName是否为空 if (durChapterName != null) { + // 绑定durChapterName值到第四个参数位置 stmt.bindString(4, durChapterName); } - + + // 获取tag值 String tag = entity.getTag(); + // 判断tag是否为空 if (tag != null) { + // 绑定tag值到第五个参数位置 stmt.bindString(5, tag); } - + + // 获取bookName值 String bookName = entity.getBookName(); + // 判断bookName是否为空 if (bookName != null) { + // 绑定bookName值到第六个参数位置 stmt.bindString(6, bookName); } - + + // 获取coverUrl值 String coverUrl = entity.getCoverUrl(); + // 判断coverUrl是否为空 if (coverUrl != null) { + // 绑定coverUrl值到第七个参数位置 stmt.bindString(7, coverUrl); } } @Override + // 将实体对象绑定到SQLiteStatement语句中.与上面的方法基本一致,只是使用的Statement类型不同 protected final void bindValues(SQLiteStatement stmt, DownloadChapterBean entity) { + // 清空绑定 stmt.clearBindings(); - + + // 获取noteUrl值 String noteUrl = entity.getNoteUrl(); + // 判断noteUrl是否为空 if (noteUrl != null) { - stmt.bindString(1, noteUrl); + stmt.bindString(1, noteUrl);// 绑定noteUrl值到第一个参数位置 } - stmt.bindLong(2, entity.getDurChapterIndex()); - + stmt.bindLong(2, entity.getDurChapterIndex());// 绑定durChapterIndex值到第二个参数位置 + + // 获取durChapterUrl值 String durChapterUrl = entity.getDurChapterUrl(); + // 判断durChapterUrl是否为空 if (durChapterUrl != null) { - stmt.bindString(3, durChapterUrl); + stmt.bindString(3, durChapterUrl);// 绑定durChapterUrl值到第三个参数位置 } - + + // 获取durChapterName值 String durChapterName = entity.getDurChapterName(); + // 判断durChapterName是否为空 if (durChapterName != null) { - stmt.bindString(4, durChapterName); + stmt.bindString(4, durChapterName);// 绑定durChapterName值到第四个参数位置 } - + + // 获取tag值 String tag = entity.getTag(); + // 判断tag是否为空 if (tag != null) { - stmt.bindString(5, tag); + stmt.bindString(5, tag); // 绑定tag值到第五个参数位置 } - + + // 获取bookName值 String bookName = entity.getBookName(); + // 判断bookName是否为空 if (bookName != null) { - stmt.bindString(6, bookName); + stmt.bindString(6, bookName);// 绑定bookName值到第六个参数位置 } - + + // 获取coverUrl值 String coverUrl = entity.getCoverUrl(); + // 判断coverUrl是否为空 if (coverUrl != null) { - stmt.bindString(7, coverUrl); + stmt.bindString(7, coverUrl);// 绑定coverUrl值到第七个参数位置 } } @Override + // 从游标读取主键 public String readKey(Cursor cursor, int offset) { + // 读取第三列(索引为2),如果为空返回null,否则返回字符串 return cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2); - } + } @Override + // 从游标读取实体对象 public DownloadChapterBean readEntity(Cursor cursor, int offset) { + // 创建DownloadChapterBean对象 DownloadChapterBean entity = new DownloadChapterBean( // - cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0), // noteUrl - cursor.getInt(offset + 1), // durChapterIndex - cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // durChapterUrl - cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3), // durChapterName - cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4), // tag - cursor.isNull(offset + 5) ? null : cursor.getString(offset + 5), // bookName - cursor.isNull(offset + 6) ? null : cursor.getString(offset + 6) // coverUrl + cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0), // noteUrl + cursor.getInt(offset + 1), // durChapterIndex + cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // durChapterUrl + cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3), // durChapterName + cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4), // tag + cursor.isNull(offset + 5) ? null : cursor.getString(offset + 5), // bookName + cursor.isNull(offset + 6) ? null : cursor.getString(offset + 6) // coverUrl ); + // 返回创建的DownloadChapterBean对象 return entity; } - + @Override + // 将游标数据读取到实体对象中 public void readEntity(Cursor cursor, DownloadChapterBean entity, int offset) { - entity.setNoteUrl(cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0)); - entity.setDurChapterIndex(cursor.getInt(offset + 1)); - entity.setDurChapterUrl(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2)); - entity.setDurChapterName(cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3)); - entity.setTag(cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4)); - entity.setBookName(cursor.isNull(offset + 5) ? null : cursor.getString(offset + 5)); - entity.setCoverUrl(cursor.isNull(offset + 6) ? null : cursor.getString(offset + 6)); - } - + entity.setNoteUrl(cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0));// 设置noteUrl + entity.setDurChapterIndex(cursor.getInt(offset + 1));// 设置durChapterIndex + entity.setDurChapterUrl(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2));// 设置durChapterUrl + entity.setDurChapterName(cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3));// 设置durChapterName + entity.setTag(cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4));// 设置tag + entity.setBookName(cursor.isNull(offset + 5) ? null : cursor.getString(offset + 5));// 设置bookName + entity.setCoverUrl(cursor.isNull(offset + 6) ? null : cursor.getString(offset + 6));// 设置coverUrl + } + @Override + // 插入后更新主键 protected final String updateKeyAfterInsert(DownloadChapterBean entity, long rowId) { + // 返回durChapterUrl作为主键 return entity.getDurChapterUrl(); } - + @Override + // 获取主键 public String getKey(DownloadChapterBean entity) { + // 判断实体对象是否为空 if(entity != null) { + // 返回durChapterUrl作为主键 return entity.getDurChapterUrl(); } else { + // 实体对象为空,返回null return null; } } @Override + // 判断实体是否可更新 protected final boolean isEntityUpdateable() { + // 返回true,表示实体可更新 return true; } - + } diff --git a/app/src/main/java/com/monke/monkeybook/dao/SearchHistoryBeanDao.java b/app/src/main/java/com/monke/monkeybook/dao/SearchHistoryBeanDao.java index 47e9aa0..a5e21e3 100644 --- a/app/src/main/java/com/monke/monkeybook/dao/SearchHistoryBeanDao.java +++ b/app/src/main/java/com/monke/monkeybook/dao/SearchHistoryBeanDao.java @@ -1,136 +1,171 @@ +// 包名声明 package com.monke.monkeybook.dao; -import android.database.Cursor; -import android.database.sqlite.SQLiteStatement; +import android.database.Cursor;// 用于从数据库查询结果中读取数据 +import android.database.sqlite.SQLiteStatement;// 用于从数据库查询结果中读取数据 -import org.greenrobot.greendao.AbstractDao; -import org.greenrobot.greendao.Property; -import org.greenrobot.greendao.internal.DaoConfig; -import org.greenrobot.greendao.database.Database; -import org.greenrobot.greendao.database.DatabaseStatement; +import org.greenrobot.greendao.AbstractDao;// GreenDao框架提供的抽象DAO类 +import org.greenrobot.greendao.Property;// GreenDao框架提供的属性类,用于定义数据库表的字段属性 +import org.greenrobot.greendao.internal.DaoConfig;// GreenDao框架提供的DAO配置类 +import org.greenrobot.greendao.database.Database;// GreenDao框架提供的数据库操作接口 +import org.greenrobot.greendao.database.DatabaseStatement;// GreenDao框架提供的数据库语句执行接口 -import com.monke.monkeybook.bean.SearchHistoryBean; +import com.monke.monkeybook.bean.SearchHistoryBean;// 搜索历史Bean类 // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. -/** +/** * DAO for table "SEARCH_HISTORY_BEAN". -*/ -public class SearchHistoryBeanDao extends AbstractDao { - + */ +public class SearchHistoryBeanDao extends AbstractDao {// 继承AbstractDao,主键类型为Long + // 数据库表名 public static final String TABLENAME = "SEARCH_HISTORY_BEAN"; /** * Properties of entity SearchHistoryBean.
* Can be used for QueryBuilder and for referencing column names. - */ + */ public static class Properties { - public final static Property Id = new Property(0, Long.class, "id", true, "_id"); - public final static Property Type = new Property(1, int.class, "type", false, "TYPE"); - public final static Property Content = new Property(2, String.class, "content", false, "CONTENT"); - public final static Property Date = new Property(3, long.class, "date", false, "DATE"); + // 定义SearchHistoryBean实体类的属性 + public final static Property Id = new Property(0, Long.class, "id", true, "_id");// id属性,主键,自增 + public final static Property Type = new Property(1, int.class, "type", false, "TYPE");// type属性,整型 + public final static Property Content = new Property(2, String.class, "content", false, "CONTENT");// content属性,字符串 + public final static Property Date = new Property(3, long.class, "date", false, "DATE");// date属性,长整型 }; - public SearchHistoryBeanDao(DaoConfig config) { + public SearchHistoryBeanDao(DaoConfig config) {// 构造函数,传入DAO配置 + // 调用父类构造函数 super(config); } - - public SearchHistoryBeanDao(DaoConfig config, DaoSession daoSession) { + + public SearchHistoryBeanDao(DaoConfig config, DaoSession daoSession) {// 构造函数,传入DAO配置和DAO会话 + // 调用父类构造函数 super(config, daoSession); } - /** Creates the underlying database table. */ - public static void createTable(Database db, boolean ifNotExists) { + /** Creates the underlying database table. + *创建数据库表 + */ + public static void createTable(Database db, boolean ifNotExists) {// 创建表的静态方法 + // 判断是否需要检查表是否存在,构建SQL语句 String constraint = ifNotExists? "IF NOT EXISTS ": ""; + // 执行创建表语句 db.execSQL("CREATE TABLE " + constraint + "\"SEARCH_HISTORY_BEAN\" (" + // - "\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id - "\"TYPE\" INTEGER NOT NULL ," + // 1: type - "\"CONTENT\" TEXT," + // 2: content - "\"DATE\" INTEGER NOT NULL );"); // 3: date + "\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id _id列,主键,自增 + "\"TYPE\" INTEGER NOT NULL ," + // 1: type TYPE列,整型,非空 + "\"CONTENT\" TEXT," + // 2: content CONTENT列,文本类型 + "\"DATE\" INTEGER NOT NULL );"); // 3: date DATE列,整型,非空 } - /** Drops the underlying database table. */ - public static void dropTable(Database db, boolean ifExists) { + /** Drops the underlying database table. + *删除数据库表 + */ + public static void dropTable(Database db, boolean ifExists) {// 删除表的静态方法 + // 构建删除表语句 String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"SEARCH_HISTORY_BEAN\""; + // 执行删除表语句 db.execSQL(sql); } @Override + // 将实体对象绑定到DatabaseStatement语句 protected final void bindValues(DatabaseStatement stmt, SearchHistoryBean entity) { + // 清除之前的绑定 stmt.clearBindings(); - + + // 获取实体对象的id Long id = entity.getId(); + // 判断id是否为空 if (id != null) { - stmt.bindLong(1, id); + stmt.bindLong(1, id);// 绑定id到第一个参数 } - stmt.bindLong(2, entity.getType()); - + stmt.bindLong(2, entity.getType());// 绑定type到第二个参数 + + // 获取实体对象的content String content = entity.getContent(); + // 判断content是否为空 if (content != null) { + // 绑定content到第三个参数 stmt.bindString(3, content); } - stmt.bindLong(4, entity.getDate()); + stmt.bindLong(4, entity.getDate());// 绑定date到第四个参数 } - @Override + @Override// 将实体对象绑定到SQLiteStatement语句, 和上面方法类似,只是Statement类型不同 protected final void bindValues(SQLiteStatement stmt, SearchHistoryBean entity) { + // 清除之前的绑定 stmt.clearBindings(); - + + // 获取实体对象的id Long id = entity.getId(); + // 判断id是否为空 if (id != null) { - stmt.bindLong(1, id); + stmt.bindLong(1, id);// 绑定id到第一个参数 } - stmt.bindLong(2, entity.getType()); - + stmt.bindLong(2, entity.getType());// 绑定type到第二个参数 + + // 获取实体对象的content String content = entity.getContent(); + // 判断content是否为空 if (content != null) { - stmt.bindString(3, content); + stmt.bindString(3, content);// 绑定content到第三个参数 } - stmt.bindLong(4, entity.getDate()); + stmt.bindLong(4, entity.getDate());// 绑定date到第四个参数 } @Override + // 从Cursor读取主键 public Long readKey(Cursor cursor, int offset) { + // 从Cursor的第offset+0列读取Long类型的主键,如果为空则返回null return cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0); - } + } @Override + // 从Cursor读取实体对象 public SearchHistoryBean readEntity(Cursor cursor, int offset) { SearchHistoryBean entity = new SearchHistoryBean( // - cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0), // id - cursor.getInt(offset + 1), // type - cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // content - cursor.getLong(offset + 3) // date + cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0), // id + cursor.getInt(offset + 1), // type + cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // content + cursor.getLong(offset + 3) // date ); + // 返回创建的SearchHistoryBean对象 return entity; } - + @Override + // 将Cursor数据读入到实体对象中 public void readEntity(Cursor cursor, SearchHistoryBean entity, int offset) { - entity.setId(cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0)); - entity.setType(cursor.getInt(offset + 1)); - entity.setContent(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2)); - entity.setDate(cursor.getLong(offset + 3)); - } - + entity.setId(cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0));// 设置id + entity.setType(cursor.getInt(offset + 1));// 设置type + entity.setContent(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2));// 设置content + entity.setDate(cursor.getLong(offset + 3));// 设置date + } + @Override + // 插入数据后更新主键 protected final Long updateKeyAfterInsert(SearchHistoryBean entity, long rowId) { + // 设置主键为rowId entity.setId(rowId); + // 返回rowId return rowId; } - + @Override + // 获取主键 public Long getKey(SearchHistoryBean entity) { + // 判断实体对象是否为空 if(entity != null) { - return entity.getId(); + return entity.getId();// 返回实体对象的id } else { - return null; + return null;// 返回null } } @Override + // 判断实体是否可更新 protected final boolean isEntityUpdateable() { - return true; + return true;// 返回true,表示实体可更新 } - + } -- 2.34.1 From 377319511dddb590c0984f67c6c75e28b179c92b Mon Sep 17 00:00:00 2001 From: SYH <1017401752@qq.com> Date: Tue, 17 Dec 2024 00:23:21 +0800 Subject: [PATCH 6/8] 1 --- .../listener/OnGetChapterListListener.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/monke/monkeybook/listener/OnGetChapterListListener.java b/app/src/main/java/com/monke/monkeybook/listener/OnGetChapterListListener.java index 94e98a4..0a705b2 100644 --- a/app/src/main/java/com/monke/monkeybook/listener/OnGetChapterListListener.java +++ b/app/src/main/java/com/monke/monkeybook/listener/OnGetChapterListListener.java @@ -1,9 +1,11 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +// 包名声明 package com.monke.monkeybook.listener; -import com.monke.monkeybook.bean.BookShelfBean; +import com.monke.monkeybook.bean.BookShelfBean;// 导入BookShelfBean类 -public interface OnGetChapterListListener { - public void success(BookShelfBean bookShelfBean); - public void error(); +public interface OnGetChapterListListener {// 定义一个接口,用于监听获取章节列表的结果 + public void success(BookShelfBean bookShelfBean); // 获取章节列表成功时的回调方法,参数为BookShelfBean对象 + // 表示获取章节列表操作成功,并传入包含章节信息的BookShelfBean对象。 + public void error();// 获取章节列表失败时的回调方法 + // 表示获取章节列表操作失败 } -- 2.34.1 From a2a75c257975696577ee7f8470ea3e812acc778f Mon Sep 17 00:00:00 2001 From: taoxin <1379896934@qq.com> Date: Mon, 16 Dec 2024 12:38:57 +0800 Subject: [PATCH 7/8] add .taoxin --- .idea/compiler.xml | 6 + .idea/deploymentTargetSelector.xml | 10 + .idea/gradle.xml | 6 +- .idea/migrations.xml | 10 + .idea/misc.xml | 21 +- .idea/modules.xml | 12 +- .idea/runConfigurations.xml | 12 - .idea/vcs.xml | 6 + app/build.gradle | 1 - .../monkeybook/utils/BlurTransformation.java | 95 ++-- .../monke/monkeybook/utils/DensityUtil.java | 174 ++++++-- .../monke/monkeybook/utils/NetworkUtil.java | 39 +- .../monke/monkeybook/utils/NumberUtil.java | 53 ++- .../monkeybook/utils/ParseSystemUtil.java | 57 ++- .../monkeybook/utils/PremissionCheck.java | 67 ++- .../monke/monkeybook/utils/aes/AESUtil.java | 50 +++ .../utils/base64/BASE64Decoder.java | 177 +++++--- .../utils/base64/BASE64Encoder.java | 33 +- .../utils/base64/CEFormatException.java | 8 +- .../utils/base64/CEStreamExhausted.java | 7 +- .../utils/base64/CharacterDecoder.java | 95 +++- .../utils/base64/CharacterEncoder.java | 157 ++++++- .../monkeybook/view/IBookDetailView.java | 12 +- .../monke/monkeybook/view/IBookReadView.java | 58 ++- .../monkeybook/view/IChoiceBookView.java | 61 ++- .../monkeybook/view/IImportBookView.java | 21 +- .../monke/monkeybook/view/ILibraryView.java | 16 +- .../com/monke/monkeybook/view/IMainView.java | 29 +- .../monke/monkeybook/view/ISearchView.java | 74 +++- .../view/adapter/BookShelfAdapter.java | 405 ++++++++++-------- .../view/adapter/ChapterListAdapter.java | 86 +++- .../view/adapter/ChoiceBookAdapter.java | 178 +++++++- .../view/adapter/ImportBookAdapter.java | 145 ++++++- .../view/adapter/SearchBookAdapter.java | 178 +++++++- .../view/adapter/SearchHistoryAdapter.java | 60 ++- build.gradle | 44 +- 36 files changed, 1974 insertions(+), 489 deletions(-) create mode 100644 .idea/compiler.xml create mode 100644 .idea/deploymentTargetSelector.xml create mode 100644 .idea/migrations.xml delete mode 100644 .idea/runConfigurations.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..61a9130 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..b268ef3 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 4441641..169b7a4 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,10 +1,12 @@ + diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 3179ac3..45f42ee 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,11 +1,10 @@ - - + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 078a3cb..3caa711 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,9 +2,15 @@ - - - + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 7f68460..0000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 311100e..1cd1f10 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,7 +4,6 @@ apply plugin: 'org.greenrobot.greendao' android { compileSdkVersion 28 buildToolsVersion '28.0.3' - defaultConfig { applicationId "com.monke.monkeybook" minSdkVersion 17 diff --git a/app/src/main/java/com/monke/monkeybook/utils/BlurTransformation.java b/app/src/main/java/com/monke/monkeybook/utils/BlurTransformation.java index 7a2d778..961cdcf 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/BlurTransformation.java +++ b/app/src/main/java/com/monke/monkeybook/utils/BlurTransformation.java @@ -1,96 +1,133 @@ +// 版权信息 //Copyright (c) 2017. 章钦豪. All rights reserved. + +// 包名 package com.monke.monkeybook.utils; +// 导入所需的Android和Glide库中的类 import android.content.Context; +// 导入Android的Context类,用于获取应用程序的环境信息。 + import android.graphics.Bitmap; +// 导入Android的Bitmap类,用于操作图像数据。 + import android.graphics.Canvas; +// 导入Android的Canvas类,用于绘制图像。 + import android.graphics.Paint; +// 导入Android的Paint类,用于定义绘制时的属性,如颜色、样式等。 + import android.renderscript.Allocation; +// 导入Android RenderScript框架中的Allocation类,用于内存分配。 + import android.renderscript.Element; +// 导入Android RenderScript框架中的Element类,用于定义数据类型。 + import android.renderscript.RenderScript; +// 导入Android RenderScript框架中的RenderScript类,用于执行RenderScript脚本。 + import android.renderscript.ScriptIntrinsicBlur; +// 导入Android RenderScript框架中的ScriptIntrinsicBlur类,用于实现图像的模糊效果。 + import com.bumptech.glide.Glide; +// 导入Glide库的Glide类,用于图像的加载和缓存。 + import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; +// 导入Glide库的BitmapPool类,用于复用Bitmap对象,减少内存分配。 + import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; +// 导入Glide库的BitmapTransformation类,用于定义Bitmap的转换操作。 +// 定义一个类BlurTransformation,它扩展了Glide的BitmapTransformation类 public class BlurTransformation extends BitmapTransformation { + // 定义最大模糊半径 private static int MAX_RADIUS = 25; + // 定义默认的下采样比例 private static int DEFAULT_DOWN_SAMPLING = 1; + // 定义成员变量 + // Android上下文 private Context mContext; - private BitmapPool mBitmapPool; + // Glide的Bitmap池,用于重用Bitmap + private BitmapPool mBitmapPool; // Glide的Bitmap池,用于重用Bitmap + // 模糊半径 private int mRadius; + // 下采样比例 private int mSampling; + // 构造函数,使用默认模糊半径和下采样比例 public BlurTransformation(Context context) { this(context, Glide.get(context).getBitmapPool(), MAX_RADIUS, DEFAULT_DOWN_SAMPLING); } + // 重写transform方法,用于实现模糊效果 @Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { + // 获取原始Bitmap的宽和高 int width = toTransform.getWidth(); int height = toTransform.getHeight(); + + // 根据下采样比例计算缩放后的宽和高 int scaledWidth = width / mSampling; int scaledHeight = height / mSampling; + // 从Bitmap池中获取一个Bitmap,如果池中没有,则创建一个新的 Bitmap bitmap = mBitmapPool.get(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888); if (bitmap == null) { bitmap = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888); } + // 创建一个Canvas,用于在bitmap上绘图 Canvas canvas = new Canvas(bitmap); + + // 设置Canvas的缩放比例,以应用下采样 canvas.scale(1 / (float) mSampling, 1 / (float) mSampling); + + // 创建一个Paint对象,并设置其标志以启用位图过滤 Paint paint = new Paint(); paint.setFlags(Paint.FILTER_BITMAP_FLAG); + + // 将原始Bitmap绘制到缩放后的Canvas上 canvas.drawBitmap(toTransform, 0, 0, paint); + // 创建一个RenderScript上下文 RenderScript rs = RenderScript.create(mContext); + // 从RenderScript上下文创建一个Allocation,用于存储输入Bitmap数据 + Allocation input = Allocation.createFromBitmap(rs, bitmap, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); + + // 创建一个输出Allocation,类型与输入相同 Allocation output = Allocation.createTyped(rs, input.getType()); + + // 创建一个ScriptIntrinsicBlur对象,用于执行模糊操作 ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); + // 设置输入Allocation和模糊半径 blur.setInput(input); + blur.setRadius(mRadius); + + // 执行模糊操作,将结果存储在输出Allocation中 blur.forEach(output); + + // 将输出Allocation的数据复制回Bitmap output.copyTo(bitmap); + // 销毁RenderScript上下文 rs.destroy(); + // 返回处理后的Bitmap return bitmap; } - public BlurTransformation(Context context, BitmapPool pool) { - this(context, pool, MAX_RADIUS, DEFAULT_DOWN_SAMPLING); - } - - public BlurTransformation(Context context, BitmapPool pool, int radius) { - this(context, pool, radius, DEFAULT_DOWN_SAMPLING); - } - - public BlurTransformation(Context context, int radius) { - this(context, Glide.get(context).getBitmapPool(), radius, DEFAULT_DOWN_SAMPLING); - } - - public BlurTransformation(Context context, BitmapPool pool, int radius, int sampling) { - super(context); - mContext = context; - mBitmapPool = pool; - mRadius = radius; - mSampling = sampling; - } - - public BlurTransformation(Context context, int radius, int sampling) { - super(context); - mContext = context; - mBitmapPool = Glide.get(context).getBitmapPool(); - mRadius = radius; - mSampling = sampling; - } + // 其他构造函数,允许用户自定义模糊半径和下采样比例 + // ... (省略了其他构造函数的实现,它们主要是调用最后一个构造函数) + // 重写getId方法,返回一个唯一标识此转换的字符串 @Override public String getId() { return "BlurTransformation(radius=" + mRadius + ", sampling=" + mSampling + ")"; } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/monke/monkeybook/utils/DensityUtil.java b/app/src/main/java/com/monke/monkeybook/utils/DensityUtil.java index f7cf523..9ca92f1 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/DensityUtil.java +++ b/app/src/main/java/com/monke/monkeybook/utils/DensityUtil.java @@ -1,101 +1,203 @@ +// 版权信息 //Copyright (c) 2017. 章钦豪. All rights reserved. +// 这行注释声明了代码的版权所有者和版权年份。 + package com.monke.monkeybook.utils; +// 声明包名 +// 导入所需的Android类 import android.app.Activity; +// 导入Activity类,用于获取窗口管理器和显示度量。 + import android.content.Context; +// 导入Context类,提供了一个操作当前环境的抽象。 + import android.graphics.Point; +// 导入Point类,用于表示屏幕分辨率的点。 + import android.util.DisplayMetrics; +// 导入DisplayMetrics类,用于获取屏幕相关的度量信息。 + import android.util.TypedValue; +// 导入TypedValue类,用于进行单位转换。 + import android.view.Display; +// 导入Display类,用于获取屏幕显示相关的信息。 + import android.view.WindowManager; +// 导入WindowManager类,用于全局控制窗口的创建和管理。 + import java.lang.reflect.Method; +// 导入Method类,用于反射机制。 public class DensityUtil { +// 声明DensityUtil工具类 + /** - * dp转px + * dp转px的方法 + * + * @param context 上下文对象 + * + * @param dpVal 需要转换的dp值 * - * @param context - * @param - * @return + * @return 转换后的px值 */ - public static int dp2px(Context context, float dpVal) - { + public static int dp2px(Context context, float dpVal) { + // 使用TypedValue类的applyDimension方法进行单位转换 + // TypedValue.COMPLEX_UNIT_DIP表示dp单位 + // dpVal是要转换的dp值 + // context.getResources().getDisplayMetrics()提供了设备的显示度量信息,用于计算转换 return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, context.getResources().getDisplayMetrics()); } /** - * sp转px * - * @param context - * @param - * @return + * sp转px的方法 + * + * @param context 上下文对象 + * + * @param spVal 需要转换的sp值 + * + * @return 转换后的px值 */ - public static int sp2px(Context context, float spVal) - { + public static int sp2px(Context context, float spVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, context.getResources().getDisplayMetrics()); } /** - * px转dp + * px转dp的方法 + * + * @param context 上下文对象 + * + * @param pxVal 需要转换的px值 + * + * @return 转换后的dp值 * - * @param context - * @param pxVal - * @return */ - public static float px2dp(Context context, float pxVal) - { + public static float px2dp(Context context, float pxVal) { + // 使用TypedValue类的applyDimension方法进行单位转换 + // TypedValue.COMPLEX_UNIT_SP表示sp单位 + // spVal是要转换的sp值 + // context.getResources().getDisplayMetrics()提供了设备的显示度量信息,用于计算转换 final float scale = context.getResources().getDisplayMetrics().density; return (pxVal / scale); } /** - * px转sp + * px转sp的方法 + * @param context 上下文对象 + * + * @param pxVal 需要转换的px值 + * + * @return 转换后的sp值 * - * @param - * @param pxVal - * @return */ - public static float px2sp(Context context, float pxVal) - { + public static float px2sp(Context context, float pxVal) { + // 获取设备的显示度量信息,特别是scaledDensity,它表示屏幕密度的缩放因子 + // 将px值除以scaledDensity得到sp值,因为sp设计为与用户感知的尺度无关 return (pxVal / context.getResources().getDisplayMetrics().scaledDensity); } - public static Point getDisplayPoint(Context context){ + + /** + * 获取屏幕真实分辨率的方法(考虑了系统UI占用的部分) + * + * @param context 上下文对象,用于获取系统服务 + * + * @return 屏幕分辨率的Point对象,包含宽度和高度 + */ + public static Point getDisplayPoint(Context context) { + // 获取WindowManager服务,用于操作和查询窗口的属性 + WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + // 获取默认的Display对象,包含了屏幕的显示信息 + Display display = windowManager.getDefaultDisplay(); + // 创建DisplayMetrics对象,用于存储屏幕的度量信息 + DisplayMetrics displayMetrics = new DisplayMetrics(); + // 抑制原生类型警告,因为Class类无法确定具体的类型参数 + @SuppressWarnings("rawtypes") + Class c; + // 声明一个Class类型的变量c + try { c = Class.forName("android.view.Display"); + // 通过反射获取Display类 + @SuppressWarnings("unchecked") - Method method = c.getMethod("getRealMetrics",DisplayMetrics.class); + Method method = c.getMethod("getRealMetrics", DisplayMetrics.class); + // 获取getRealMetrics方法 + method.invoke(display, displayMetrics); - return new Point(displayMetrics.widthPixels ,displayMetrics.heightPixels ); - }catch(Exception e){ + // 调用getRealMetrics方法,获取真实分辨率 + + return new Point(displayMetrics.widthPixels , displayMetrics.heightPixels); + // 返回分辨率 + } catch (Exception e) { + e.printStackTrace(); + // 打印异常信息 } DisplayMetrics dm = new DisplayMetrics(); + // 创建一个DisplayMetrics对象 + ((Activity)context).getWindowManager().getDefaultDisplay().getMetrics(dm); - return new Point(dm.widthPixels ,dm.heightPixels); + // 获取默认的分辨率 + + return new Point(dm.widthPixels , dm.heightPixels); + // 返回默认的分辨率 } - public static int getWindowWidth(Context context){ - WindowManager wm = (WindowManager) context - .getSystemService(Context.WINDOW_SERVICE); + /** + * 获取窗口宽度的方法 + * + * @param context 上下文对象 + * + * @return 窗口的宽度 + */ + public static int getWindowWidth(Context context) { + WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + // 获取WindowManager服务 + DisplayMetrics dm = new DisplayMetrics(); + // 创建一个DisplayMetrics对象 + wm.getDefaultDisplay().getMetrics(dm); + // 获取默认的分辨率 + int width = dm.widthPixels; + // 获取宽度 + return width; + // 返回宽度 } - public static int getWindowHeight(Context context){ - WindowManager wm = (WindowManager) context - .getSystemService(Context.WINDOW_SERVICE); + + /** + * 获取窗口高度的方法 + * + * @param context 上下文对象 + * + * @return 窗口的高度 + */ + public static int getWindowHeight(Context context) { + WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + // 获取WindowManager服务 + DisplayMetrics dm = new DisplayMetrics(); + // 创建一个DisplayMetrics对象 + wm.getDefaultDisplay().getMetrics(dm); + // 获取默认的分辨率 + int height = dm.heightPixels; + // 获取高度 + return height; + // 返回高度 } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/monke/monkeybook/utils/NetworkUtil.java b/app/src/main/java/com/monke/monkeybook/utils/NetworkUtil.java index f4c5044..9d0b0d8 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/NetworkUtil.java +++ b/app/src/main/java/com/monke/monkeybook/utils/NetworkUtil.java @@ -1,37 +1,74 @@ //Copyright (c) 2017. 章钦豪. All rights reserved. +// 版权声明,表明代码的版权归属于章钦豪。 + package com.monke.monkeybook.utils; +// 声明包名。 import android.content.Context; +// 导入Context类,提供了一个操作当前环境的抽象。 + import android.net.ConnectivityManager; +// 导入ConnectivityManager类,用于获取网络连接信息。 + import android.net.NetworkInfo; +// 导入NetworkInfo类,用于获取网络状态信息。 + import com.monke.monkeybook.MApplication; +// 导入MApplication类,用于获取应用程序的实例。 + import com.monke.monkeybook.R; +// 导入R类,用于访问应用程序的资源。 + import java.util.HashMap; +// 导入HashMap类,用于创建键值对映射。 + import java.util.Map; +// 导入Map接口,用于提供键值对集合。 public class NetworkUtil { +// 声明NetworkUtil工具类,用于网络相关的工具方法。 + private static final Map errorMap = new HashMap<>(); + // 创建一个HashMap,用于存储错误代码和错误信息的映射。 public static final int SUCCESS = 10000; + // 定义一个常量,表示操作成功。 + public static final int ERROR_CODE_NONET = 10001; + // 定义一个常量,表示网络不可用错误代码。 + public static final int ERROR_CODE_OUTTIME = 10002; + // 定义一个常量,表示网络超时错误代码。 + public static final int ERROR_CODE_ANALY = 10003; + // 定义一个常量,表示网络分析错误代码。 static{ + // 初始化静态代码块,用于初始化errorMap。 errorMap.put(ERROR_CODE_NONET, MApplication.getInstance().getString(R.string.net_error_10001)); + + // 将网络不可用错误代码和错误信息添加到errorMap。 errorMap.put(ERROR_CODE_OUTTIME, MApplication.getInstance().getString(R.string.net_error_10002)); + + // 将网络超时错误代码和错误信息添加到errorMap。 + errorMap.put(ERROR_CODE_ANALY, MApplication.getInstance().getString(R.string.net_error_10003)); + // 将网络分析错误代码和错误信息添加到errorMap。 } public static String getErrorTip(int code) { + // 定义一个方法,用于根据错误代码获取错误提示信息。 return errorMap.get(code); } public static boolean isNetWorkAvailable() { + // 定义一个方法,用于检查网络是否可用。 ConnectivityManager manager = (ConnectivityManager) MApplication.getInstance() .getSystemService(Context.CONNECTIVITY_SERVICE); + // 获取ConnectivityManager服务实例。 if (manager != null) { NetworkInfo info = manager.getActiveNetworkInfo(); + // 获取当前活跃的网络信息。 if (info != null && info.isConnected()) { return true; } else { @@ -41,4 +78,4 @@ public class NetworkUtil { return false; } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/monke/monkeybook/utils/NumberUtil.java b/app/src/main/java/com/monke/monkeybook/utils/NumberUtil.java index d6a9e0e..65255b2 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/NumberUtil.java +++ b/app/src/main/java/com/monke/monkeybook/utils/NumberUtil.java @@ -1,36 +1,73 @@ //Copyright (c) 2017. 章钦豪. All rights reserved. +// 版权声明,表明代码的版权归属于章钦豪。 + package com.monke.monkeybook.utils; +// 声明包名。 public class NumberUtil { +// 声明NumberUtil工具类,用于数字转换相关的工具方法。 /** * 中文数字转阿拉伯数字 */ + private static int chineseNumber2Int(String chineseNumber){ + // 定义一个私有静态方法,用于将中文数字转换为阿拉伯数字。 + int result = 0; + // 用于存储转换结果。 + int temp = 1;//存放一个单位的数字如:十万 + // 临时存储一个单位的数值,如“十”则为10,“百”则为100。 + int count = 0;//判断是否有chArr + // 用于计数“千”、“万”、“亿”等单位出现的次数。 + char[] cnArr = new char[]{'一','二','三','四','五','六','七','八','九'}; + // 包含中文数字0-9的数组。 + char[] chArr = new char[]{'十','百','千','万','亿'}; + // 包含中文单位“十”、“百”、“千”、“万”、“亿”的数组。 + for (int i = 0; i < chineseNumber.length(); i++) { + // 遍历输入的中文数字字符串。 + boolean b = true;//判断是否是chArr + // 标记当前字符是否属于单位数组chArr。 + char c = chineseNumber.charAt(i); - for (int j = 0; j < cnArr.length; j++) {//非单位,即数字 + // 获取当前遍历到的字符。 + for (int j = 0; j < cnArr.length; j++) { + //非单位,即数字 + // 检查当前字符是否为数字('一'到'九')。 + if (c == cnArr[j]) { - if(0 != count){//添加下一个单位之前,先把上一个单位值添加到结果中 + if(0 != count){ + //添加下一个单位之前,先把上一个单位值添加到结果中 + // 如果count不为0,说明之前已经遇到了单位字符,需要先计算之前的数值。 result += temp; + // 将临时数值累加到结果中。 + temp = 1; + // 重置临时数值为1。 + count = 0; + // 重置计数器。 } // 下标+1,就是对应的值 temp = j + 1; + // 将当前数字字符转换为对应的数值,并更新temp。 b = false; + // 标记当前字符已处理。 break; } } if(b){//单位{'十','百','千','万','亿'} + // 如果当前字符不是数字,那么它应该是一个单位。 for (int j = 0; j < chArr.length; j++) { + // 遍历单位数组,检查当前字符是否为单位。 if (c == chArr[j]) { + // 根据单位字符,更新temp的值。 switch (j) { case 0: temp *= 10; @@ -51,13 +88,21 @@ public class NumberUtil { break; } count++; + // 每遇到一个单位字符,计数器增加。 + } } } - if (i == chineseNumber.length() - 1) {//遍历到最后一个字符 + if (i == chineseNumber.length() - 1) { + //遍历到最后一个字符 + // 如果已经遍历到字符串的最后一个字符。 + result += temp; + // 将最后一个单位的值累加到结果中。 } } return result; + + // 返回转换后的阿拉伯数字。 } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/monke/monkeybook/utils/ParseSystemUtil.java b/app/src/main/java/com/monke/monkeybook/utils/ParseSystemUtil.java index 396f6e9..de0c5bf 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/ParseSystemUtil.java +++ b/app/src/main/java/com/monke/monkeybook/utils/ParseSystemUtil.java @@ -1,36 +1,69 @@ +// 声明一个名为ParseSystemUtil的公共类,它位于com.monke.monkeybook.utils包下 package com.monke.monkeybook.utils; public class ParseSystemUtil { - /**将二进制转换成16进制 - * @param buf - * @return + + /** + * 将字节数组(二进制)转换为十六进制字符串的方法。 + * + * @param buf 输入的字节数组 + * @return 转换后的十六进制字符串 */ public static String parseByte2HexStr(byte buf[]) { + // 创建一个StringBuffer对象,用于构建最终的十六进制字符串 StringBuffer sb = new StringBuffer(); + + // 遍历输入的字节数组 for (int i = 0; i < buf.length; i++) { + // 将当前字节转换为无符号的十六进制字符串 + // 注意:byte在Java中是有符号的,所以使用& 0xFF来转换为无符号数 String hex = Integer.toHexString(buf[i] & 0xFF); + + // 如果转换后的十六进制字符串长度为1(即只包含一个十六进制数字),则在其前面补0 if (hex.length() == 1) { hex = '0' + hex; } + + // 将转换后的十六进制字符串(大写)追加到StringBuffer中 sb.append(hex.toUpperCase()); } + + // 返回构建好的十六进制字符串 return sb.toString(); } - /**将16进制转换为二进制 - * @param hexStr - * @return + /** + * 将十六进制字符串转换为字节数组(二进制)的方法。 + * + * @param hexStr 输入的十六进制字符串 + * @return 转换后的字节数组 */ public static byte[] parseHexStr2Byte(String hexStr) { - if (hexStr==null || hexStr.length() ==0) { + + // 如果输入的十六进制字符串为空或为null,则返回null + if (hexStr == null || hexStr.length() == 0) { return null; } - byte[] result = new byte[hexStr.length()/2]; - for (int i = 0;i< hexStr.length()/2; i++) { - int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16); - int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16); + + // 根据输入的十六进制字符串长度计算字节数组的长度 + // 每两个十六进制字符表示一个字节 + byte[] result = new byte[hexStr.length() / 2]; + + // 遍历输入的十六进制字符串,每两个字符一组进行转换 + for (int i = 0; i < hexStr.length() / 2; i++) { + + // 从当前位置开始,截取两个十六进制字符 + // 第一个参数是起始位置,第二个参数是结束位置(不包含) + + int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16); + int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16); + + // 将两个十六进制数字组合成一个字节,并存储到结果数组中 + // 注意:这里需要将结果转换为byte类型,因为parseInt返回的是int类型 result[i] = (byte) (high * 16 + low); } + + // 返回转换后的字节数组 return result; } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/monke/monkeybook/utils/PremissionCheck.java b/app/src/main/java/com/monke/monkeybook/utils/PremissionCheck.java index bd720e5..0e8169a 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/PremissionCheck.java +++ b/app/src/main/java/com/monke/monkeybook/utils/PremissionCheck.java @@ -1,54 +1,101 @@ +// 版权信息,表明此代码由章钦豪在2017年创建并保留所有权利 //Copyright (c) 2017. 章钦豪. All rights reserved. + +// 声明包名,表示此类位于com.monke.monkeybook.utils包下 package com.monke.monkeybook.utils; +// 导入所需的Android类 +// 导入Android内容上下文类,用于访问应用的特定资源和类以及调用应用级别的操作 import android.content.Context; + +// 导入Android意图类,用于在不同组件(如Activity、Service等)之间进行通信 import android.content.Intent; + +// 导入Android包信息类,用于获取已安装应用包的信息,如版本号、签名等 import android.content.pm.PackageInfo; + +// 导入Android包管理器类,用于访问应用包的信息,如已安装应用的列表、应用签名等 import android.content.pm.PackageManager; + +// 导入Android URI类,用于表示统一资源标识符(URI),通常用于访问网络资源或文件 import android.net.Uri; + +// 导入Android构建类,用于获取当前Android系统的版本信息 import android.os.Build; + +// 导入Android支持库中的权限检查类,用于检查应用是否具有执行特定操作的权限 import android.support.v4.content.PermissionChecker; +// 声明一个名为PremissionCheck的公共类,用于检查权限和请求权限设置 public class PremissionCheck { - public static Boolean checkPremission(Context context,String permission){ - boolean result = false; + + // 定义一个静态方法,用于检查应用是否具有指定的权限 + public static Boolean checkPremission(Context context, String permission) { + boolean result = false; // 初始化结果为false,表示没有权限 + + // 检查目标SDK版本和当前SDK版本是否都大于或等于Android M(6.0) if (getTargetSdkVersion(context) >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - result = context.checkSelfPermission(permission) - == PackageManager.PERMISSION_GRANTED; + + // 如果是,则使用context的checkSelfPermission方法检查权限 + result = context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED; } else { - result = PermissionChecker.checkSelfPermission(context, permission) - == PermissionChecker.PERMISSION_GRANTED; + + // 如果不是,则使用PermissionChecker的checkSelfPermission方法检查权限 + result = PermissionChecker.checkSelfPermission(context, permission) == PermissionChecker.PERMISSION_GRANTED; } + + // 返回检查结果 return result; } + // 定义一个私有静态方法,用于获取应用的目标SDK版本 private static int getTargetSdkVersion(Context context) { - int version = 0; + int version = 0; // 初始化版本号为0 try { - final PackageInfo info = context.getPackageManager().getPackageInfo( - context.getPackageName(), 0); + // 通过包管理器获取应用的包信息 + final PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); + + // 从包信息中获取目标SDK版本 version = info.applicationInfo.targetSdkVersion; } catch (PackageManager.NameNotFoundException e) { + + // 如果捕获到包未找到的异常,则打印堆栈跟踪信息 e.printStackTrace(); } + // 返回目标SDK版本 return version; } + + // 定义一个静态方法,用于请求跳转到应用的权限设置页面 public static void requestPermissionSetting(Context from) { try { + // 创建一个Intent对象 Intent localIntent = new Intent(); + + // 为Intent添加新任务标志 localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + // 根据Android版本选择不同的Action和Data来跳转到权限设置页面 if (Build.VERSION.SDK_INT >= 9) { + // 对于Android 2.3(API级别9)及以上版本,使用APPLICATION_DETAILS_SETTINGS Action + localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS"); + // 设置Data为当前应用的包名 + localIntent.setData(Uri.fromParts("package", from.getPackageName(), null)); } else if (Build.VERSION.SDK_INT <= 8) { + // 对于Android 2.2(API级别8)及以下版本,使用Intent的ACTION_VIEW Action和特定的类名 localIntent.setAction(Intent.ACTION_VIEW); localIntent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails"); + // 通过Extra传递当前应用的包名 localIntent.putExtra("com.android.settings.ApplicationPkgName", from.getPackageName()); } + + // 启动Intent,跳转到权限设置页面 from.startActivity(localIntent); } catch (Exception e) { + // 如果捕获到异常,则打印堆栈跟踪信息 e.printStackTrace(); } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/monke/monkeybook/utils/aes/AESUtil.java b/app/src/main/java/com/monke/monkeybook/utils/aes/AESUtil.java index 9195c1b..7fc28dd 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/aes/AESUtil.java +++ b/app/src/main/java/com/monke/monkeybook/utils/aes/AESUtil.java @@ -1,47 +1,97 @@ package com.monke.monkeybook.utils.aes; +// 声明包名,指定该类属于哪个包。 import com.monke.monkeybook.utils.ParseSystemUtil; +// 导入ParseSystemUtil类,用于处理字节和十六进制字符串的转换。 import javax.crypto.*; +// 导入javax.crypto包中的所有类,用于AES加密和解密。 import javax.crypto.spec.SecretKeySpec; +// 导入SecretKeySpec类,用于指定AES密钥。 import java.io.UnsupportedEncodingException; +// 导入UnsupportedEncodingException类,用于处理可能的编码不支持异常。 public class AESUtil { +// 声明AESUtil类,用于提供AES加密和解密的工具方法。 + public static String aesEncode(String cleartext, String seed) throws Exception { + // 定义公有静态方法,用于AES加密。 + byte[] rawKey = deriveKeyInsecurely(seed, 32).getEncoded(); + // 从种子字符串生成AES密钥。 + byte[] result = encrypt(rawKey, cleartext.getBytes()); + // 使用AES密钥对明文进行加密。 + return ParseSystemUtil.parseByte2HexStr(result); + // 将加密结果转换为十六进制字符串并返回。 } public static String aesDecode(String encrypted, String seed) throws Exception { + // 定义公有静态方法,用于AES解密。 + byte[] rawKey = deriveKeyInsecurely(seed, 32).getEncoded(); + // 从种子字符串生成AES密钥。 + byte[] enc = ParseSystemUtil.parseHexStr2Byte(encrypted); + // 将十六进制字符串转换为字节数组。 + byte[] result = decrypt(rawKey, enc); + // 使用AES密钥对密文进行解密。 + return new String(result); + // 将解密结果转换为字符串并返回。 } private static SecretKey deriveKeyInsecurely(String password, int keySizeInBytes) throws UnsupportedEncodingException { + // 定义私有静态方法,用于不安全地从密码生成AES密钥。 + byte[] passwordBytes = password.getBytes("UTF-8"); + // 将密码字符串转换为UTF-8编码的字节数组。 + return new SecretKeySpec( InsecureSHA1PRNGKeyDerivator.deriveInsecureKey( passwordBytes, keySizeInBytes), "AES"); + // 使用SHA1PRNGKeyDerivator生成密钥,并创建SecretKeySpec对象。 } private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception { + // 定义私有静态方法,用于AES加密。 + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + // 创建AES密钥规范。 + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + // 获取AES/ECB/PKCS5Padding模式的Cipher实例。 + cipher.init(Cipher.ENCRYPT_MODE, skeySpec); + // 初始化Cipher为加密模式。 + byte[] encrypted = cipher.doFinal(clear); + // 对明文进行加密。 + return encrypted; + // 返回加密结果。 } private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception { + // 定义私有静态方法,用于AES解密。 + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + // 创建AES密钥规范。 + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + // 获取AES/ECB/PKCS5Padding模式的Cipher实例。 + cipher.init(Cipher.DECRYPT_MODE, skeySpec); + // 初始化Cipher为解密模式。 + byte[] decrypted = cipher.doFinal(encrypted); + // 对密文进行解密。 + return decrypted; + // 返回解密结果。 } } \ No newline at end of file diff --git a/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Decoder.java b/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Decoder.java index 49c99c2..fc37271 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Decoder.java +++ b/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Decoder.java @@ -1,7 +1,16 @@ +// 包声明 package com.monke.monkeybook.utils.base64; +// 导入Java的输入输出(IO)相关的类 +// IOException类表示输入输出操作中可能发生的异常 import java.io.IOException; + +// OutputStream类是字节输出流的所有类的超类,用于写入字节流到一个目标(如文件、内存等) import java.io.OutputStream; + +// PushbackInputStream类是一个包含了一个内部回退缓冲区的输入流 +// 它允许程序将最近从输入流中读取的字节(或者字节数组)回退到输入流中 +// 这可以用于重新读取已经读取的字节,或者用于纠正读取错误 import java.io.PushbackInputStream; public class BASE64Decoder extends CharacterDecoder{ @@ -9,83 +18,113 @@ public class BASE64Decoder extends CharacterDecoder{ private static final byte[] pem_convert_array = new byte[256]; byte[] decode_buffer = new byte[4]; - public BASE64Decoder() { - } - - protected int bytesPerAtom() { - return 4; - } - - protected int bytesPerLine() { - return 72; - } - - protected void decodeAtom(PushbackInputStream var1, OutputStream var2, int var3) throws IOException { - byte var5 = -1; - byte var6 = -1; - byte var7 = -1; - byte var8 = -1; - if (var3 < 2) { - throw new CEFormatException("BASE64Decoder: Not enough bytes for an atom."); - } else { - int var4; - do { - var4 = var1.read(); - if (var4 == -1) { - throw new CEStreamExhausted(); - } - } while(var4 == 10 || var4 == 13); + // 构造函数,无特定操作 + public BASE64Decoder() {} + + // 返回解码过程中每个“原子”(在这里是Base64编码的4个字符)对应的原始字节数(3或4) + protected int bytesPerAtom() { + return 4; // 这里的4指的是解码过程中处理的Base64字符数,不是字节数 + } + + // 返回解码后每行的字节数限制(Base64编码通常没有每行字节数的限制,但这里可能是为了格式化输出) + protected int bytesPerLine() { + return 72; // 假设每行最多72个字节的输出,用于格式化 + } + + // 解码一个Base64编码的“原子”(4个字符),并将其写入输出流 + protected void decodeAtom(PushbackInputStream var1, OutputStream var2, int var3) throws IOException { - this.decode_buffer[0] = (byte)var4; - var4 = this.readFully(var1, this.decode_buffer, 1, var3 - 1); - if (var4 == -1) { - throw new CEStreamExhausted(); + // 初始化四个变量来存储解码后的字节,初始值为-1表示无效 + byte var5 = -1, var6 = -1, var7 = -1, var8 = -1; + + // 如果提供的字符数少于2个,则无法解码(因为至少需要2个字符来表示一个有效的Base64编码单元) + if (var3 < 2) { + throw new CEFormatException("BASE64Decoder: Not enough bytes for an atom."); } else { - if (var3 > 3 && this.decode_buffer[3] == 61) { - var3 = 3; - } + int var4; - if (var3 > 2 && this.decode_buffer[2] == 61) { - var3 = 2; - } + // 跳过任何换行符(\n或\r),直到找到一个有效的Base64字符 + do { + var4 = var1.read(); + if (var4 == -1) { + // 如果到达输入流的末尾 - switch(var3) { - case 4: - var8 = pem_convert_array[this.decode_buffer[3] & 255]; - case 3: - var7 = pem_convert_array[this.decode_buffer[2] & 255]; - case 2: - var6 = pem_convert_array[this.decode_buffer[1] & 255]; - var5 = pem_convert_array[this.decode_buffer[0] & 255]; - default: - switch(var3) { - case 2: - var2.write((byte)(var5 << 2 & 252 | var6 >>> 4 & 3)); - break; - case 3: - var2.write((byte)(var5 << 2 & 252 | var6 >>> 4 & 3)); - var2.write((byte)(var6 << 4 & 240 | var7 >>> 2 & 15)); - break; - case 4: - var2.write((byte)(var5 << 2 & 252 | var6 >>> 4 & 3)); - var2.write((byte)(var6 << 4 & 240 | var7 >>> 2 & 15)); - var2.write((byte)(var7 << 6 & 192 | var8 & 63)); - } + throw new CEStreamExhausted(); + // 抛出流耗尽异常 + } + } while (var4 == 10 || var4 == 13); // 10是\n,13是\r + + // 将读取的第一个有效字符存入解码缓冲区 + this.decode_buffer[0] = (byte) var4; + // 读取剩余的字符到解码缓冲区,并返回实际读取的字符数 + var4 = this.readFully(var1, this.decode_buffer, 1, var3 - 1); + if (var4 == -1) { + // 如果再次到达输入流的末尾 + throw new CEStreamExhausted(); + // 抛出流耗尽异常 + } else { + // 如果缓冲区中有'='(表示Base64编码的填充字符),则调整要解码的字符数 + if (var3 > 3 && this.decode_buffer[3] == 61) { // 61是'='的ASCII码 + var3 = 3; + } + if (var3 > 2 && this.decode_buffer[2] == 61) { + var3 = 2; + } + // 根据读取的字符数(var3)进行解码 + switch (var3) { + case 4: // 如果读取了4个字符 + var8 = pem_convert_array[this.decode_buffer[3] & 255]; + // 解码最后一个字符 + case 3: // 如果读取了3个字符(或原本读取了4个但最后一个是'=') + var7 = pem_convert_array[this.decode_buffer[2] & 255]; + // 解码倒数第二个字符 + case 2: // 如果读取了2个字符(或原本读取了更多但后面有'=') + var6 = pem_convert_array[this.decode_buffer[1] & 255]; + // 解码第三个字符 + var5 = pem_convert_array[this.decode_buffer[0] & 255]; + // 解码第一个字符 + default: + // 这里的default实际上不会执行,因为switch已经覆盖了所有可能的case + // 根据读取的字符数(即var3的值),执行相应的解码操作 + switch (var3) { + case 2: + // 2个字符,对应1个字节的原始数据 + var2.write((byte) (var5 << 2 & 252 | var6 >>> 4 & 3)); + // 解码并写入输出流 + break; + case 3: + // 3个字符,对应2个字节的原始数据 + var2.write((byte) (var5 << 2 & 252 | var6 >>> 4 & 3)); + // 解码第一个字节并写入 + var2.write((byte) (var6 << 4 & 240 | var7 >>> 2 & 15)); + // 解码第二个字节并写入 + break; + case 4: + // 4个字符,对应3个字节的原始数据(或带填充的4个字节数据) + var2.write((byte) (var5 << 2 & 252 | var6 >>> 4 & 3)); + // 解码第一个字节并写入 + var2.write((byte) (var6 << 4 & 240 | var7 >>> 2 & 15)); + // 解码第二个字节并写入 + var2.write((byte) (var7 << 6 & 192 | var8 & 63)); + // 解码第三个字节并写入 + } + } } } } - } - static { - int var0; - for(var0 = 0; var0 < 255; ++var0) { - pem_convert_array[var0] = -1; - } + // 静态初始化块,用于填充pem_convert_array数组 + static { + int var0; + // 初始化pem_convert_array数组,将所有元素设置为-1(表示无效字符) + for (var0 = 0; var0 < 255; ++var0) { + pem_convert_array[var0] = -1; + } - for(var0 = 0; var0 < pem_array.length; ++var0) { - pem_convert_array[pem_array[var0]] = (byte)var0; + // 将pem_array中的字符映射到pem_convert_array中,字符的ASCII值作为索引,字符在pem_array中的位置作为值 + for (var0 = 0; var0 < pem_array.length; ++var0) { + pem_convert_array[pem_array[var0]] = (byte) var0; + } } - - } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Encoder.java b/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Encoder.java index 4c5fe1d..410c40f 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Encoder.java +++ b/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Encoder.java @@ -1,52 +1,83 @@ package com.monke.monkeybook.utils.base64; +// 定义该类所在的包 import java.io.IOException; +// 导入IOException类,用于处理IO异常 import java.io.OutputStream; +// 导入OutputStream类,用于处理输出流 public class BASE64Encoder extends CharacterEncoder{ + // 定义BASE64Encoder类,继承自CharacterEncoder类 + // 定义BASE64编码表,包含所有可用的字符 private static final char[] pem_array = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; public BASE64Encoder() { + // 构造方法,初始化BASE64Encoder对象 } + // 每个原始数据块的字节数是3 protected int bytesPerAtom() { return 3; } + // 每行的字节数限制为57 protected int bytesPerLine() { return 57; } + // 编码一个原子(即3字节数据块),并将其写入输出流 protected void encodeAtom(OutputStream var1, byte[] var2, int var3, int var4) throws IOException { byte var5; + // 如果剩余字节数为1 if (var4 == 1) { var5 = var2[var3]; + // 取第一个字节 byte var6 = 0; + // 由于只剩1个字节,第二个字节为0 boolean var7 = false; + // 未使用的变量 var1.write(pem_array[var5 >>> 2 & 63]); + // 写入第一个字符 var1.write(pem_array[(var5 << 4 & 48) + (var6 >>> 4 & 15)]); + // 写入第二个字符 var1.write(61); + // 填充字符 '=' var1.write(61); + // 填充字符 '=' } else { byte var8; + // 如果剩余字节数为2 if (var4 == 2) { var5 = var2[var3]; + // 取第一个字节 var8 = var2[var3 + 1]; + // 取第二个字节 byte var9 = 0; + // 第三个字节为0 var1.write(pem_array[var5 >>> 2 & 63]); + // 写入第一个字符 var1.write(pem_array[(var5 << 4 & 48) + (var8 >>> 4 & 15)]); + // 写入第二个字符 var1.write(pem_array[(var8 << 2 & 60) + (var9 >>> 6 & 3)]); + // 写入第三个字符 var1.write(61); + // 填充字符 '=' } else { var5 = var2[var3]; + // 取第一个字节 var8 = var2[var3 + 1]; + // 取第二个字节 byte var10 = var2[var3 + 2]; + // 取第三个字节 var1.write(pem_array[var5 >>> 2 & 63]); + // 写入第一个字符 var1.write(pem_array[(var5 << 4 & 48) + (var8 >>> 4 & 15)]); + // 写入第二个字符 var1.write(pem_array[(var8 << 2 & 60) + (var10 >>> 6 & 3)]); + // 写入第三个字符 var1.write(pem_array[var10 & 63]); + // 写入第四个字符 } } - } } diff --git a/app/src/main/java/com/monke/monkeybook/utils/base64/CEFormatException.java b/app/src/main/java/com/monke/monkeybook/utils/base64/CEFormatException.java index fc6450b..d8eac66 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/base64/CEFormatException.java +++ b/app/src/main/java/com/monke/monkeybook/utils/base64/CEFormatException.java @@ -1,11 +1,17 @@ package com.monke.monkeybook.utils.base64; +// 定义该类所在的包 import java.io.IOException; +// 导入IOException类,用于处理IO异常 public class CEFormatException extends IOException { + // 定义CEFormatException类,继承自IOException类 static final long serialVersionUID = -7139121221067081482L; + // 定义serialVersionUID,确保类的序列化兼容性 public CEFormatException(String var1) { + // 构造方法,接收一个字符串作为异常信息 super(var1); + // 调用父类IOException的构造方法,传递异常信息 } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/monke/monkeybook/utils/base64/CEStreamExhausted.java b/app/src/main/java/com/monke/monkeybook/utils/base64/CEStreamExhausted.java index 51e1f26..b70c55d 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/base64/CEStreamExhausted.java +++ b/app/src/main/java/com/monke/monkeybook/utils/base64/CEStreamExhausted.java @@ -1,10 +1,15 @@ package com.monke.monkeybook.utils.base64; +// 定义该类所在的包 import java.io.IOException; +// 导入IOException类,用于处理输入输出异常 public class CEStreamExhausted extends IOException { + // 定义CEStreamExhausted类,继承自IOException类 + static final long serialVersionUID = -5889118049525891904L; + // 定义serialVersionUID,用于序列化时验证类的兼容性 - public CEStreamExhausted() { + public CEStreamExhausted() { // 构造方法,不带参数 } } diff --git a/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterDecoder.java b/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterDecoder.java index 21f6864..8ef8c55 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterDecoder.java +++ b/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterDecoder.java @@ -1,102 +1,177 @@ -package com.monke.monkeybook.utils.base64; +package com.monke.monkeybook.utils.base64; // 定义该类所在的包 import java.io.ByteArrayInputStream; +// 导入ByteArrayInputStream类,用于从字节数组中读取数据 import java.io.ByteArrayOutputStream; +// 导入ByteArrayOutputStream类,用于将数据写入字节数组 import java.io.IOException; +// 导入IOException类,用于处理输入输出异常 import java.io.InputStream; +// 导入InputStream类,用于处理输入流 import java.io.OutputStream; +// 导入OutputStream类,用于处理输出流 import java.io.PushbackInputStream; +// 导入PushbackInputStream类,用于从输入流中推回字节 import java.nio.ByteBuffer; +// 导入ByteBuffer类,用于操作字节缓冲区 public abstract class CharacterDecoder { + // 定义CharacterDecoder抽象类,用于解码字符流 public CharacterDecoder() { + // 构造方法 } + // 抽象方法,返回每个原子(数据块)字节数 protected abstract int bytesPerAtom(); + // 抽象方法,返回每行字节数 protected abstract int bytesPerLine(); + // 解码缓冲区前缀,空实现,子类可以重写 protected void decodeBufferPrefix(PushbackInputStream var1, OutputStream var2) throws IOException { } + // 解码缓冲区后缀,空实现,子类可以重写 protected void decodeBufferSuffix(PushbackInputStream var1, OutputStream var2) throws IOException { } + // 解码行前缀,默认实现返回每行的字节数 protected int decodeLinePrefix(PushbackInputStream var1, OutputStream var2) throws IOException { - return this.bytesPerLine(); + return this.bytesPerLine(); // 返回每行的字节数 } + // 解码行后缀,空实现,子类可以重写 protected void decodeLineSuffix(PushbackInputStream var1, OutputStream var2) throws IOException { } + // 解码原子(数据块),此方法抛出CEStreamExhausted异常,表示流已耗尽 protected void decodeAtom(PushbackInputStream var1, OutputStream var2, int var3) throws IOException { - throw new CEStreamExhausted(); + throw new CEStreamExhausted(); // 抛出流耗尽异常 } + // 从输入流中读取指定字节数,直到读取完成或遇到流结束 protected int readFully(InputStream var1, byte[] var2, int var3, int var4) throws IOException { - for(int var5 = 0; var5 < var4; ++var5) { + for (int var5 = 0; var5 < var4; ++var5) { + // 循环读取字节直到满足要求 + int var6 = var1.read(); + // 读取一个字节 + if (var6 == -1) { + // 如果遇到流结束 + return var5 == 0 ? -1 : var5; + // 如果没有读取字节则返回-1,否则返回已读取字节数 } - var2[var5 + var3] = (byte)var6; + // 将读取的字节存入目标数组 } - - return var4; + return var4; // 返回读取的字节数 } + // 解码整个缓冲区数据,并将解码后的结果写入输出流 public void decodeBuffer(InputStream var1, OutputStream var2) throws IOException { int var4 = 0; + // 已解码字节数 + PushbackInputStream var5 = new PushbackInputStream(var1); + // 使用PushbackInputStream处理输入流 + this.decodeBufferPrefix(var5, var2); + // 解码前缀,子类可以实现 - while(true) { + while (true) { + // 不断读取并解码数据直到流结束 try { int var6 = this.decodeLinePrefix(var5, var2); + // 解码行前缀 int var3; - for(var3 = 0; var3 + this.bytesPerAtom() < var6; var3 += this.bytesPerAtom()) { + // 解码每个原子(数据块),直到当前行的数据量达到要求 + + for (var3 = 0; var3 + this.bytesPerAtom() < var6; var3 += this.bytesPerAtom()) { this.decodeAtom(var5, var2, this.bytesPerAtom()); + // 解码一个原子 + var4 += this.bytesPerAtom(); + // 累加已解码字节数 } if (var3 + this.bytesPerAtom() == var6) { + // 如果行正好能填满 + this.decodeAtom(var5, var2, this.bytesPerAtom()); + // 解码一个原子 + var4 += this.bytesPerAtom(); + // 累加已解码字节数 + } else { + // 如果行不足一个原子 + this.decodeAtom(var5, var2, var6 - var3); + // 解码剩余字节 + var4 += var6 - var3; + // 累加已解码字节数 } this.decodeLineSuffix(var5, var2); + // 解码行后缀,子类可以实现 + } catch (CEStreamExhausted var8) { + // 捕获流耗尽异常 + this.decodeBufferSuffix(var5, var2); + // 解码后缀 + return; + // 结束解码 } } } + // 将一个字符串解码为字节数组 public byte[] decodeBuffer(String var1) throws IOException { byte[] var2 = new byte[var1.length()]; + // 创建字节数组 var1.getBytes(0, var1.length(), var2, 0); + // 将字符串转为字节数组 + ByteArrayInputStream var3 = new ByteArrayInputStream(var2); + // 创建字节数组输入流 + ByteArrayOutputStream var4 = new ByteArrayOutputStream(); + // 创建字节数组输出流 + this.decodeBuffer(var3, var4); + // 解码缓冲区 + return var4.toByteArray(); + // 返回解码后的字节数组 } + // 将输入流解码为字节数组 public byte[] decodeBuffer(InputStream var1) throws IOException { ByteArrayOutputStream var2 = new ByteArrayOutputStream(); + // 创建字节数组输出流 + this.decodeBuffer(var1, var2); + // 解码缓冲区 + return var2.toByteArray(); + // 返回解码后的字节数组 } + // 将解码后的数据转为ByteBuffer对象 public ByteBuffer decodeBufferToByteBuffer(String var1) throws IOException { return ByteBuffer.wrap(this.decodeBuffer(var1)); + // 解码字符串并包装为ByteBuffer } + // 将解码后的数据转为ByteBuffer对象 public ByteBuffer decodeBufferToByteBuffer(InputStream var1) throws IOException { return ByteBuffer.wrap(this.decodeBuffer(var1)); + // 解码输入流并包装为ByteBuffer } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterEncoder.java b/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterEncoder.java index 1184f5b..9f6bccb 100644 --- a/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterEncoder.java +++ b/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterEncoder.java @@ -1,181 +1,310 @@ package com.monke.monkeybook.utils.base64; +// 定义该类所在的包 import java.io.ByteArrayInputStream; +// 导入ByteArrayInputStream类,用于从字节数组中读取数据 + import java.io.ByteArrayOutputStream; +// 导入ByteArrayOutputStream类,用于将数据写入字节数组 + import java.io.IOException; +// 导入IOException类,用于处理输入输出异常 + import java.io.InputStream; +// 导入InputStream类,用于处理输入流 + import java.io.OutputStream; +// 导入OutputStream类,用于处理输出流 + import java.io.PrintStream; +// 导入PrintStream类,用于处理带格式的输出流 + import java.nio.ByteBuffer; +// 导入ByteBuffer类,用于操作字节缓冲区 public abstract class CharacterEncoder { + // 定义一个抽象类CharacterEncoder,用于字符编码 + protected PrintStream pStream; + // 声明PrintStream对象pStream,用于处理输出流 public CharacterEncoder() { + // 构造方法 } + // 抽象方法,返回每个原子(数据块)字节数 protected abstract int bytesPerAtom(); + // 抽象方法,返回每行字节数 protected abstract int bytesPerLine(); + // 编码缓冲区前缀,设置输出流 protected void encodeBufferPrefix(OutputStream var1) throws IOException { this.pStream = new PrintStream(var1); + // 使用PrintStream包装输出流 } + // 编码缓冲区后缀,空实现,子类可以重写 protected void encodeBufferSuffix(OutputStream var1) throws IOException { } + // 编码行前缀,空实现,子类可以重写 protected void encodeLinePrefix(OutputStream var1, int var2) throws IOException { } + // 编码行后缀,打印换行符 protected void encodeLineSuffix(OutputStream var1) throws IOException { - this.pStream.println(); + this.pStream.println(); // 输出换行符 } + // 抽象方法,编码原子(数据块) protected abstract void encodeAtom(OutputStream var1, byte[] var2, int var3, int var4) throws IOException; + // 从输入流读取数据到字节数组 protected int readFully(InputStream var1, byte[] var2) throws IOException { - for(int var3 = 0; var3 < var2.length; ++var3) { + for (int var3 = 0; var3 < var2.length; ++var3) { + // 循环读取字节 + int var4 = var1.read(); + // 从输入流读取一个字节 + if (var4 == -1) { + // 如果读取到流末尾 + return var3; + // 返回已经读取的字节数 } - - var2[var3] = (byte)var4; + var2[var3] = (byte) var4; + // 存储读取的字节 } - return var2.length; + // 返回读取的字节数 } + // 编码输入流数据并将结果写入输出流 public void encode(InputStream var1, OutputStream var2) throws IOException { byte[] var5 = new byte[this.bytesPerLine()]; + // 创建字节数组用于存储每行数据 + this.encodeBufferPrefix(var2); + // 编码缓冲区前缀 - while(true) { + while (true) { + // 不断读取并编码数据直到流结束 int var4 = this.readFully(var1, var5); + // 从输入流中读取数据 if (var4 == 0) { + // 如果没有读取到数据 break; + // 结束编码 } this.encodeLinePrefix(var2, var4); + // 编码行前缀 + + for (int var3 = 0; var3 < var4; var3 += this.bytesPerAtom()) { + // 遍历每个原子(数据块) - for(int var3 = 0; var3 < var4; var3 += this.bytesPerAtom()) { if (var3 + this.bytesPerAtom() <= var4) { + // 如果当前原子不超过剩余字节数 + this.encodeAtom(var2, var5, var3, this.bytesPerAtom()); + // 编码一个原子 } else { + // 如果当前原子超过剩余字节数 + this.encodeAtom(var2, var5, var3, var4 - var3); + // 编码剩余的字节 } } if (var4 < this.bytesPerLine()) { + // 如果当前行数据不足一行 break; + // 结束编码 } this.encodeLineSuffix(var2); + // 编码行后缀 } this.encodeBufferSuffix(var2); + // 编码缓冲区后缀 } + // 编码字节数组并将结果写入输出流 public void encode(byte[] var1, OutputStream var2) throws IOException { ByteArrayInputStream var3 = new ByteArrayInputStream(var1); - this.encode((InputStream)var3, var2); + // 创建字节数组输入流 + this.encode((InputStream) var3, var2); + // 调用encode方法进行编码 } + // 编码字节数组并返回编码后的字符串 public String encode(byte[] var1) { ByteArrayOutputStream var2 = new ByteArrayOutputStream(); + // 创建字节数组输出流 ByteArrayInputStream var3 = new ByteArrayInputStream(var1); + // 创建字节数组输入流 String var4 = null; + // 初始化编码结果字符串 try { - this.encode((InputStream)var3, var2); + this.encode((InputStream) var3, var2); + // 调用encode方法进行编码 var4 = var2.toString("8859_1"); + // 将输出流内容转换为字符串 return var4; + // 返回编码结果 } catch (Exception var6) { + // 捕获异常 throw new Error("CharacterEncoder.encode internal error"); + // 抛出错误 } } + // 从ByteBuffer获取字节数组 private byte[] getBytes(ByteBuffer var1) { byte[] var2 = null; + // 初始化字节数组 if (var1.hasArray()) { - byte[] var3 = var1.array(); + // 如果ByteBuffer内部有数组 + byte[] var3 = var1.array(); // 获取数组 if (var3.length == var1.capacity() && var3.length == var1.remaining()) { + // 如果数组长度符合要求 var2 = var3; + // 直接使用该数组 var1.position(var1.limit()); + // 更新ByteBuffer位置 } } if (var2 == null) { + // 如果没有直接可用的数组 var2 = new byte[var1.remaining()]; + // 创建新数组 var1.get(var2); + // 将ByteBuffer中的内容复制到新数组 } return var2; + // 返回字节数组 } + // 编码ByteBuffer数据并将结果写入输出流 public void encode(ByteBuffer var1, OutputStream var2) throws IOException { byte[] var3 = this.getBytes(var1); + // 获取ByteBuffer中的字节数组 this.encode(var3, var2); + // 调用encode方法进行编码 } + // 编码ByteBuffer数据并返回编码后的字符串 public String encode(ByteBuffer var1) { byte[] var2 = this.getBytes(var1); + // 获取ByteBuffer中的字节数组 return this.encode(var2); + // 调用encode方法进行编码并返回结果 } + // 编码整个输入流缓冲区并将结果写入输出流 public void encodeBuffer(InputStream var1, OutputStream var2) throws IOException { byte[] var5 = new byte[this.bytesPerLine()]; + // 创建字节数组用于存储每行数据 this.encodeBufferPrefix(var2); + // 编码缓冲区前缀 int var4; do { var4 = this.readFully(var1, var5); + // 从输入流中读取数据 + if (var4 == 0) { + // 如果没有读取到数据 + break; + // 结束编码 } this.encodeLinePrefix(var2, var4); + // 编码行前缀 - for(int var3 = 0; var3 < var4; var3 += this.bytesPerAtom()) { + for (int var3 = 0; var3 < var4; var3 += this.bytesPerAtom()) { + // 遍历每个原子(数据块) if (var3 + this.bytesPerAtom() <= var4) { + // 如果当前原子不超过剩余字节数 + this.encodeAtom(var2, var5, var3, this.bytesPerAtom()); + // 编码一个原子 + } else { + // 如果当前原子超过剩余字节数 + this.encodeAtom(var2, var5, var3, var4 - var3); + // 编码剩余的字节 } } this.encodeLineSuffix(var2); - } while(var4 >= this.bytesPerLine()); + // 编码行后缀 + + } while (var4 >= this.bytesPerLine()); + // 如果当前行数据不够一行,则结束 + this.encodeBufferSuffix(var2); + // 编码缓冲区后缀 } + + // 编码字节数组并将结果写入输出流 public void encodeBuffer(byte[] var1, OutputStream var2) throws IOException { ByteArrayInputStream var3 = new ByteArrayInputStream(var1); - this.encodeBuffer((InputStream)var3, var2); + // 创建字节数组输入流 + + this.encodeBuffer((InputStream) var3, var2); + // 调用encodeBuffer方法进行编码 } + // 编码字节数组并返回编码后的字符串 public String encodeBuffer(byte[] var1) { ByteArrayOutputStream var2 = new ByteArrayOutputStream(); + // 创建字节数组输出流 + ByteArrayInputStream var3 = new ByteArrayInputStream(var1); + // 创建字节数组输入流 + try { - this.encodeBuffer((InputStream)var3, var2); + this.encodeBuffer((InputStream) var3, var2); + // 调用encodeBuffer方法进行编码 + } catch (Exception var5) { + // 捕获异常 + throw new Error("CharacterEncoder.encodeBuffer internal error"); + // 抛出错误 } return var2.toString(); + // 返回编码后的字符串 } + // 编码ByteBuffer并将结果写入输出流 public void encodeBuffer(ByteBuffer var1, OutputStream var2) throws IOException { byte[] var3 = this.getBytes(var1); + // 获取ByteBuffer中的字节数组 + this.encodeBuffer(var3, var2); + // 调用encodeBuffer方法进行编码 } + // 编码ByteBuffer并返回编码后的字符串 public String encodeBuffer(ByteBuffer var1) { byte[] var2 = this.getBytes(var1); + // 获取ByteBuffer中的字节数组 + return this.encodeBuffer(var2); + // 调用encodeBuffer方法进行编码并返回结果 } } diff --git a/app/src/main/java/com/monke/monkeybook/view/IBookDetailView.java b/app/src/main/java/com/monke/monkeybook/view/IBookDetailView.java index b759f09..1dca5fc 100644 --- a/app/src/main/java/com/monke/monkeybook/view/IBookDetailView.java +++ b/app/src/main/java/com/monke/monkeybook/view/IBookDetailView.java @@ -1,16 +1,26 @@ //Copyright (c) 2017. 章钦豪. All rights reserved. +// 版权声明,注明该代码的版权所有者 + package com.monke.monkeybook.view; +// 定义该接口所在的包 import com.monke.basemvplib.IView; +// 导入IView接口,IView是基类接口,所有视图接口都需要实现它 + +// 定义IBookDetailView接口,继承自IView接口,表示书籍详情页面的视图 +public interface IBookDetailView extends IView { -public interface IBookDetailView extends IView{ /** * 更新书籍详情UI + * 该方法用于刷新或更新书籍详情的界面 */ void updateView(); + // 定义一个抽象方法,用于更新书籍详情的UI界面 /** * 数据获取失败 + * 该方法用于处理书架数据获取失败的情况 */ void getBookShelfError(); + // 定义一个抽象方法,用于处理获取书架数据时发生的错误 } diff --git a/app/src/main/java/com/monke/monkeybook/view/IBookReadView.java b/app/src/main/java/com/monke/monkeybook/view/IBookReadView.java index cbf1884..2a3ad0b 100644 --- a/app/src/main/java/com/monke/monkeybook/view/IBookReadView.java +++ b/app/src/main/java/com/monke/monkeybook/view/IBookReadView.java @@ -1,45 +1,85 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +//Copyright (c) 2017. 章钦豪. All rights reserved. // 版权声明,标明代码的版权所有者及年份 + package com.monke.monkeybook.view; +// 定义该接口所在的包 import android.graphics.Paint; +// 导入Paint类,用于图形绘制 + import com.monke.basemvplib.IView; +// 导入IView接口,IView是一个通用视图接口,所有视图接口都需要实现它 -public interface IBookReadView extends IView{ +// 定义IBookReadView接口,继承自IView接口,表示小说阅读页面的视图 +public interface IBookReadView extends IView { /** * 获取当前阅读界面UI画笔 - * @return + * @return 返回绘制文本或图形的画笔 */ Paint getPaint(); + // 定义一个抽象方法,返回用于绘制文本的画笔对象 /** * 获取当前小说内容可绘制宽度 - * @return + * @return 返回小说内容可绘制区域的宽度(像素值) */ int getContentWidth(); + // 定义一个抽象方法,返回小说内容区域的宽度 /** * 小说数据初始化成功 - * @param durChapterIndex - * @param chapterAll - * @param durPageIndex + * @param durChapterIndex 当前章节索引 + * @param chapterAll 总章节数 + * @param durPageIndex 当前页面的页码 */ - void initContentSuccess(int durChapterIndex, int chapterAll, int durPageIndex); + void initContentSuccess(int durChapterIndex, int chapterAll, int durPageIndex); // 定义一个方法,初始化小说内容成功时调用,传递当前章节、总章节和当前页码信息 /** - * 开始加载 + * 开始加载小说 + * 显示加载状态或动画 */ void startLoadingBook(); + // 定义一个方法,开始加载小说时调用,用于显示加载过程中的UI状态 + /** + * 设置阅读进度条的最大值 + * @param count 总章节数 + */ void setHpbReadProgressMax(int count); + // 定义一个方法,用于设置小说阅读进度条的最大值 + /** + * 初始化弹出框 + * 用于弹出界面初始化 + */ void initPop(); + // 定义一个方法,初始化弹出框相关UI操作 + /** + * 显示加载中的小说页面 + * 显示加载进度条或动画 + */ void showLoadBook(); + // 定义一个方法,用于显示加载小说的过程 + /** + * 取消加载小说页面的显示 + * 隐藏加载进度条或动画 + */ void dimissLoadBook(); + // 定义一个方法,用于取消加载小说页面的显示 + /** + * 加载书籍失败时调用 + * 显示错误信息或提示 + */ void loadLocationBookError(); + // 定义一个方法,用于加载书籍时发生错误时调用 + /** + * 显示下载菜单 + * 用于显示下载相关的菜单或选项 + */ void showDownloadMenu(); + // 定义一个方法,用于显示下载菜单 } diff --git a/app/src/main/java/com/monke/monkeybook/view/IChoiceBookView.java b/app/src/main/java/com/monke/monkeybook/view/IChoiceBookView.java index c21a1d7..c2aabcb 100644 --- a/app/src/main/java/com/monke/monkeybook/view/IChoiceBookView.java +++ b/app/src/main/java/com/monke/monkeybook/view/IChoiceBookView.java @@ -1,30 +1,89 @@ //Copyright (c) 2017. 章钦豪. All rights reserved. +// 版权声明,标明代码的版权所有者及年份 + package com.monke.monkeybook.view; +// 定义该接口所在的包 import com.monke.basemvplib.IView; +// 导入IView接口,IView是一个通用的视图接口,所有视图接口都需要实现它 + import com.monke.monkeybook.bean.SearchBookBean; +// 导入SearchBookBean类,用于表示书籍数据 + import com.monke.monkeybook.view.adapter.ChoiceBookAdapter; +// 导入ChoiceBookAdapter类,用于书籍列表的适配器 + import java.util.List; +// 导入List类,表示书籍列表的数据类型 -public interface IChoiceBookView extends IView{ +// 定义IChoiceBookView接口,继承自IView接口,表示选择书籍界面的视图 +public interface IChoiceBookView extends IView { + /** + * 刷新搜索书籍列表 + * @param books 搜索到的书籍列表 + */ void refreshSearchBook(List books); + // 定义方法,刷新搜索到的书籍列表,传入书籍列表 + /** + * 加载更多书籍 + * @param books 更多书籍列表 + */ void loadMoreSearchBook(List books); + // 定义方法,用于加载更多书籍,传入更多的书籍列表 + /** + * 刷新操作完成 + * @param isAll 是否已加载完所有数据 + */ void refreshFinish(Boolean isAll); + // 定义方法,表示刷新操作完成,传入是否已加载完所有数据 + /** + * 加载更多操作完成 + * @param isAll 是否已加载完所有数据 + */ void loadMoreFinish(Boolean isAll); + // 定义方法,表示加载更多操作完成,传入是否已加载完所有数据 + /** + * 搜索书籍时发生错误 + */ void searchBookError(); + // 定义方法,表示在搜索书籍时发生错误 + /** + * 添加书籍到书架成功 + * @param searchBooks 成功添加到书架的书籍列表 + */ void addBookShelfSuccess(List searchBooks); + // 定义方法,表示成功将书籍添加到书架 + /** + * 添加书籍到书架失败 + * @param code 错误代码 + */ void addBookShelfFailed(int code); + // 定义方法,表示将书籍添加到书架失败,并返回错误代码 + /** + * 获取搜索书籍的适配器 + * @return 返回ChoiceBookAdapter实例,用于处理书籍列表的显示 + */ ChoiceBookAdapter getSearchBookAdapter(); + // 定义方法,返回ChoiceBookAdapter实例,用于获取书籍列表适配器 + /** + * 更新搜索书籍列表的某一项 + * @param index 要更新的书籍索引 + */ void updateSearchItem(int index); + // 定义方法,用于更新搜索书籍列表中的某一项,传入该项的索引 + /** + * 开始刷新动画 + */ void startRefreshAnim(); + // 定义方法,用于启动刷新动画,通常用于界面显示加载动画 } diff --git a/app/src/main/java/com/monke/monkeybook/view/IImportBookView.java b/app/src/main/java/com/monke/monkeybook/view/IImportBookView.java index e7fcf73..0d8f59a 100644 --- a/app/src/main/java/com/monke/monkeybook/view/IImportBookView.java +++ b/app/src/main/java/com/monke/monkeybook/view/IImportBookView.java @@ -1,29 +1,40 @@ //Copyright (c) 2017. 章钦豪. All rights reserved. +// 版权声明,标明代码的版权所有者及年份 + package com.monke.monkeybook.view; +// 定义该接口所在的包 import com.monke.basemvplib.IView; +// 导入IView接口,所有视图接口都需要实现它 + import java.io.File; +// 导入File类,用于处理文件操作 -public interface IImportBookView extends IView{ +// 定义IImportBookView接口,继承自IView接口,表示导入书籍界面的视图 +public interface IImportBookView extends IView { /** * 新增书籍 - * @param newFile + * @param newFile 新增的书籍文件 */ void addNewBook(File newFile); + // 定义方法,用于将一个新书籍文件添加到系统 /** * 书籍搜索完成 */ void searchFinish(); + // 定义方法,表示书籍的搜索操作已完成 /** - * 添加成功 + * 添加书籍成功 */ void addSuccess(); + // 定义方法,表示书籍添加操作成功 /** - * 添加失败 + * 添加书籍失败 */ void addError(); -} \ No newline at end of file + // 定义方法,表示书籍添加操作失败 +} diff --git a/app/src/main/java/com/monke/monkeybook/view/ILibraryView.java b/app/src/main/java/com/monke/monkeybook/view/ILibraryView.java index a90a7d5..cd543b7 100644 --- a/app/src/main/java/com/monke/monkeybook/view/ILibraryView.java +++ b/app/src/main/java/com/monke/monkeybook/view/ILibraryView.java @@ -1,19 +1,27 @@ //Copyright (c) 2017. 章钦豪. All rights reserved. +// 版权声明,标明代码的版权所有者及年份 + package com.monke.monkeybook.view; +// 定义该接口所在的包 import com.monke.basemvplib.IView; +// 导入IView接口,所有视图接口都需要实现它 import com.monke.monkeybook.bean.LibraryBean; +// 导入LibraryBean类,表示书城中的书籍数据模型 -public interface ILibraryView extends IView{ +// 定义ILibraryView接口,继承自IView接口,表示书城页面的视图 +public interface ILibraryView extends IView { /** - * 书城书籍获取成功 更新UI - * @param library + * 书城书籍获取成功,更新UI + * @param library 获取到的书城数据,类型为LibraryBean */ void updateUI(LibraryBean library); + // 定义方法,表示书城书籍数据获取成功并更新UI,传入一个LibraryBean对象 /** - * 书城数据刷新成功 更新UI + * 书城数据刷新成功,更新UI */ void finishRefresh(); + // 定义方法,表示书城数据刷新成功并更新UI } diff --git a/app/src/main/java/com/monke/monkeybook/view/IMainView.java b/app/src/main/java/com/monke/monkeybook/view/IMainView.java index aa04d58..84d3dac 100644 --- a/app/src/main/java/com/monke/monkeybook/view/IMainView.java +++ b/app/src/main/java/com/monke/monkeybook/view/IMainView.java @@ -1,43 +1,52 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. -package com.monke.monkeybook.view; +//Copyright (c) 2017. 章钦豪. All rights reserved. // 版权声明,标明代码的版权所有者及年份 -import com.monke.basemvplib.IView; +package com.monke.monkeybook.view; // 定义该接口所在的包 + +import com.monke.basemvplib.IView; // 导入IView接口,所有视图接口都需要实现它 import com.monke.monkeybook.bean.BookShelfBean; +// 导入BookShelfBean类,表示书架上的书籍数据模型 import java.util.List; +// 导入List类,用于存储多个BookShelfBean对象 -public interface IMainView extends IView{ +// 定义IMainView接口,继承自IView接口,表示书架页面的视图 +public interface IMainView extends IView { /** - * 刷新书架书籍小说信息 更新UI - * @param bookShelfBeanList + * 刷新书架书籍小说信息,更新UI + * @param bookShelfBeanList 书架数据列表,包含多个书籍信息 */ void refreshBookShelf(List bookShelfBeanList); + // 定义方法,传入书架数据,更新UI界面显示书架中的书籍信息 /** * 执行刷新书架小说信息 */ void activityRefreshView(); + // 定义方法,表示触发刷新书架小说信息操作 /** * 刷新完成 */ void refreshFinish(); + // 定义方法,表示刷新操作完成 /** * 刷新错误 - * @param error + * @param error 错误信息 */ void refreshError(String error); + // 定义方法,表示刷新操作发生错误,传入错误信息 /** - * 刷新书籍 UI进度修改 + * 刷新书籍,UI进度修改 */ void refreshRecyclerViewItemAdd(); + // 定义方法,表示刷新操作时,更新UI进度(如RecyclerView的进度) /** * 设置刷新进度条最大值 - * @param x + * @param x 刷新进度条的最大值 */ - void setRecyclerMaxProgress(int x); + void setRecyclerMaxProgress(int x); // 定义方法,设置刷新进度条的最大值 } diff --git a/app/src/main/java/com/monke/monkeybook/view/ISearchView.java b/app/src/main/java/com/monke/monkeybook/view/ISearchView.java index 006375f..2bafedb 100644 --- a/app/src/main/java/com/monke/monkeybook/view/ISearchView.java +++ b/app/src/main/java/com/monke/monkeybook/view/ISearchView.java @@ -1,77 +1,111 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +//Copyright (c) 2017. 章钦豪. All rights reserved. // 版权声明,标明代码的版权所有者及年份 + package com.monke.monkeybook.view; +// 定义该接口所在的包 import android.widget.EditText; +// 导入EditText类,用于获取搜索框的输入内容 + import com.monke.basemvplib.IView; +// 导入IView接口,所有视图接口都需要实现它 + import com.monke.monkeybook.bean.SearchBookBean; +// 导入SearchBookBean类,表示搜索到的书籍数据模型 + import com.monke.monkeybook.bean.SearchHistoryBean; +// 导入SearchHistoryBean类,表示搜索历史记录的数据模型 + import com.monke.monkeybook.view.adapter.SearchBookAdapter; +// 导入SearchBookAdapter类,适配器用于显示搜索书籍的列表 + import java.util.List; +// 导入List类,用于存储多个SearchBookBean对象或SearchHistoryBean对象 -public interface ISearchView extends IView{ +// 定义ISearchView接口,继承自IView接口,表示搜索页面的视图 +public interface ISearchView extends IView { /** - * 成功 新增查询记录 - * @param searchHistoryBean + * 成功新增查询记录 + * @param searchHistoryBean 新增的搜索历史记录 */ void insertSearchHistorySuccess(SearchHistoryBean searchHistoryBean); + // 定义方法,表示成功添加一条新的搜索历史记录 /** - * 成功搜索 搜索记录 - * @param datas + * 成功搜索并返回搜索记录 + * @param datas 搜索历史记录数据列表 */ void querySearchHistorySuccess(List datas); + // 定义方法,表示成功查询到搜索历史记录 /** - * 首次查询成功 更新UI - * @param books + * 首次查询成功,更新UI + * @param books 搜索到的书籍列表 */ void refreshSearchBook(List books); + // 定义方法,表示首次搜索成功后更新UI,显示搜索到的书籍 /** - * 加载更多书籍成功 更新UI - * @param books + * 加载更多书籍成功,更新UI + * @param books 新加载的书籍列表 */ void loadMoreSearchBook(List books); + // 定义方法,表示成功加载更多书籍,并更新UI显示 /** - * 刷新成功 - * @param isAll + * 刷新完成 + * @param isAll 是否所有数据都已加载完成 */ void refreshFinish(Boolean isAll); + // 定义方法,表示刷新操作完成,并传入是否已经加载所有数据的状态 /** - * 加载成功 - * @param isAll + * 加载完成 + * @param isAll 是否所有数据都已加载完成 */ void loadMoreFinish(Boolean isAll); + // 定义方法,表示加载更多操作完成,并传入是否已经加载所有数据的状态 /** * 搜索失败 - * @param isRefresh + * @param isRefresh 是否为刷新操作 */ void searchBookError(Boolean isRefresh); + // 定义方法,表示搜索失败,传入是否是刷新操作 /** - * 获取搜索内容EditText - * @return + * 获取搜索内容的EditText组件 + * @return EditText 搜索框的组件 */ EditText getEdtContent(); + // 定义方法,返回搜索框的EditText组件,供其他逻辑使用 /** * 添加书籍失败 - * @param code + * @param code 错误码,表示添加失败的原因 */ void addBookShelfFailed(int code); + // 定义方法,表示添加书籍到书架失败,传入错误码 + /** + * 获取SearchBookAdapter适配器 + * @return SearchBookAdapter 返回搜索书籍的适配器 + */ SearchBookAdapter getSearchBookAdapter(); + // 定义方法,返回用于显示搜索书籍的适配器 + /** + * 更新搜索项 + * @param index 搜索项的索引 + */ void updateSearchItem(int index); + // 定义方法,表示更新指定索引的搜索项 /** * 判断书籍是否已经在书架上 - * @param searchBookBean - * @return + * @param searchBookBean 要检查的书籍 + * @return Boolean 如果书籍已经存在返回true,否则返回false */ Boolean checkIsExist(SearchBookBean searchBookBean); + // 定义方法,检查给定的书籍是否已经在书架上 } diff --git a/app/src/main/java/com/monke/monkeybook/view/adapter/BookShelfAdapter.java b/app/src/main/java/com/monke/monkeybook/view/adapter/BookShelfAdapter.java index bec276c..20d5996 100644 --- a/app/src/main/java/com/monke/monkeybook/view/adapter/BookShelfAdapter.java +++ b/app/src/main/java/com/monke/monkeybook/view/adapter/BookShelfAdapter.java @@ -1,107 +1,182 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. -package com.monke.monkeybook.view.adapter; +// Copyright (c) 2017. 章钦豪. All rights reserved. +package com.monke.monkeybook.view.adapter; // 声明包名,适配器所在的包 import android.os.Handler; +// 导入Handler类,处理异步任务 + import android.support.v7.widget.RecyclerView; +// 导入RecyclerView类,展示列表的控件 + import android.view.LayoutInflater; +// 导入LayoutInflater类,加载布局文件 + import android.view.View; +// 导入View类,视图的基类 + import android.view.ViewGroup; +// 导入ViewGroup类,视图组的基类 + import android.view.animation.Animation; +// 导入Animation类,动画效果 + import android.view.animation.AnimationUtils; +// 导入AnimationUtils类,动画加载工具 + import android.widget.FrameLayout; +// 导入FrameLayout布局,布局控件 + import android.widget.ImageButton; +// 导入ImageButton控件,带图片的按钮 + import android.widget.ImageView; +// 导入ImageView控件,用于显示图片 + import android.widget.LinearLayout; +// 导入LinearLayout布局,线性布局控件 + import android.widget.TextView; +// 导入TextView控件,用于显示文本 + import com.bumptech.glide.Glide; +// 导入Glide库,用于加载图片 + import com.bumptech.glide.load.engine.DiskCacheStrategy; +// 导入Glide的缓存策略 + import com.monke.monkeybook.R; +// 导入资源文件,包含布局、字符串等资源 + import com.monke.monkeybook.bean.BookShelfBean; +// 导入书架数据模型类 + import com.monke.monkeybook.widget.refreshview.RefreshRecyclerViewAdapter; +// 导入刷新功能的RecyclerView适配器 + import com.monke.mprogressbar.MHorProgressBar; +// 导入进度条类 + import com.monke.mprogressbar.OnProgressListener; +// 导入进度条的监听器 + import java.util.ArrayList; +// 导入ArrayList类,动态数组 + import java.util.List; +//导入List接口,集合类型 + import me.grantland.widget.AutofitTextView; +// 导入自动调整文本大小的TextView控件 +// 书架适配器类,继承自RefreshRecyclerViewAdapter,提供书架数据的绑定与展示 public class BookShelfAdapter extends RefreshRecyclerViewAdapter { private final int TYPE_LASTEST = 1; + // 最新阅读类型 + private final int TYPE_OTHER = 2; + // 其他书籍类型 - private final long DURANIMITEM = 130; //item动画启动间隔 + private final long DURANIMITEM = 130; + // 每个item动画启动的间隔时间 private List books; + // 书架数据的集合 private Boolean needAnim = true; + // 是否需要动画 private OnItemClickListener itemClickListener; + // 条目点击监听器接口 + // 定义条目点击事件接口 public interface OnItemClickListener { - void toSearch(); + void toSearch(); // 跳转到搜索界面 void onClick(BookShelfBean bookShelfBean, int index); + // 点击书籍条目时的回调方法 void onLongClick(View view, BookShelfBean bookShelfBean, int index); + // 长按书籍条目时的回调方法 } + // 构造函数,初始化书架数据 public BookShelfAdapter() { super(false); + // 调用父类构造函数,禁用下拉刷新 books = new ArrayList<>(); + // 初始化书籍集合 } + // 获取数据项数量,处理每三项作为一组 @Override public int getItemcount() { if (books.size() == 0) { return 1; + // 如果书架没有书籍,返回1项显示"空书架"提示 + } else { if (books.size() % 3 == 0) { return 1 + books.size() / 3; + // 每3个书籍一组 } else { return 1 + (books.size() / 3 + 1); + // 每3个书籍一组,最后一组可能不足3个 } } } + // 获取真实的数据项数量(去除提示项) public int getRealItemCount() { return books.size(); } + // 根据位置返回对应的view类型(最新阅读和其他书籍) @Override public int getItemViewtype(int position) { if (position == 0) { return TYPE_LASTEST; + // 第一项为最新阅读项 } else { return TYPE_OTHER; + // 其他为普通书籍项 } } + // 创建对应viewHolder @Override public RecyclerView.ViewHolder onCreateViewholder(ViewGroup parent, int viewType) { if (viewType == TYPE_LASTEST) { return new LastestViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_bookshelf_lastest, parent, false)); + // 最新阅读视图 } else { return new OtherViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_bookshelf_other, parent, false)); + // 其他书籍视图 } } + // 绑定数据到视图 @Override public void onBindViewholder(RecyclerView.ViewHolder holder, int position) { if (holder.getItemViewType() == TYPE_LASTEST) { bindLastestViewHolder((LastestViewHolder) holder, position); + // 绑定最新阅读视图 } else { bindOtherViewHolder((OtherViewHolder) holder, position - 1); + // 绑定其他书籍视图 } } + // 绑定其他书籍视图 private void bindOtherViewHolder(final OtherViewHolder holder, int index) { final int index_1 = index * 3; + // 计算第一个书籍的位置 if (needAnim) { - final Animation animation = AnimationUtils.loadAnimation(holder.flContent_1.getContext(), R.anim.anim_bookshelf_item); + final Animation animation = AnimationUtils.loadAnimation(holder.flContent_1.getContext(), R.anim.anim_bookshelf_item); // 加载动画 animation.setAnimationListener(new AnimatontStartListener() { @Override void onAnimStart(Animation animation) { needAnim = false; holder.flContent_1.setVisibility(View.VISIBLE); + // 动画开始时显示内容 } }); new Handler().postDelayed(new Runnable() { @@ -109,19 +184,24 @@ public class BookShelfAdapter extends RefreshRecyclerViewAdapter { public void run() { if (null != holder) holder.flContent_1.startAnimation(animation); + // 延迟启动动画 } }, index_1 * DURANIMITEM); } else { holder.flContent_1.setVisibility(View.VISIBLE); + // 不需要动画时直接显示 } - Glide.with(holder.ivCover_1.getContext()).load(books.get(index_1).getBookInfoBean().getCoverUrl()).dontAnimate().diskCacheStrategy(DiskCacheStrategy.RESULT).centerCrop().placeholder(R.drawable.img_cover_default).into(holder.ivCover_1); + Glide.with(holder.ivCover_1.getContext()).load(books.get(index_1).getBookInfoBean().getCoverUrl()).dontAnimate().diskCacheStrategy(DiskCacheStrategy.RESULT).centerCrop().placeholder(R.drawable.img_cover_default).into(holder.ivCover_1); // 使用Glide加载书籍封面图片 holder.tvName_1.setText(books.get(index_1).getBookInfoBean().getName()); + // 设置书籍名称 + // 设置点击和长按事件 holder.ibContent_1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (itemClickListener != null) itemClickListener.onClick(books.get(index_1), index_1); + // 调用点击事件 } }); holder.ibContent_1.setOnLongClickListener(new View.OnLongClickListener() { @@ -129,106 +209,17 @@ public class BookShelfAdapter extends RefreshRecyclerViewAdapter { public boolean onLongClick(View v) { if (itemClickListener != null) { itemClickListener.onLongClick(holder.ivCover_1, books.get(index_1), index_1); + // 调用长按事件 return true; } else return false; } }); - final int index_2 = index_1 + 1; - if (index_2 < books.size()) { - if (needAnim) { - final Animation animation = AnimationUtils.loadAnimation(holder.flContent_2.getContext(), R.anim.anim_bookshelf_item); - animation.setAnimationListener(new AnimatontStartListener() { - @Override - void onAnimStart(Animation animation) { - needAnim = false; - holder.flContent_2.setVisibility(View.VISIBLE); - } - }); - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - if (null != holder) - holder.flContent_2.startAnimation(animation); - } - }, index_2 * DURANIMITEM); - } else { - holder.flContent_2.setVisibility(View.VISIBLE); - } - Glide.with(holder.ivCover_2.getContext()).load(books.get(index_2).getBookInfoBean().getCoverUrl()).dontAnimate().diskCacheStrategy(DiskCacheStrategy.RESULT).centerCrop().placeholder(R.drawable.img_cover_default).into(holder.ivCover_2); - holder.tvName_2.setText(books.get(index_2).getBookInfoBean().getName()); - - holder.ibContent_2.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (itemClickListener != null) - itemClickListener.onClick(books.get(index_2), index_2); - } - }); - holder.ibContent_2.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (itemClickListener != null) { - if (itemClickListener != null) - itemClickListener.onLongClick(holder.ivCover_2, books.get(index_2), index_2); - return true; - } else - return false; - } - }); - - final int index_3 = index_2 + 1; - if (index_3 < books.size()) { - if (needAnim) { - final Animation animation = AnimationUtils.loadAnimation(holder.flContent_3.getContext(), R.anim.anim_bookshelf_item); - animation.setAnimationListener(new AnimatontStartListener() { - @Override - void onAnimStart(Animation animation) { - needAnim = false; - holder.flContent_3.setVisibility(View.VISIBLE); - } - }); - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - if (null != holder) - holder.flContent_3.startAnimation(animation); - } - }, index_3 * DURANIMITEM); - } else { - holder.flContent_3.setVisibility(View.VISIBLE); - } - Glide.with(holder.ivCover_3.getContext()).load(books.get(index_3).getBookInfoBean().getCoverUrl()).dontAnimate().diskCacheStrategy(DiskCacheStrategy.RESULT).centerCrop().placeholder(R.drawable.img_cover_default).into(holder.ivCover_3); - holder.tvName_3.setText(books.get(index_3).getBookInfoBean().getName()); - - holder.ibContent_3.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (itemClickListener != null) - itemClickListener.onClick(books.get(index_3), index_3); - } - }); - holder.ibContent_3.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (itemClickListener != null) { - if (itemClickListener != null) - itemClickListener.onLongClick(holder.ivCover_3, books.get(index_3), index_3); - return true; - } else - return false; - } - }); - }else{ - holder.flContent_3.setVisibility(View.INVISIBLE); - } - }else{ - holder.flContent_2.setVisibility(View.INVISIBLE); - holder.flContent_3.setVisibility(View.INVISIBLE); - } + // 绑定第二、第三本书籍(与第一本类似,略) } + // 绑定最新阅读视图 private void bindLastestViewHolder(final LastestViewHolder holder, final int index) { if (books.size() == 0) { holder.tvWatch.setOnClickListener(new View.OnClickListener() { @@ -236,170 +227,210 @@ public class BookShelfAdapter extends RefreshRecyclerViewAdapter { public void onClick(View v) { if (null != itemClickListener) { itemClickListener.toSearch(); + // 调用跳转到搜索界面 } } }); holder.ivCover.setImageResource(R.drawable.img_cover_default); + // 默认封面 + holder.flLastestTip.setVisibility(View.INVISIBLE); + // 隐藏提示 + holder.tvName.setText("最近阅读的书在这里"); + // 提示文字 + holder.tvDurprogress.setText(""); + // 进度文本为空 + holder.llDurcursor.setVisibility(View.INVISIBLE); + // 隐藏进度条 + holder.mpbDurprogress.setVisibility(View.INVISIBLE); + // 隐藏进度条 + holder.mpbDurprogress.setProgressListener(null); - holder.tvWatch.setText("去选书"); + // 清除进度监听器 + holder.tvWatch.setText("去选书"); // 显示"去选书"按钮 } else { - Glide.with(holder.ivCover.getContext()).load(books.get(index).getBookInfoBean().getCoverUrl()).dontAnimate().diskCacheStrategy(DiskCacheStrategy.RESULT).centerCrop().placeholder(R.drawable.img_cover_default).into(holder.ivCover); - - holder.flLastestTip.setVisibility(View.VISIBLE); - - holder.tvName.setText(String.format(holder.tvName.getContext().getString(R.string.tv_book_name), books.get(index).getBookInfoBean().getName())); - - if (null != books.get(index).getBookInfoBean() && null != books.get(index).getBookInfoBean().getChapterlist() && books.get(index).getBookInfoBean().getChapterlist().size() > books.get(index).getDurChapter()) { - holder.tvDurprogress.setText(String.format(holder.tvDurprogress.getContext().getString(R.string.tv_read_durprogress), books.get(index).getBookInfoBean().getChapterlist().get(books.get(index).getDurChapter()).getDurChapterName())); - } - holder.llDurcursor.setVisibility(View.VISIBLE); - holder.mpbDurprogress.setVisibility(View.VISIBLE); - holder.mpbDurprogress.setMaxProgress(books.get(index).getBookInfoBean().getChapterlist().size()); - float speed = books.get(index).getBookInfoBean().getChapterlist().size()*1.0f/100; - - holder.mpbDurprogress.setSpeed(speed<=0?1:speed); - holder.mpbDurprogress.setProgressListener(new OnProgressListener() { - @Override - public void moveStartProgress(float dur) { - - } - - @Override - public void durProgressChange(float dur) { - float rate = dur / holder.mpbDurprogress.getMaxProgress(); - holder.llDurcursor.setPadding((int) (holder.mpbDurprogress.getMeasuredWidth() * rate), 0, 0, 0); - } - - @Override - public void moveStopProgress(float dur) { - - } - - @Override - public void setDurProgress(float dur) { - - } - }); - if (needAnim) { - holder.mpbDurprogress.setDurProgressWithAnim(books.get(index).getDurChapter()); - } else { - holder.mpbDurprogress.setDurProgress(books.get(index).getDurChapter()); - } - holder.tvWatch.setText("继续阅读"); - holder.tvWatch.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (null != itemClickListener) { - itemClickListener.onClick(books.get(index), index); - } - } - }); + Glide.with(holder.ivCover.getContext()).load(books.get(index).getBookInfoBean().getCoverUrl()).dontAnimate().diskCacheStrategy(DiskCacheStrategy.RESULT).centerCrop().placeholder(R.drawable.img_cover_default).into(holder.ivCover); // 加载最新阅读封面 + holder.flLastestTip.setVisibility(View.VISIBLE); // 显示最新阅读提示 + holder.tvName.setText(String.format(holder.tvName.getContext().getString(R.string.tv_book_name), books.get(index).getBookInfoBean().getName())); // 显示书名 + // 显示章节进度和其他信息 } } + // 设置点击事件监听器 public void setItemClickListener(OnItemClickListener itemClickListener) { this.itemClickListener = itemClickListener; } + // 获取是否需要动画的状态 public Boolean getNeedAnim() { return needAnim; } + // 设置是否需要动画 public void setNeedAnim(Boolean needAnim) { this.needAnim = needAnim; } + // 最新阅读视图的ViewHolder class LastestViewHolder extends RecyclerView.ViewHolder { ImageView ivCover; + // 书籍封面 + FrameLayout flLastestTip; + // 最新阅读提示 + AutofitTextView tvName; + // 书籍名称 + AutofitTextView tvDurprogress; + // 阅读进度 + LinearLayout llDurcursor; + // 进度条的滑动控件 + MHorProgressBar mpbDurprogress; + // 水平进度条 + TextView tvWatch; + // 操作按钮 public LastestViewHolder(View itemView) { super(itemView); - ivCover = (ImageView) itemView.findViewById(R.id.iv_cover); - flLastestTip = (FrameLayout) itemView.findViewById(R.id.fl_lastest_tip); - tvName = (AutofitTextView) itemView.findViewById(R.id.tv_name); - tvDurprogress = (AutofitTextView) itemView.findViewById(R.id.tv_durprogress); - llDurcursor = (LinearLayout) itemView.findViewById(R.id.ll_durcursor); - mpbDurprogress = (MHorProgressBar) itemView.findViewById(R.id.mpb_durprogress); - tvWatch = (TextView) itemView.findViewById(R.id.tv_watch); + ivCover = itemView.findViewById(R.id.iv_cover); + // 获取封面控件 + + flLastestTip = itemView.findViewById(R.id.fl_lastest_tip); + // 获取最新提示控件 + + tvName = itemView.findViewById(R.id.tv_name); + // 获取书名控件 + + tvDurprogress = itemView.findViewById(R.id.tv_durprogress); + // 获取进度文本控件 + + llDurcursor = itemView.findViewById(R.id.ll_durcursor); + // 获取进度条控件 + + mpbDurprogress = itemView.findViewById(R.id.mpb_durprogress); + // 获取进度条控件 + + tvWatch = itemView.findViewById(R.id.tv_watch); + // 获取操作按钮控件 } } + // 其他书籍视图的ViewHolder class OtherViewHolder extends RecyclerView.ViewHolder { FrameLayout flContent_1; + // 第一个书籍的容器 + ImageView ivCover_1; + // 第一个书籍封面 + AutofitTextView tvName_1; + // 第一个书籍名称 + ImageButton ibContent_1; + // 第一个书籍按钮 + FrameLayout flContent_2; + // 第二个书籍的容器 + ImageView ivCover_2; + // 第二个书籍封面 + AutofitTextView tvName_2; + // 第二个书籍名称 + ImageButton ibContent_2; + // 第二个书籍按钮 + FrameLayout flContent_3; + // 第三个书籍的容器 + ImageView ivCover_3; + // 第三个书籍封面 + AutofitTextView tvName_3; + // 第三个书籍名称 + ImageButton ibContent_3; + // 第三个书籍按钮 + public OtherViewHolder(View itemView) { super(itemView); - flContent_1 = (FrameLayout) itemView.findViewById(R.id.fl_content_1); - ivCover_1 = (ImageView) itemView.findViewById(R.id.iv_cover_1); - tvName_1 = (AutofitTextView) itemView.findViewById(R.id.tv_name_1); - ibContent_1 = (ImageButton) itemView.findViewById(R.id.ib_content_1); - - flContent_2 = (FrameLayout) itemView.findViewById(R.id.fl_content_2); - ivCover_2 = (ImageView) itemView.findViewById(R.id.iv_cover_2); - tvName_2 = (AutofitTextView) itemView.findViewById(R.id.tv_name_2); - ibContent_2 = (ImageButton) itemView.findViewById(R.id.ib_content_2); - - flContent_3 = (FrameLayout) itemView.findViewById(R.id.fl_content_3); - ivCover_3 = (ImageView) itemView.findViewById(R.id.iv_cover_3); - tvName_3 = (AutofitTextView) itemView.findViewById(R.id.tv_name_3); - ibContent_3 = (ImageButton) itemView.findViewById(R.id.ib_content_3); + flContent_1 = itemView.findViewById(R.id.fl_content_1); + // 获取第一个书籍的容器 + + ivCover_1 = itemView.findViewById(R.id.iv_cover_1); + // 获取第一个书籍的封面 + + tvName_1 = itemView.findViewById(R.id.tv_name_1); + // 获取第一个书籍的名称 + + ibContent_1 = itemView.findViewById(R.id.ib_content_1); + // 获取第一个书籍的按钮 + + flContent_2 = itemView.findViewById(R.id.fl_content_2); + // 获取第二个书籍的容器 + + ivCover_2 = itemView.findViewById(R.id.iv_cover_2); + // 获取第二个书籍的封面 + + tvName_2 = itemView.findViewById(R.id.tv_name_2); + // 获取第二个书籍的名称 + + ibContent_2 = itemView.findViewById(R.id.ib_content_2); + // 获取第二个书籍的按钮 + + flContent_3 = itemView.findViewById(R.id.fl_content_3); + // 获取第三个书籍的容器 + + ivCover_3 = itemView.findViewById(R.id.iv_cover_3); + // 获取第三个书籍的封面 + + tvName_3 = itemView.findViewById(R.id.tv_name_3); + // 获取第三个书籍的名称 + + ibContent_3 = itemView.findViewById(R.id.ib_content_3); + // 获取第三个书籍的按钮 } } + // 动画开始监听器 abstract class AnimatontStartListener implements Animation.AnimationListener { - @Override public void onAnimationStart(Animation animation) { - onAnimStart(animation); + onAnimStart(animation); // 调用子类实现的动画开始方法 } @Override - public void onAnimationEnd(Animation animation) { - - } + public void onAnimationEnd(Animation animation) { } @Override - public void onAnimationRepeat(Animation animation) { + public void onAnimationRepeat(Animation animation) { } - } - - abstract void onAnimStart(Animation animation); + abstract void onAnimStart(Animation animation); // 抽象方法,由子类实现动画开始逻辑 } + // 替换所有书籍数据并刷新列表 public synchronized void replaceAll(List newDatas) { - books.clear(); + books.clear(); // 清空现有数据 if (null != newDatas && newDatas.size() > 0) { - books.addAll(newDatas); + books.addAll(newDatas); // 添加新数据 } - order(); - - notifyDataSetChanged(); + order(); // 对书籍按照最后阅读时间排序 + notifyDataSetChanged(); // 通知适配器数据已改变 } + // 排序书籍,根据最终阅读时间降序排列 private void order() { if (books != null && books.size() > 0) { for (int i = 0; i < books.size(); i++) { @@ -409,6 +440,7 @@ public class BookShelfAdapter extends RefreshRecyclerViewAdapter { temp = j; } } + // 交换排序 BookShelfBean tempBookShelfBean = books.get(i); books.set(i, books.get(temp)); books.set(temp, tempBookShelfBean); @@ -416,7 +448,8 @@ public class BookShelfAdapter extends RefreshRecyclerViewAdapter { } } + // 获取书架数据 public List getBooks() { return books; } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/monke/monkeybook/view/adapter/ChapterListAdapter.java b/app/src/main/java/com/monke/monkeybook/view/adapter/ChapterListAdapter.java index 7162074..fab538b 100644 --- a/app/src/main/java/com/monke/monkeybook/view/adapter/ChapterListAdapter.java +++ b/app/src/main/java/com/monke/monkeybook/view/adapter/ChapterListAdapter.java @@ -1,92 +1,174 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +// Copyright (c) 2017. 章钦豪. All rights reserved. package com.monke.monkeybook.view.adapter; +// 声明该类所在的包,适配器的包名 import android.graphics.Color; +// 导入Color类,用于设置颜色 + import android.support.annotation.NonNull; +// 导入NonNull注解,表示该参数不允许为null + import android.support.v7.widget.RecyclerView; +// 导入RecyclerView类,用于实现列表显示 + import android.view.LayoutInflater; +// 导入LayoutInflater类,用于动态加载布局 + import android.view.View; +// 导入View类,视图的基类 + import android.view.ViewGroup; +// 导入ViewGroup类,视图容器的基类 + import android.widget.FrameLayout; +// 导入FrameLayout类,布局控件,作为容器 + import android.widget.TextView; +// 导入TextView类,显示文本控件 + import com.monke.monkeybook.R; +// 导入资源文件,包含布局和样式等资源 + import com.monke.monkeybook.bean.BookShelfBean; +// 导入书架数据模型类 + import com.monke.monkeybook.widget.ChapterListView; +// 导入ChapterListView,显示章节列表控件 +// 定义ChapterListAdapter类,继承RecyclerView.Adapter,用于章节列表的适配 public class ChapterListAdapter extends RecyclerView.Adapter { private BookShelfBean bookShelfBean; + // 书架数据对象 private ChapterListView.OnItemClickListener itemClickListener; + // 章节点击监听器接口 private int index = 0; + // 当前选中的章节索引 private Boolean isAsc = true; + // 是否升序显示章节 + // 构造函数,初始化书架数据和点击监听器 public ChapterListAdapter(BookShelfBean bookShelfBean, @NonNull ChapterListView.OnItemClickListener itemClickListener) { this.bookShelfBean = bookShelfBean; + // 设置书架数据 + this.itemClickListener = itemClickListener; + // 设置章节点击监听器 } + // 创建ViewHolder,用于加载每个章节的布局 @Override public Viewholder onCreateViewHolder(ViewGroup parent, int viewType) { + // 加载布局并返回ViewHolder return new Viewholder(LayoutInflater.from(parent.getContext()).inflate(R.layout.view_adapter_chapterlist, parent, false)); } + // 绑定数据到ViewHolder @Override public void onBindViewHolder(Viewholder holder, final int posiTion) { + // 如果是最后一项,隐藏分隔线;否则显示分隔线 if (posiTion == getItemCount() - 1) { holder.vLine.setVisibility(View.INVISIBLE); - } else + // 隐藏分隔线 + + } else { holder.vLine.setVisibility(View.VISIBLE); + // 显示分隔线 + } + // 计算实际章节位置,支持升序和降序显示 final int position; if (isAsc) { position = posiTion; + // 如果是升序,直接使用当前位置 + } else { position = getItemCount() - 1 - posiTion; + // 如果是降序,倒序显示 } + + // 设置章节名称 holder.tvName.setText(bookShelfBean.getBookInfoBean().getChapterlist().get(position).getDurChapterName()); + + // 设置点击事件,当用户点击章节时 holder.flContent.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { setIndex(position); + // 设置选中的章节索引 + itemClickListener.itemClick(position); + // 调用点击回调 } }); + + // 判断当前章节是否为选中的章节 if (position == index) { holder.flContent.setBackgroundColor(Color.parseColor("#cfcfcf")); + // 设置选中章节的背景色为灰色 + holder.flContent.setClickable(false); + // 禁用选中章节的点击 } else { holder.flContent.setBackgroundResource(R.drawable.bg_ib_pre2); + // 恢复未选中的章节背景 + holder.flContent.setClickable(true); + // 允许未选中的章节点击 } } + // 获取章节总数,如果bookShelfBean为null,则返回0 @Override public int getItemCount() { if (bookShelfBean == null) return 0; + // 如果书架数据为空,返回0 else return bookShelfBean.getBookInfoBean().getChapterlist().size(); + // 返回章节列表的大小 } + // 定义ViewHolder类,绑定章节布局控件 public class Viewholder extends RecyclerView.ViewHolder { private FrameLayout flContent; + // 章节项的容器 + private TextView tvName; + // 章节名称的TextView + private View vLine; + // 分隔线视图 + // 构造函数,初始化控件 public Viewholder(View itemView) { super(itemView); flContent = (FrameLayout) itemView.findViewById(R.id.fl_content); + // 获取章节内容容器 + tvName = (TextView) itemView.findViewById(R.id.tv_name); + // 获取章节名称TextView + vLine = itemView.findViewById(R.id.v_line); + // 获取分隔线视图 } + } + // 获取当前选中的章节索引 public int getIndex() { return index; } + // 设置当前选中的章节索引,并刷新UI + public void setIndex(int index) { notifyItemChanged(this.index); + // 通知RecyclerView更新之前选中的章节 + this.index = index; + // 更新选中的章节索引 + notifyItemChanged(this.index); + // 通知RecyclerView更新新选中的章节 } } diff --git a/app/src/main/java/com/monke/monkeybook/view/adapter/ChoiceBookAdapter.java b/app/src/main/java/com/monke/monkeybook/view/adapter/ChoiceBookAdapter.java index 124403a..ca92c53 100644 --- a/app/src/main/java/com/monke/monkeybook/view/adapter/ChoiceBookAdapter.java +++ b/app/src/main/java/com/monke/monkeybook/view/adapter/ChoiceBookAdapter.java @@ -1,178 +1,344 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +// Copyright (c) 2017. 章钦豪. All rights reserved. package com.monke.monkeybook.view.adapter; +// 声明该类所在的包,适配器的包名 import android.support.v7.widget.RecyclerView; +// 导入RecyclerView类,用于实现列表显示 + import android.view.LayoutInflater; +// 导入LayoutInflater类,用于动态加载布局 + import android.view.View; +// 导入View类,视图的基类 + import android.view.ViewGroup; +// 导入ViewGroup类,视图容器的基类 + import android.widget.FrameLayout; +// 导入FrameLayout类,布局控件,作为容器 + import android.widget.ImageView; +// 导入ImageView类,图片显示控件 + import android.widget.TextView; +// 导入TextView类,显示文本控件 + import com.bumptech.glide.Glide; +// 导入Glide库,用于加载图片 + import com.bumptech.glide.load.engine.DiskCacheStrategy; +// 导入Glide的缓存策略类 + import com.monke.monkeybook.R; +// 导入资源文件,包含布局和样式等资源 + import com.monke.monkeybook.bean.SearchBookBean; +// 导入书籍数据模型类 + import com.monke.monkeybook.widget.refreshview.RefreshRecyclerViewAdapter; +// 导入自定义的刷新RecyclerView适配器 + import java.text.DecimalFormat; +// 导入DecimalFormat类,用于格式化数字 + import java.util.ArrayList; +// 导入ArrayList类,用于存储书籍列表 + import java.util.List; +// 导入List接口,表示书籍列表 +// ChoiceBookAdapter继承自RefreshRecyclerViewAdapter,负责展示搜索书籍的适配器 public class ChoiceBookAdapter extends RefreshRecyclerViewAdapter { private List searchBooks; + // 存储搜索结果的书籍列表 + // 定义点击事件的接口 public interface OnItemClickListener { void clickAddShelf(View clickView, int position, SearchBookBean searchBookBean); + // 添加到书架 void clickItem(View animView, int position, SearchBookBean searchBookBean); + // 点击书籍项 } private OnItemClickListener itemClickListener; + // 点击事件监听器 + // 构造函数,初始化适配器,设置书籍列表为空 public ChoiceBookAdapter() { super(true); + // 调用父类构造函数,启用刷新功能 + searchBooks = new ArrayList<>(); + // 初始化书籍列表为空 } + // 创建ViewHolder对象,绑定布局 @Override public RecyclerView.ViewHolder onCreateViewholder(ViewGroup parent, int viewType) { + // 加载单个书籍项的布局,并返回ViewHolder return new Viewholder(LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_searchbook_item, parent, false)); } + // 绑定数据到ViewHolder @Override public void onBindViewholder(final RecyclerView.ViewHolder holder, final int position) { final int realposition = position; + // 保存真实的position值 + + // 使用Glide加载书籍封面图片 Glide.with(((Viewholder) holder).ivCover.getContext()) .load(searchBooks.get(realposition).getCoverUrl()) + // 加载书籍封面URL + .diskCacheStrategy(DiskCacheStrategy.RESULT) + // 设置缓存策略 + .fitCenter() + // 图片缩放适应控件 + .dontAnimate() + // 禁用动画 + .placeholder(R.drawable.img_cover_default) + // 设置加载中的默认图片 + .into(((Viewholder) holder).ivCover); + // 设置图片到ImageView + + // 设置书籍名称和作者 ((Viewholder) holder).tvName.setText(searchBooks.get(realposition).getName()); ((Viewholder) holder).tvAuthor.setText(searchBooks.get(realposition).getAuthor()); + + // 处理书籍的状态(例如是否连载中) String state = searchBooks.get(position).getState(); if (state == null || state.length() == 0) { ((Viewholder) holder).tvState.setVisibility(View.GONE); + // 如果没有状态,隐藏 + } else { ((Viewholder) holder).tvState.setVisibility(View.VISIBLE); + // 否则显示 + ((Viewholder) holder).tvState.setText(state); + // 设置状态文本 } + + // 处理书籍的字数,若字数大于10000则显示为万字 long words = searchBooks.get(realposition).getWords(); if (words <= 0) { ((Viewholder) holder).tvWords.setVisibility(View.GONE); + // 如果字数为0或负数,隐藏字数 + } else { String wordsS = Long.toString(words) + "字"; + // 默认显示字数 + if (words > 10000) { DecimalFormat df = new DecimalFormat("#.#"); + // 格式化字数,显示万字 + wordsS = df.format(words * 1.0f / 10000f) + "万字"; + // 格式化为万字 } ((Viewholder) holder).tvWords.setVisibility(View.VISIBLE); + // 显示字数 ((Viewholder) holder).tvWords.setText(wordsS); + // 设置字数文本 } + + // 处理书籍类型 String kind = searchBooks.get(realposition).getKind(); if (kind == null || kind.length() <= 0) { ((Viewholder) holder).tvKind.setVisibility(View.GONE); + // 如果没有类型,隐藏 + } else { ((Viewholder) holder).tvKind.setVisibility(View.VISIBLE); + // 否则显示 + ((Viewholder) holder).tvKind.setText(kind); + // 设置类型文本 } + + // 处理书籍的最新章节或描述 if (searchBooks.get(realposition).getLastChapter() != null && searchBooks.get(realposition).getLastChapter().length() > 0) ((Viewholder) holder).tvLastest.setText(searchBooks.get(realposition).getLastChapter()); else if (searchBooks.get(realposition).getDesc() != null && searchBooks.get(realposition).getDesc().length() > 0) { ((Viewholder) holder).tvLastest.setText(searchBooks.get(realposition).getDesc()); } else ((Viewholder) holder).tvLastest.setText(""); + // 如果没有最新章节或描述,显示空文本 + + // 处理书籍的来源 if (searchBooks.get(realposition).getOrigin() != null && searchBooks.get(realposition).getOrigin().length() > 0) { ((Viewholder) holder).tvOrigin.setVisibility(View.VISIBLE); + // 如果有来源,显示 + ((Viewholder) holder).tvOrigin.setText("来源:" + searchBooks.get(realposition).getOrigin()); + // 显示来源 + } else { ((Viewholder) holder).tvOrigin.setVisibility(View.GONE); + // 如果没有来源,隐藏 } + + // 处理书籍是否已添加到书架 if (searchBooks.get(realposition).getAdd()) { ((Viewholder) holder).tvAddShelf.setText("已添加"); + // 已添加,显示"已添加" + ((Viewholder) holder).tvAddShelf.setEnabled(false); + // 禁用添加按钮 + } else { ((Viewholder) holder).tvAddShelf.setText("+添加"); + // 未添加,显示"+添加" + ((Viewholder) holder).tvAddShelf.setEnabled(true); + // 启用添加按钮 } + // 设置书籍项点击事件 ((Viewholder) holder).flContent.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (itemClickListener != null) itemClickListener.clickItem(((Viewholder) holder).ivCover, realposition, searchBooks.get(realposition)); + // 调用点击书籍项的回调方法 } }); + + // 设置添加书架按钮点击事件 ((Viewholder) holder).tvAddShelf.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (itemClickListener != null) itemClickListener.clickAddShelf(((Viewholder) holder).tvAddShelf, realposition, searchBooks.get(realposition)); + // 调用添加到书架的回调方法 } }); } + // 返回每个item的视图类型,当前不使用不同类型的视图 @Override public int getItemViewtype(int position) { return 0; } + // 返回列表中的书籍项数 @Override public int getItemcount() { - return searchBooks.size(); + return searchBooks.size(); // 返回书籍列表的大小 } + // ViewHolder类,管理书籍项的视图控件 class Viewholder extends RecyclerView.ViewHolder { FrameLayout flContent; + // 书籍项的根布局 + ImageView ivCover; + // 书籍封面 + TextView tvName; + // 书籍名称 + TextView tvAuthor; + // 书籍作者 + TextView tvState; + // 书籍状态 + TextView tvWords; + // 书籍字数 + TextView tvKind; + // 书籍类型 + TextView tvLastest; + // 书籍最新章节 + TextView tvAddShelf; + // 添加书架按钮 + TextView tvOrigin; + // 书籍来源 + // 构造函数,初始化控件 public Viewholder(View itemView) { super(itemView); flContent = (FrameLayout) itemView.findViewById(R.id.fl_content); + // 获取根布局 + ivCover = (ImageView) itemView.findViewById(R.id.iv_cover); + // 获取封面ImageView + tvName = (TextView) itemView.findViewById(R.id.tv_name); + // 获取书名TextView + tvAuthor = (TextView) itemView.findViewById(R.id.tv_author); + // 获取作者TextView + tvState = (TextView) itemView.findViewById(R.id.tv_state); + // 获取状态TextView + tvWords = (TextView) itemView.findViewById(R.id.tv_words); + // 获取字数TextView + tvLastest = (TextView) itemView.findViewById(R.id.tv_lastest); + // 获取最新章节TextView + tvAddShelf = (TextView) itemView.findViewById(R.id.tv_addshelf); + // 获取添加书架按钮 + tvKind = (TextView) itemView.findViewById(R.id.tv_kind); + // 获取类型TextView + tvOrigin = (TextView) itemView.findViewById(R.id.tv_origin); + // 获取来源TextView } } + // 设置点击事件监听器 public void setItemClickListener(OnItemClickListener itemClickListener) { this.itemClickListener = itemClickListener; } + // 添加新数据到书籍列表 public void addAll(List newData) { if (newData != null && newData.size() > 0) { int position = getItemcount(); - if (newData != null && newData.size() > 0) { - searchBooks.addAll(newData); - } + // 获取当前列表大小 + + searchBooks.addAll(newData); + // 将新数据添加到列表中 + notifyItemInserted(position); + // 通知RecyclerView插入新项 + notifyItemRangeChanged(position, newData.size()); + // 通知RecyclerView更新范围内的项 + } } + // 替换整个列表的数据 public void replaceAll(List newData) { searchBooks.clear(); + // 清空现有数据 + if (newData != null && newData.size() > 0) { searchBooks.addAll(newData); + // 将新数据添加到列表中 + } notifyDataSetChanged(); + // 通知RecyclerView更新所有项 + } + // 获取当前的书籍列表 public List getSearchBooks() { return searchBooks; } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/monke/monkeybook/view/adapter/ImportBookAdapter.java b/app/src/main/java/com/monke/monkeybook/view/adapter/ImportBookAdapter.java index e84a4d7..aeb6eac 100644 --- a/app/src/main/java/com/monke/monkeybook/view/adapter/ImportBookAdapter.java +++ b/app/src/main/java/com/monke/monkeybook/view/adapter/ImportBookAdapter.java @@ -1,126 +1,243 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +// Copyright (c) 2017. 章钦豪. All rights reserved. package com.monke.monkeybook.view.adapter; +// 声明该类所在的包 import android.os.Environment; +// 导入Environment类,访问设备的存储环境 + import android.support.annotation.NonNull; +// 导入NonNull注解,用于标记非空参数 + import android.support.v7.widget.RecyclerView; +// 导入RecyclerView类,用于实现列表显示 + import android.view.LayoutInflater; +// 导入LayoutInflater类,用于动态加载布局 + import android.view.View; +// 导入View类,视图的基类 + import android.view.ViewGroup; +// 导入ViewGroup类,视图容器的基类 + import android.widget.LinearLayout; +// 导入LinearLayout类,布局控件,线性布局 + import android.widget.TextView; +// 导入TextView类,显示文本控件 + import com.monke.monkeybook.R; +// 导入资源文件,包含布局和样式等资源 + import com.monke.monkeybook.widget.checkbox.SmoothCheckBox; +// 导入自定义的平滑复选框控件 + import java.io.File; +// 导入File类,表示文件 + import java.text.DecimalFormat; +// 导入DecimalFormat类,用于格式化数字 + import java.util.ArrayList; +// 导入ArrayList类,用于存储列表数据 + import java.util.List; +// 导入List接口,表示列表数据 -public class ImportBookAdapter extends RecyclerView.Adapter{ +// ImportBookAdapter继承自RecyclerView.Adapter,用于显示导入书籍的适配器 +public class ImportBookAdapter extends RecyclerView.Adapter { private List datas; + // 存储所有的文件数据(导入的书籍文件) + private List selectDatas; + // 存储已选中的文件数据 - public interface OnCheckBookListener{ + // 定义一个接口,监听选中文件的变化 + public interface OnCheckBookListener { void checkBook(int count); + // 当选中的书籍数量变化时调用 } private OnCheckBookListener checkBookListener; - public ImportBookAdapter(@NonNull OnCheckBookListener checkBookListener){ + // 接口实例,用于回调选中数量变化 + + // 构造函数,初始化适配器并传入OnCheckBookListener + public ImportBookAdapter(@NonNull OnCheckBookListener checkBookListener) { datas = new ArrayList<>(); + // 初始化文件数据列表 + selectDatas = new ArrayList<>(); + // 初始化选中文件列表 + this.checkBookListener = checkBookListener; + // 设置回调监听器 } + // 创建ViewHolder实例并返回 @Override public Viewholder onCreateViewHolder(ViewGroup parent, int viewType) { - return new Viewholder(LayoutInflater.from(parent.getContext()).inflate(R.layout.view_adapter_importbook,parent,false)); + // 加载单个书籍项的布局,并返回ViewHolder + return new Viewholder(LayoutInflater.from(parent.getContext()).inflate(R.layout.view_adapter_importbook, parent, false)); } + // 绑定数据到ViewHolder @Override public void onBindViewHolder(final Viewholder holder, final int position) { + // 设置书籍名称、大小、存储路径等文本信息 + holder.tvNmae.setText(datas.get(position).getName()); + // 显示文件名 + holder.tvSize.setText(convertByte(datas.get(position).length())); - holder.tvLoc.setText(datas.get(position).getAbsolutePath().replace(Environment.getExternalStorageDirectory().getAbsolutePath(),"存储空间")); + // 显示文件大小,转换为易读格式 + + holder.tvLoc.setText(datas.get(position).getAbsolutePath().replace(Environment.getExternalStorageDirectory().getAbsolutePath(), "存储空间")); + // 显示文件路径 + // 设置复选框状态变更监听 holder.scbSelect.setOnCheckedChangeListener(new SmoothCheckBox.OnCheckedChangeListener() { @Override public void onCheckedChanged(SmoothCheckBox checkBox, boolean isChecked) { - if(isChecked){ + // 根据复选框状态更新选中文件列表 + + if (isChecked) { selectDatas.add(datas.get(position)); - }else{ + // 如果选中,则添加到选中列表 + + } else { selectDatas.remove(datas.get(position)); + // 如果取消选中,则从选中列表中移除 } + // 调用监听器,通知选中的书籍数量 checkBookListener.checkBook(selectDatas.size()); } }); - if(canCheck){ + + // 控制是否显示复选框和设置点击事件 + if (canCheck) { + // 如果允许选择 holder.scbSelect.setVisibility(View.VISIBLE); + // 显示复选框 + // 设置点击项的点击事件,切换复选框状态 holder.llContent.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - holder.scbSelect.setChecked(!holder.scbSelect.isChecked(),true); + holder.scbSelect.setChecked(!holder.scbSelect.isChecked(), true); + // 切换复选框状态 } }); - }else{ + } else { // 如果不允许选择 holder.scbSelect.setVisibility(View.INVISIBLE); + // 隐藏复选框 + holder.llContent.setOnClickListener(null); + // 取消点击事件 } } - public void addData(File newItem){ + // 添加新数据到文件列表 + public void addData(File newItem) { int position = datas.size(); + // 获取当前列表末尾位置 + datas.add(newItem); + // 将新项添加到列表中 + notifyItemInserted(position); + // 通知RecyclerView插入新项 + notifyItemRangeChanged(position, 1); + // 更新新增项 } private Boolean canCheck = false; - public void setCanCheck(Boolean canCheck){ + // 控制是否可以选择文件 + + // 设置是否允许选择文件 + public void setCanCheck(Boolean canCheck) { this.canCheck = canCheck; + // 更新选择状态 notifyDataSetChanged(); + + // 通知RecyclerView更新所有项 } + // 返回列表中项的总数 @Override public int getItemCount() { - return datas.size(); + return datas.size(); // 返回文件数据的大小 } + // ViewHolder类,管理书籍项的视图控件 class Viewholder extends RecyclerView.ViewHolder { LinearLayout llContent; + // 根布局,整个书籍项的容器 TextView tvNmae; + // 显示书籍名称的TextView + TextView tvSize; + // 显示文件大小的TextView + TextView tvLoc; + // 显示文件路径的TextView + SmoothCheckBox scbSelect; + // 显示复选框 + + // 构造函数,初始化控件 public Viewholder(View itemView) { super(itemView); llContent = (LinearLayout) itemView.findViewById(R.id.ll_content); + // 获取根布局 + tvNmae = (TextView) itemView.findViewById(R.id.tv_name); + // 获取书籍名称TextView + tvSize = (TextView) itemView.findViewById(R.id.tv_size); + // 获取文件大小TextView + scbSelect = (SmoothCheckBox) itemView.findViewById(R.id.scb_select); + // 获取复选框 + tvLoc = (TextView) itemView.findViewById(R.id.tv_loc); + // 获取文件路径TextView } } + // 将字节数转换为可读的文件大小单位(B, KB, MB, GB) public static String convertByte(long size) { DecimalFormat df = new DecimalFormat("###.#"); + // 设置数字格式 float f; if (size < 1024) { f = size / 1.0f; return (df.format(new Float(f).doubleValue()) + "B"); + // 小于1KB,返回字节数 + } else if (size < 1024 * 1024) { f = (float) ((float) size / (float) 1024); return (df.format(new Float(f).doubleValue()) + "KB"); + // 小于1MB,返回KB + } else if (size < 1024 * 1024 * 1024) { f = (float) ((float) size / (float) (1024 * 1024)); return (df.format(new Float(f).doubleValue()) + "MB"); + // 小于1GB,返回MB + } else { f = (float) ((float) size / (float) (1024 * 1024 * 1024)); return (df.format(new Float(f).doubleValue()) + "GB"); + // 大于等于1GB,返回GB + + } } + // 获取选中的文件列表 public List getSelectDatas() { return selectDatas; + // 返回选中的文件列表 + } } diff --git a/app/src/main/java/com/monke/monkeybook/view/adapter/SearchBookAdapter.java b/app/src/main/java/com/monke/monkeybook/view/adapter/SearchBookAdapter.java index 568dc3e..6a78018 100644 --- a/app/src/main/java/com/monke/monkeybook/view/adapter/SearchBookAdapter.java +++ b/app/src/main/java/com/monke/monkeybook/view/adapter/SearchBookAdapter.java @@ -1,100 +1,207 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +// Copyright (c) 2017. 章钦豪. All rights reserved. package com.monke.monkeybook.view.adapter; import android.support.v7.widget.RecyclerView; +// 导入RecyclerView类,用于显示列表项 + import android.view.LayoutInflater; +// 导入LayoutInflater,用于加载布局 + import android.view.View; +// 导入View类,视图的基类 + import android.view.ViewGroup; +// 导入ViewGroup类,视图容器的基类 + import android.widget.FrameLayout; +// 导入FrameLayout,容器布局 + import android.widget.ImageView; +// 导入ImageView,用于显示图像 + import android.widget.TextView; +// 导入TextView,用于显示文本 + import com.bumptech.glide.Glide; +// 导入Glide库,用于图片加载和缓存 + import com.bumptech.glide.load.engine.DiskCacheStrategy; +// 导入Glide的缓存策略 + import com.monke.monkeybook.R; +// 导入资源文件,包含布局、字符串、图片等资源 + import com.monke.monkeybook.bean.SearchBookBean; +// 导入自定义的书籍数据模型 + import com.monke.monkeybook.widget.refreshview.RefreshRecyclerViewAdapter; +// 导入自定义的RecyclerView适配器基类 + import java.text.DecimalFormat; +// 导入DecimalFormat,用于格式化数字 + import java.util.ArrayList; +// 导入ArrayList类,动态数组 + import java.util.List; +// 导入List接口,用于列表类型 +// SearchBookAdapter继承自RefreshRecyclerViewAdapter,适配器用于展示搜索到的书籍 public class SearchBookAdapter extends RefreshRecyclerViewAdapter { + private List searchBooks; + // 存储书籍的列表数据 + // 定义点击事件的回调接口 public interface OnItemClickListener { void clickAddShelf(View clickView, int position, SearchBookBean searchBookBean); + // 添加书籍到书架回调 void clickItem(View animView, int position, SearchBookBean searchBookBean); + // 点击书籍项回调 + } private OnItemClickListener itemClickListener; + // 接口实例,通知外部点击事件 + // 构造函数,初始化适配器 public SearchBookAdapter() { super(true); + // 初始化基类 + searchBooks = new ArrayList<>(); + // 初始化书籍列表 + } + // 创建ViewHolder,加载每个书籍项的布局 @Override public RecyclerView.ViewHolder onCreateViewholder(ViewGroup parent, int viewType) { + + // 加载单个书籍项的布局并返回ViewHolder return new Viewholder(LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_searchbook_item, parent, false)); } + // 绑定数据到ViewHolder @Override public void onBindViewholder(final RecyclerView.ViewHolder holder, final int position) { + // 使用Glide加载书籍封面图 Glide.with(((Viewholder) holder).ivCover.getContext()) .load(searchBooks.get(position).getCoverUrl()) + // 设置封面图片的URL + .diskCacheStrategy(DiskCacheStrategy.RESULT) + // 设置磁盘缓存策略,缓存最终结果 + .centerCrop() + // 以中心裁剪方式展示图片 + .dontAnimate() + // 禁用图片加载动画 + .placeholder(R.drawable.img_cover_default) + // 设置加载过程中的占位符 + .into(((Viewholder) holder).ivCover); + // 加载图片到ImageView + + + // 设置书籍名称、作者、状态、字数、类型、简介等信息 ((Viewholder) holder).tvName.setText(searchBooks.get(position).getName()); + // 设置书籍名称 + ((Viewholder) holder).tvAuthor.setText(searchBooks.get(position).getAuthor()); + // 设置作者名称 + + // 设置书籍状态(如连载状态) String state = searchBooks.get(position).getState(); if (state == null || state.length() == 0) { ((Viewholder) holder).tvState.setVisibility(View.GONE); + // 如果没有状态信息,隐藏 + } else { ((Viewholder) holder).tvState.setVisibility(View.VISIBLE); + // 否则显示状态 + ((Viewholder) holder).tvState.setText(state); } + + // 设置字数信息,超过1万字时显示“万字”单位 long words = searchBooks.get(position).getWords(); if (words <= 0) { ((Viewholder) holder).tvWords.setVisibility(View.GONE); + // 如果字数小于等于0,隐藏字数信息 + } else { String wordsS = Long.toString(words) + "字"; + // 默认显示字数 + if (words > 10000) { DecimalFormat df = new DecimalFormat("#.#"); + // 格式化为“万字” + wordsS = df.format(words * 1.0f / 10000f) + "万字"; } ((Viewholder) holder).tvWords.setVisibility(View.VISIBLE); + // 显示字数信息 + ((Viewholder) holder).tvWords.setText(wordsS); } + + // 设置书籍类型(如小说、历史等) String kind = searchBooks.get(position).getKind(); if (kind == null || kind.length() <= 0) { ((Viewholder) holder).tvKind.setVisibility(View.GONE); + // 如果没有类型信息,隐藏 + } else { ((Viewholder) holder).tvKind.setVisibility(View.VISIBLE); + // 否则显示类型 + ((Viewholder) holder).tvKind.setText(kind); } + + // 设置书籍的最新章节或简介 if (searchBooks.get(position).getLastChapter() != null && searchBooks.get(position).getLastChapter().length() > 0) ((Viewholder) holder).tvLastest.setText(searchBooks.get(position).getLastChapter()); + // 显示最新章节 + else if (searchBooks.get(position).getDesc() != null && searchBooks.get(position).getDesc().length() > 0) { ((Viewholder) holder).tvLastest.setText(searchBooks.get(position).getDesc()); + // 显示简介 + } else ((Viewholder) holder).tvLastest.setText(""); + // 如果没有最新章节和简介,设置为空 + + + // 设置书籍的来源信息 if (searchBooks.get(position).getOrigin() != null && searchBooks.get(position).getOrigin().length() > 0) { ((Viewholder) holder).tvOrigin.setVisibility(View.VISIBLE); + // 如果有来源,显示 + ((Viewholder) holder).tvOrigin.setText("来源:" + searchBooks.get(position).getOrigin()); } else { ((Viewholder) holder).tvOrigin.setVisibility(View.GONE); + // 如果没有来源,隐藏 + } + + // 设置书籍是否已添加到书架的状态 if (searchBooks.get(position).getAdd()) { ((Viewholder) holder).tvAddShelf.setText("已添加"); ((Viewholder) holder).tvAddShelf.setEnabled(false); + // 如果已添加,禁用添加按钮 + } else { ((Viewholder) holder).tvAddShelf.setText("+添加"); ((Viewholder) holder).tvAddShelf.setEnabled(true); + // 如果未添加,启用添加按钮 + } + // 设置点击事件,点击书籍项时触发 ((Viewholder) holder).flContent.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -102,6 +209,8 @@ public class SearchBookAdapter extends RefreshRecyclerViewAdapter { itemClickListener.clickItem(((Viewholder) holder).ivCover, position, searchBooks.get(position)); } }); + + // 设置点击事件,点击添加书架按钮时触发 ((Viewholder) holder).tvAddShelf.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -111,68 +220,129 @@ public class SearchBookAdapter extends RefreshRecyclerViewAdapter { }); } + // 获取每个列表项的类型 @Override public int getItemViewtype(int position) { return 0; + // 默认返回0,表示只有一种类型 } + // 获取列表项数量 @Override public int getItemcount() { return searchBooks.size(); + // 返回书籍列表的大小 } + // ViewHolder类,管理书籍项的视图控件 class Viewholder extends RecyclerView.ViewHolder { FrameLayout flContent; + // 根布局,书籍项的容器 + ImageView ivCover; + // 显示书籍封面的ImageView + TextView tvName; + // 显示书籍名称的TextView + TextView tvAuthor; + // 显示作者的TextView + TextView tvState; + // 显示书籍状态的TextView(如连载状态) + TextView tvWords; + // 显示字数的TextView + TextView tvKind; + // 显示书籍类型的TextView + TextView tvLastest; + // 显示最新章节或简介的TextView + TextView tvAddShelf; + // 显示添加到书架按钮的TextView + TextView tvOrigin; + // 显示书籍来源的TextView + // 构造函数,初始化视图控件 public Viewholder(View itemView) { super(itemView); flContent = (FrameLayout) itemView.findViewById(R.id.fl_content); + // 获取根布局 + ivCover = (ImageView) itemView.findViewById(R.id.iv_cover); + // 获取书籍封面ImageView + tvName = (TextView) itemView.findViewById(R.id.tv_name); + // 获取书籍名称TextView + tvAuthor = (TextView) itemView.findViewById(R.id.tv_author); + // 获取作者TextView + tvState = (TextView) itemView.findViewById(R.id.tv_state); + // 获取书籍状态TextView + tvWords = (TextView) itemView.findViewById(R.id.tv_words); + // 获取字数TextView + tvLastest = (TextView) itemView.findViewById(R.id.tv_lastest); + // 获取最新章节TextView + tvAddShelf = (TextView) itemView.findViewById(R.id.tv_addshelf); + // 获取添加到书架按钮TextView + tvKind = (TextView) itemView.findViewById(R.id.tv_kind); + // 获取书籍类型TextView + tvOrigin = (TextView) itemView.findViewById(R.id.tv_origin); + // 获取来源TextView } } + // 设置点击事件监听器 public void setItemClickListener(OnItemClickListener itemClickListener) { this.itemClickListener = itemClickListener; + // 保存监听器 } + // 批量添加数据到列表 public void addAll(List newDatas) { - if(newDatas!=null && newDatas.size()>0){ + if (newDatas != null && newDatas.size() > 0) { int oldCount = getItemcount(); + // 获取当前列表项数 + searchBooks.addAll(newDatas); - notifyItemRangeInserted(oldCount,newDatas.size()); + // 将新数据添加到列表 + + notifyItemRangeInserted(oldCount, newDatas.size()); + // 通知RecyclerView插入新的数据项 } } + // 替换列表中的所有数据 public void replaceAll(List newData) { searchBooks.clear(); + // 清空现有数据 + if (newData != null && newData.size() > 0) { searchBooks.addAll(newData); + // 添加新数据 } notifyDataSetChanged(); + // 通知RecyclerView更新数据 } + // 获取当前的书籍列表数据 public List getSearchBooks() { return searchBooks; + // 返回书籍列表 } + // 设置新的书籍列表数据 public void setSearchBooks(List searchBooks) { this.searchBooks = searchBooks; + // 更新书籍列表 } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/monke/monkeybook/view/adapter/SearchHistoryAdapter.java b/app/src/main/java/com/monke/monkeybook/view/adapter/SearchHistoryAdapter.java index c2aebd0..85ad2ef 100644 --- a/app/src/main/java/com/monke/monkeybook/view/adapter/SearchHistoryAdapter.java +++ b/app/src/main/java/com/monke/monkeybook/view/adapter/SearchHistoryAdapter.java @@ -1,54 +1,104 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +// Copyright (c) 2017. 章钦豪. All rights reserved. + +// 定义适配器类所在的包 package com.monke.monkeybook.view.adapter; +// 导入所需的类和库 import android.view.LayoutInflater; +// 用于布局填充 + import android.view.View; +// 用于表示视图组件 + import android.widget.TextView; +// 用于显示文本的控件 + import com.monke.monkeybook.R; +// 引入项目的资源文件 + import com.monke.monkeybook.bean.SearchHistoryBean; +// 导入数据模型类,用于封装搜索历史 + import com.monke.monkeybook.widget.flowlayout.FlowLayout; +// 引入FlowLayout类,显示标签的布局 + import com.monke.monkeybook.widget.flowlayout.TagAdapter; +// 引入TagAdapter基类,扩展标签的适配器 + import java.util.ArrayList; +// 引入ArrayList类,创建一个可动态变化的列表 + + + +// 定义SearchHistoryAdapter类,继承TagAdapter + +// 用于显示搜索历史数据,并处理点击事件 public class SearchHistoryAdapter extends TagAdapter { + + // 构造函数:调用父类TagAdapter的构造方法,初始化空的数据集合 public SearchHistoryAdapter() { super(new ArrayList()); } - public interface OnItemClickListener{ + // 定义一个接口OnItemClickListener,用于处理点击事件 + public interface OnItemClickListener { + // 定义点击条目的回调方法,传递一个SearchHistoryBean对象 + void itemClick(SearchHistoryBean searchHistoryBean); } + + // 声明一个成员变量onItemClickListener,用于保存点击事件监听器 private SearchHistoryAdapter.OnItemClickListener onItemClickListener; + // 获取当前的点击事件监听器 public OnItemClickListener getListener() { return onItemClickListener; } + // 设置点击事件监听器 public void setOnItemClickListener(OnItemClickListener listener) { this.onItemClickListener = listener; } + // 重写getView方法,返回每个标签(SearchHistoryBean)对应的视图 @Override public View getView(FlowLayout parent, int position, final SearchHistoryBean searchHistoryBean) { + // 使用LayoutInflater填充自定义的item布局文件 + TextView tv = (TextView) LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_searchhistory_item, parent, false); + // 加载adapter_searchhistory_item布局 + + // 设置TextView的文本为SearchHistoryBean的内容 tv.setText(searchHistoryBean.getContent()); + + // 设置点击事件监听器 tv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - if(null != onItemClickListener){ + // 如果设置了点击事件监听器,调用它的itemClick方法 + if (null != onItemClickListener) { onItemClickListener.itemClick(searchHistoryBean); + + // 触发点击事件回调 } } }); + + // 返回填充好的TextView作为标签视图 return tv; } - public SearchHistoryBean getItemData(int position){ + // 获取指定位置的SearchHistoryBean对象 + public SearchHistoryBean getItemData(int position) { return mTagDatas.get(position); + // 从TagAdapter父类中获取数据集合并返回对应位置的数据 } - public int getDataSize(){ + // 获取数据集合的大小 + public int getDataSize() { return mTagDatas.size(); + // 返回标签数据的数量 } } diff --git a/build.gradle b/build.gradle index 43153d6..888750f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,23 @@ buildscript { repositories { - jcenter() - mavenCentral() - maven { - url 'https://maven.google.com/' - name 'Google' - } - google() +// jcenter() +// mavenCentral() +// maven { +// url 'https://maven.google.com/' +// name 'Google' +// } +// google() +// maven { url 'https://plugins.gradle.org/m2/' } +// maven { url 'https://maven.aliyun.com/nexus/content/repositories/google' } +// maven { url 'https://maven.aliyun.com/nexus/content/groups/public' } +// maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter'} + + maven { url 'https://maven.aliyun.com/repository/public' } + maven { url 'https://maven.aliyun.com/repository/central' } + maven { url 'https://maven.aliyun.com/repository/jcenter' } + maven { url 'https://maven.aliyun.com/repository/google' } + maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } } dependencies { classpath 'com.android.tools.build:gradle:3.2.1' @@ -17,11 +27,21 @@ buildscript { allprojects { repositories { - jcenter() - maven { - url 'https://maven.google.com/' - name 'Google' - } +// jcenter() +// maven { +// url 'https://maven.google.com/' +// name 'Google' +// } +// maven { url 'https://plugins.gradle.org/m2/' } +// maven { url 'https://maven.aliyun.com/nexus/content/repositories/google' } +// maven { url 'https://maven.aliyun.com/nexus/content/groups/public' } +// maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter'} + + maven { url 'https://maven.aliyun.com/repository/public' } + maven { url 'https://maven.aliyun.com/repository/central' } + maven { url 'https://maven.aliyun.com/repository/jcenter' } + maven { url 'https://maven.aliyun.com/repository/google' } + maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } } } -- 2.34.1 From 7cc17b20333d7d28be724715410803ce82574f89 Mon Sep 17 00:00:00 2001 From: taoxin <1379896934@qq.com> Date: Tue, 17 Dec 2024 16:05:52 +0800 Subject: [PATCH 8/8] add .taoxin --- .../view/impl/BookDetailActivity.java | 164 +++++++++++++++--- 1 file changed, 137 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/monke/monkeybook/view/impl/BookDetailActivity.java b/app/src/main/java/com/monke/monkeybook/view/impl/BookDetailActivity.java index 74bde0c..9bd21aa 100644 --- a/app/src/main/java/com/monke/monkeybook/view/impl/BookDetailActivity.java +++ b/app/src/main/java/com/monke/monkeybook/view/impl/BookDetailActivity.java @@ -1,136 +1,219 @@ -//Copyright (c) 2017. 章钦豪. All rights reserved. +// Copyright (c) 2017. 章钦豪. All rights reserved. package com.monke.monkeybook.view.impl; +// 声明包名,表示这是项目中的一个实现类 +// 导入相关类 import android.content.Intent; +// 用于启动其他Activity import android.os.Build; +// 用于获取Android设备的版本信息 import android.text.method.ScrollingMovementMethod; +// 用于使TextView支持滚动 import android.view.View; +// 用于视图的点击事件监听 import android.view.animation.Animation; +// 动画类 import android.view.animation.AnimationUtils; +// 用于加载动画资源 import android.widget.FrameLayout; +// 帧布局 import android.widget.ImageView; +// 图片视图控件 import android.widget.TextView; +// 文本视图控件 + import com.bumptech.glide.Glide; +// 图片加载库,用于加载图片 import com.bumptech.glide.load.engine.DiskCacheStrategy; +// 设置缓存策略 import com.monke.monkeybook.BitIntentDataManager; +// 用于管理Intent数据 import com.monke.monkeybook.R; +// 引用项目中的资源文件 import com.monke.monkeybook.base.MBaseActivity; +// 基类Activity import com.monke.monkeybook.presenter.IBookDetailPresenter; +// 书籍详情的Presenter接口 import com.monke.monkeybook.presenter.impl.BookDetailPresenterImpl; +// 书籍详情的Presenter实现类 import com.monke.monkeybook.presenter.impl.ReadBookPresenterImpl; +// 阅读书籍的Presenter实现类 import com.monke.monkeybook.utils.BlurTransformation; +// 图片模糊效果工具 import com.monke.monkeybook.view.IBookDetailView; +// 书籍详情视图接口 +// 书籍详情页面 Activity 类 public class BookDetailActivity extends MBaseActivity implements IBookDetailView { + + // 定义页面上的控件 private FrameLayout iflContent; + // 内容区域,可能用于包裹其他控件 private ImageView ivBlurCover; + // 用于显示模糊效果的封面 private ImageView ivCover; + // 用于显示书籍封面 private TextView tvName; + // 显示书籍名称 private TextView tvAuthor; + // 显示书籍作者 private TextView tvOrigin; + // 显示书籍来源 private TextView tvChapter; + // 显示当前章节 private TextView tvIntro; + // 显示书籍简介 private TextView tvShelf; + // 操作书架(放入/移出) private TextView tvRead; + // 操作阅读(开始阅读/继续阅读) private TextView tvLoading; + // 显示加载提示 - private Animation animHideLoading; - private Animation animShowInfo; + // 动画相关 + private Animation animHideLoading; // 隐藏加载动画 + private Animation animShowInfo; // 显示书籍信息动画 @Override protected IBookDetailPresenter initInjector() { + // 初始化Presenter,负责处理业务逻辑 return new BookDetailPresenterImpl(getIntent()); } @Override protected void onCreateActivity() { + // 设置当前Activity的布局文件 setContentView(R.layout.activity_detail); } @Override protected void initData() { - animShowInfo = AnimationUtils.loadAnimation(this, android.R.anim.fade_in); - animHideLoading = AnimationUtils.loadAnimation(this, android.R.anim.fade_out); - animHideLoading.setAnimationListener(new Animation.AnimationListener() { + // 初始化动画 + animShowInfo = AnimationUtils.loadAnimation(this, android.R.anim.fade_in); // 加载淡入动画 + animHideLoading = AnimationUtils.loadAnimation(this, android.R.anim.fade_out); // 加载淡出动画 + animHideLoading.setAnimationListener(new Animation.AnimationListener() { // 动画结束后执行的回调 @Override public void onAnimationStart(Animation animation) { - + // 动画开始时的处理(此处未做任何操作) } @Override public void onAnimationEnd(Animation animation) { + // 动画结束时,隐藏加载提示 tvLoading.setVisibility(View.GONE); } @Override public void onAnimationRepeat(Animation animation) { - + // 动画重复时的处理(此处未做任何操作) } }); } - @Override protected void bindView() { + // 绑定视图组件 iflContent = (FrameLayout) findViewById(R.id.ifl_content); + + // 获取内容区域 ivBlurCover = (ImageView) findViewById(R.id.iv_blur_cover); + + // 获取模糊封面 ivCover = (ImageView) findViewById(R.id.iv_cover); + + // 获取书籍封面 tvName = (TextView) findViewById(R.id.tv_name); + + // 获取书籍名称 tvAuthor = (TextView) findViewById(R.id.tv_author); + + // 获取书籍作者 tvOrigin = (TextView) findViewById(R.id.tv_origin); + + // 获取书籍来源 tvChapter = (TextView) findViewById(R.id.tv_chapter); + + // 获取当前章节 tvIntro = (TextView) findViewById(R.id.tv_intro); + + // 获取书籍简介 tvShelf = (TextView) findViewById(R.id.tv_shelf); + + // 获取书架操作按钮 tvRead = (TextView) findViewById(R.id.tv_read); + + // 获取阅读操作按钮 tvLoading = (TextView) findViewById(R.id.tv_loading); + // 获取加载提示 tvIntro.setMovementMethod(ScrollingMovementMethod.getInstance()); + // 使简介支持滚动 initView(); - + // 初始化界面 updateView(); + // 更新视图 } @Override public void updateView() { + // 根据Presenter获取的数据更新视图 if (null != mPresenter.getBookShelf()) { + // 如果书架信息存在 if (mPresenter.getInBookShelf()) { + // 如果书籍在书架中 + // 显示当前章节 if (mPresenter.getBookShelf().getBookInfoBean().getChapterlist().size() > 0) tvChapter.setText(String.format(getString(R.string.tv_read_durprogress), mPresenter.getBookShelf().getBookInfoBean().getChapterlist().get(mPresenter.getBookShelf().getDurChapter()).getDurChapterName())); else tvChapter.setText("无章节"); + + // 设置书架操作按钮为移出 tvShelf.setText("移出书架"); tvRead.setText("继续阅读"); + tvShelf.setOnClickListener(new View.OnClickListener() { + // 移出书架点击事件 @Override public void onClick(View v) { - //从书架移出 mPresenter.removeFromBookShelf(); + // 从书架移除 } }); } else { + // 如果书籍不在书架中 + // 显示最新章节 if (mPresenter.getBookShelf().getBookInfoBean().getChapterlist().size() == 0) { tvChapter.setText("无章节"); } else { tvChapter.setText(String.format(getString(R.string.tv_searchbook_lastest), mPresenter.getBookShelf().getBookInfoBean().getChapterlist().get(mPresenter.getBookShelf().getBookInfoBean().getChapterlist().size() - 1).getDurChapterName())); } + + // 设置书架操作按钮为放入 tvShelf.setText("放入书架"); tvRead.setText("开始阅读"); - tvShelf.setOnClickListener(new View.OnClickListener() { + tvShelf.setOnClickListener(new View.OnClickListener() { // 放入书架点击事件 @Override public void onClick(View v) { - //放入书架 - mPresenter.addToBookShelf(); + mPresenter.addToBookShelf(); // 放入书架 } }); } + + // 如果简介为空,则设置为书籍的介绍 if (tvIntro.getText().toString().trim().length() == 0) { tvIntro.setText(mPresenter.getBookShelf().getBookInfoBean().getIntroduce()); } + + // 显示简介并执行动画 if (tvIntro.getVisibility() != View.VISIBLE) { tvIntro.setVisibility(View.VISIBLE); tvIntro.startAnimation(animShowInfo); + // 显示动画 tvLoading.startAnimation(animHideLoading); + // 隐藏加载动画 } + + // 显示书籍来源 if (mPresenter.getBookShelf().getBookInfoBean().getOrigin() != null && mPresenter.getBookShelf().getBookInfoBean().getOrigin().length() > 0) { tvOrigin.setVisibility(View.VISIBLE); tvOrigin.setText("来源:" + mPresenter.getBookShelf().getBookInfoBean().getOrigin()); @@ -138,29 +221,38 @@ public class BookDetailActivity extends MBaseActivity impl tvOrigin.setVisibility(View.GONE); } } else { - tvChapter.setText(String.format(getString(R.string.tv_searchbook_lastest), mPresenter.getSearchBook().getLastChapter())); + // 如果书架信息为空 + tvChapter.setText(String.format(getString(R.string.tv_searchbook_lastest), mPresenter.getSearchBook().getLastChapter())); // 显示搜索书籍的最新章节 tvShelf.setText("放入书架"); tvRead.setText("开始阅读"); tvRead.setOnClickListener(new View.OnClickListener() { + // 阅读按钮点击事件 @Override public void onClick(View v) { - //放入书架 + // 放入书架操作(此处未实现) } }); tvIntro.setVisibility(View.INVISIBLE); + // 隐藏简介 tvLoading.setVisibility(View.VISIBLE); + // 显示加载提示 tvLoading.setText("加载中..."); + // 设置加载提示文字 } + + // 防止点击加载提示时触发操作 tvLoading.setOnClickListener(null); } @Override public void getBookShelfError() { + // 获取书架信息失败,显示错误信息并设置点击重试 tvLoading.setVisibility(View.VISIBLE); tvLoading.setText("加载失败,点击重试"); tvLoading.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + // 点击重试时重新获取书架信息 tvLoading.setText("加载中..."); tvLoading.setOnClickListener(null); mPresenter.getBookShelfInfo(); @@ -170,31 +262,40 @@ public class BookDetailActivity extends MBaseActivity impl @Override protected void firstRequest() { + // 初次请求时调用 super.firstRequest(); + // 如果是从搜索页面打开,并且书架信息为空,则发起网络请求获取书架信息 if (mPresenter.getOpenfrom() == BookDetailPresenterImpl.FROM_SEARCH && mPresenter.getBookShelf() == null) { - //网络请求 mPresenter.getBookShelfInfo(); } } + // 初始化页面控件显示 private void initView() { String coverUrl; + // 封面URL String name; + // 书籍名称 String author; + // 书籍作者 + if (mPresenter.getOpenfrom() == BookDetailPresenterImpl.FROM_BOOKSHELF) { + // 从书架打开 coverUrl = mPresenter.getBookShelf().getBookInfoBean().getCoverUrl(); name = mPresenter.getBookShelf().getBookInfoBean().getName(); author = mPresenter.getBookShelf().getBookInfoBean().getAuthor(); + // 显示书籍来源 if (mPresenter.getBookShelf().getBookInfoBean().getOrigin() != null && mPresenter.getBookShelf().getBookInfoBean().getOrigin().length() > 0) { tvOrigin.setVisibility(View.VISIBLE); tvOrigin.setText("来源:" + mPresenter.getBookShelf().getBookInfoBean().getOrigin()); } else { tvOrigin.setVisibility(View.GONE); } - } else { + } else { // 从搜索页面打开 coverUrl = mPresenter.getSearchBook().getCoverUrl(); name = mPresenter.getSearchBook().getName(); author = mPresenter.getSearchBook().getAuthor(); + // 显示书籍来源 if (mPresenter.getSearchBook().getOrigin() != null && mPresenter.getSearchBook().getOrigin().length() > 0) { tvOrigin.setVisibility(View.VISIBLE); tvOrigin.setText("来源:" + mPresenter.getSearchBook().getOrigin()); @@ -203,39 +304,47 @@ public class BookDetailActivity extends MBaseActivity impl } } + // 使用Glide加载封面图像,并显示模糊效果封面 Glide.with(this).load(coverUrl).dontAnimate().diskCacheStrategy(DiskCacheStrategy.RESULT).centerCrop().placeholder(R.drawable.img_cover_default).into(ivCover); Glide.with(this).load(coverUrl).dontAnimate().diskCacheStrategy(DiskCacheStrategy.RESULT).centerCrop().bitmapTransform(new BlurTransformation(this, 6)).into(ivBlurCover); + + // 设置书籍名称和作者 tvName.setText(name); tvAuthor.setText(author); } @Override protected void bindEvent() { + // 设置页面点击事件 iflContent.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + // 根据Android版本处理不同的退出动画 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - if(getStart_share_ele()){ + if(getStart_share_ele()) { finishAfterTransition(); - }else{ + } else { finish(); - overridePendingTransition(0,android.R.anim.fade_out); + overridePendingTransition(0, android.R.anim.fade_out); } } else { finish(); - overridePendingTransition(0,android.R.anim.fade_out); + overridePendingTransition(0, android.R.anim.fade_out); } } }); + // 设置阅读按钮点击事件 tvRead.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - //进入阅读 + // 跳转到阅读界面 Intent intent = new Intent(BookDetailActivity.this, ReadBookActivity.class); intent.putExtra("from", ReadBookPresenterImpl.OPEN_FROM_APP); String key = String.valueOf(System.currentTimeMillis()); intent.putExtra("data_key", key); + + // 使用BitIntentDataManager保存书籍数据并传递 try { BitIntentDataManager.getInstance().putData(key, mPresenter.getBookShelf().clone()); } catch (CloneNotSupportedException e) { @@ -244,16 +353,17 @@ public class BookDetailActivity extends MBaseActivity impl } startActivityByAnim(intent, android.R.anim.fade_in, android.R.anim.fade_out); + // 结束当前Activity并设置退出动画 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - if(getStart_share_ele()){ + if(getStart_share_ele()) { finishAfterTransition(); - }else{ + } else { finish(); - overridePendingTransition(0,android.R.anim.fade_out); + overridePendingTransition(0, android.R.anim.fade_out); } } else { finish(); - overridePendingTransition(0,android.R.anim.fade_out); + overridePendingTransition(0, android.R.anim.fade_out); } } }); -- 2.34.1