suo #1

Merged
pbxef79cv merged 11 commits from dev2 into main 1 year ago

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>

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

@ -1,12 +1,15 @@
// 声明包名
package com.monke.monkeybook;
import android.app.Application;
import android.test.ApplicationTestCase;
import android.app.Application;// 导入Application类代表整个应用程序
import android.test.ApplicationTestCase;// 导入ApplicationTestCase类它提供了应用测试的基础
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
* Android
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
// 构造函数,参数为应用程序类的.class文件对象
public ApplicationTest() {
super(Application.class);
}

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.monke.monkeybook">
package="com.monke.monkeybook"> <!-- 声明包名 -->
<application
android:name=".MApplication"
@ -12,39 +12,60 @@
android:theme="@style/CAppTheme"
tools:replace="android:theme"
android:networkSecurityConfig="@xml/network_security_config">
<!-- 自定义应用程序类 -->
<!-- 允许备份 -->
<!-- 应用图标 -->
<!-- 应用名称 -->
<!-- 支持从右向左的布局 -->
<!-- 应用主题 -->
<!-- 指示工具覆盖主题 -->
<!-- 网络安全配置 -->
<!-- 欢迎界面 -->
<activity
android:name=".view.impl.WelcomeActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<!-- 欢迎活动类 -->
<!-- 竖屏模式 -->
<intent-filter> <!-- 指定启动活动的意图过滤器 -->
<action android:name="android.intent.action.MAIN" /><!-- 主活动 -->
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LAUNCHER" /><!-- 启动器 -->
</intent-filter>
</activity>
<!-- 主活动 -->
<activity
android:name=".view.impl.MainActivity"
android:launchMode="singleTask"
android:screenOrientation="portrait" />
android:launchMode="singleTask" <!-- 单一任务模式 -->
android:screenOrientation="portrait" /><!-- 竖屏模式 -->
<!-- 搜索活动 -->
<activity
android:name=".view.impl.SearchActivity"
android:configChanges="locale|keyboardHidden|orientation|screenSize"
android:screenOrientation="portrait"
android:theme="@style/CAppTransparentTheme"
android:windowSoftInputMode="stateHidden|adjustPan" />
android:windowSoftInputMode="stateHidden|adjustPan" /><!-- 软件输入法隐藏时更改视图 -->
<service android:name=".service.DownloadService" />
<service android:name=".service.DownloadService" /><!-- 下载服务 -->
<!-- 库活动 -->
<activity
android:name=".view.impl.LibraryActivity"
android:screenOrientation="portrait"
android:theme="@style/CAppTransparentTheme" />
<!-- 选择书籍活动 -->
<activity
android:name=".view.impl.ChoiceBookActivity"
android:screenOrientation="portrait" />
<!-- 书籍详情活动 -->
<activity
android:name=".view.impl.BookDetailActivity"
android:screenOrientation="portrait"
android:theme="@style/CAppTransparentTheme" />
<!-- 阅读书籍活动 -->
<activity
android:name=".view.impl.ReadBookActivity"
android:launchMode="singleTask"
@ -52,24 +73,28 @@
<intent-filter>
<action android:name="android.intent.action.VIEW"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<data android:mimeType="text/plain"></data>
<data android:mimeType="text/plain"></data><!-- 处理文本类型数据 -->
</intent-filter>
</activity>
<!-- 导入书籍活动 -->
<activity
android:name=".view.impl.ImportBookActivity"
android:screenOrientation="portrait"
android:theme="@style/CAppTransparentTheme" />
<!-- 元数据 -->
<meta-data
android:name="UMENG_CHANNEL_VALUE"
android:value="${UMENG_CHANNEL_VALUE}" />
android:value="${UMENG_CHANNEL_VALUE}" /><!-- 用于第三方渠道的元数据 -->
</application>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- 权限要求 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><!-- 读取外部存储权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><!-- 写入外部存储权限 -->
<uses-permission android:name="android.permission.INTERNET" /><!-- 访问互联网权限 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><!-- 访问网络状态权限 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><!-- 访问Wi-Fi状态权限 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" /><!-- 读取电话状态权限 -->
</manifest>

@ -1,13 +1,13 @@
//指定了当前类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;
import android.database.Cursor;// 导入Cursor类用于数据库查询结果
import android.database.sqlite.SQLiteStatement;// 导入SQLiteStatement类用于预编译SQL语句
import org.greenrobot.greendao.AbstractDao;// 导入AbstractDao类这是GreenDAO的基类用于实现DAO功能
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;// 导入DatabaseStatement类用于执行数据库语句
// 导入实体类 BookContentBean
import com.monke.monkeybook.bean.BookContentBean;
@ -18,7 +18,7 @@ import com.monke.monkeybook.bean.BookContentBean;
//继承自GreenDAO的AbstractDao类BookContentBean为实体类String为主键类型
public class BookContentBeanDao extends AbstractDao<BookContentBean, String> {
//定义常量TABLENAME表示该表的名称
public static final String TABLENAME = "BOOK_CONTENT_BEAN";
public static final String TABLENAME = "BOOK_CONTENT_BEAN";// 表名常量
/**
* Properties of entity BookContentBean.<br/>
* Can be used for QueryBuilder and for referencing column names.
@ -26,10 +26,10 @@ public class BookContentBeanDao extends AbstractDao<BookContentBean, String> {
*/
// 定义实体类属性映射:对应数据库表字段名的属性
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");
public final static Property DurCapterContent = new Property(2, String.class, "durCapterContent", false, "DUR_CAPTER_CONTENT");
public final static Property Tag = new Property(3, String.class, "tag", false, "TAG");
public final static Property DurChapterUrl = new Property(0, String.class, "durChapterUrl", true, "DUR_CHAPTER_URL");// 章节URL属性
public final static Property DurChapterIndex = new Property(1, int.class, "durChapterIndex", false, "DUR_CHAPTER_INDEX");// 章节索引属性
public final static Property DurCapterContent = new Property(2, String.class, "durCapterContent", false, "DUR_CAPTER_CONTENT");// 章节内容属性
public final static Property Tag = new Property(3, String.class, "tag", false, "TAG");// 标签属性
};
//构造函数传入DaoConfig用于初始化DAO
@ -51,10 +51,10 @@ public class BookContentBeanDao extends AbstractDao<BookContentBean, String> {
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
"\"DUR_CAPTER_CONTENT\" TEXT," + // 2: durCapterContent
"\"TAG\" TEXT);"); // 3: tag
"\"DUR_CHAPTER_URL\" TEXT PRIMARY KEY NOT NULL ," + // 0: durChapterUrl, 主键列
"\"DUR_CHAPTER_INDEX\" INTEGER NOT NULL ," + // 1: durChapterIndex, 整数列
"\"DUR_CAPTER_CONTENT\" TEXT," + // 2: durCapterContent,章节内容列
"\"TAG\" TEXT);"); // 3: tag, 标签列
}
/** Drops the underlying database table.
@ -62,73 +62,73 @@ public class BookContentBeanDao extends AbstractDao<BookContentBean, String> {
*/
public static void dropTable(Database db, boolean ifExists) {
//使用SQL删除表确保表存在时删除
String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"BOOK_CONTENT_BEAN\"";
db.execSQL(sql);
String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"BOOK_CONTENT_BEAN\"";// 生成删除表的SQL语句
db.execSQL(sql);// 执行删除表操作
}
@Override
protected final void bindValues(DatabaseStatement stmt, BookContentBean entity) {
//清除绑定的值,确保每次绑定时都是干净的
stmt.clearBindings();
stmt.clearBindings();// 清除先前绑定的参数
//绑定durChapterUrl字段到SQL语句的第一个位置
String durChapterUrl = entity.getDurChapterUrl();
String durChapterUrl = entity.getDurChapterUrl();// 获取durChapterUrl属性值
if (durChapterUrl != null) {
//将章节URL字符串绑定到SQL语句的第一个位置
stmt.bindString(1, durChapterUrl);
stmt.bindString(1, durChapterUrl);// 将durChapterUrl绑定到SQL语句的第一个参数
}
//绑定durChapterIndex字段到SQL语句的第二个位置
stmt.bindLong(2, entity.getDurChapterIndex());//将章节索引绑定到SQL语句的第二个位置
//绑定durCapterContent字段到SQL语句的第三个位置
String durCapterContent = entity.getDurCapterContent();
String durCapterContent = entity.getDurCapterContent();// 获取durCapterContent属性值
if (durCapterContent != null) {
//将章节内容绑定到SQL语句的第三个位置
stmt.bindString(3, durCapterContent);
stmt.bindString(3, durCapterContent);// 将durCapterContent绑定到SQL语句的第三个参数
}
//绑定tag字段到SQL语句的第四个位置
String tag = entity.getTag();
String tag = entity.getTag();// 获取tag属性值
if (tag != null) {
//将标签绑定到SQL语句的第四个位置
stmt.bindString(4, tag);
stmt.bindString(4, tag);// 将tag绑定到SQL语句的第四个参数
}
}
@Override
protected final void bindValues(SQLiteStatement stmt, BookContentBean entity) {
//清除绑定的值,确保每次绑定时都是干净的
stmt.clearBindings();
stmt.clearBindings();// 清除先前绑定的参数
//绑定durChapterUrl字段到SQL语句的第一个位置
String durChapterUrl = entity.getDurChapterUrl();
String durChapterUrl = entity.getDurChapterUrl();// 获取durChapterUrl属性值
if (durChapterUrl != null) {
//将章节URL字符串绑定到SQL语句的第一个位置
stmt.bindString(1, durChapterUrl);
stmt.bindString(1, durChapterUrl);// 将durChapterUrl绑定到SQL语句的第一个参数
}
//绑定durChapterIndex字段到SQL语句的第二个位置
stmt.bindLong(2, entity.getDurChapterIndex()); //将章节索引绑定到SQL语句的第二个位置
//绑定durCapterContent字段到SQL语句的第三个位置
String durCapterContent = entity.getDurCapterContent();
String durCapterContent = entity.getDurCapterContent();// 获取durCapterContent属性值
if (durCapterContent != null) {
//将章节内容绑定到SQL语句的第三个位置
stmt.bindString(3, durCapterContent);
stmt.bindString(3, durCapterContent);// 将durCapterContent绑定到SQL语句的第三个参数
}
//绑定tag字段到SQL语句的第四个位置
String tag = entity.getTag();
String tag = entity.getTag();// 获取tag属性值
if (tag != null) {
//将标签绑定到SQL语句的第四个位置
stmt.bindString(4, tag);
stmt.bindString(4, tag);// 将tag绑定到SQL语句的第四个参数
}
}
@Override
public String readKey(Cursor cursor, int offset) {
//从Cursor中读取主键(durChapterUrl)若为null则返回null
return cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0);
return cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0);// 根据游标读取主键值
}
@Override
@ -140,38 +140,38 @@ public class BookContentBeanDao extends AbstractDao<BookContentBean, String> {
cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // durCapterContent
cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3) // tag
);
return entity;
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));
entity.setDurChapterUrl(cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0));// 设置durChapterUrl属性
entity.setDurChapterIndex(cursor.getInt(offset + 1));// 设置durChapterIndex属性
entity.setDurCapterContent(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2));// 设置durCapterContent属性
entity.setTag(cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3));// 设置tag属性
}
@Override
protected final String updateKeyAfterInsert(BookContentBean entity, long rowId) {
//返回更新时需要的主键字段这里是durChapterUrl
return entity.getDurChapterUrl();
return entity.getDurChapterUrl();// 返回用于更新的主键
}
@Override
public String getKey(BookContentBean entity) {
//获取实体对象的主键值返回durChapterUrl
if(entity != null) {
return entity.getDurChapterUrl();
return entity.getDurChapterUrl();// 返回实体的主键值
} else {
return null;
return null;// 如果实体为空返回null
}
}
@Override
protected final boolean isEntityUpdateable() {
//指示实体是否可以被更新这里返回true表示支持更新
return true;
return true;// 表示该实体可被更新
}
}

@ -44,12 +44,12 @@ public class BookInfoBeanDao extends AbstractDao<BookInfoBean, String> {
//构造函数使用DaoConfig配置创建DAO对象
public BookInfoBeanDao(DaoConfig config) {
super(config);
super(config);// 调用父类的构造函数传入DaoConfig配置
}
//构造函数使用DaoConfig和DaoSession配置创建DAO对象
public BookInfoBeanDao(DaoConfig config, DaoSession daoSession) {
super(config, daoSession);
super(config, daoSession);// 调用父类的构造函数传入DaoConfig和DaoSession配置
}
/** Creates the underlying database table.
@ -85,64 +85,64 @@ public class BookInfoBeanDao extends AbstractDao<BookInfoBean, String> {
//将BookInfoBean实体对象的值绑定到DatabaseStatement对象中
protected final void bindValues(DatabaseStatement stmt, BookInfoBean entity) {
//清除绑定的值,确保每次绑定时都是干净的
stmt.clearBindings();
stmt.clearBindings();// 清除之前的绑定值
//绑定name字段到SQL语句的第一个位置
String name = entity.getName();
String name = entity.getName();// 获取name属性值
if (name != null) {
//将书名绑定到SQL语句的第一个位置
stmt.bindString(1, name);
stmt.bindString(1, name);// 将name绑定到SQL语句的第一个位置
}
//绑定tag字段到SQL语句的第二个位置
String tag = entity.getTag();
String tag = entity.getTag();// 获取tag属性值
if (tag != null) {
//将标签绑定到SQL语句的第二个位置
stmt.bindString(2, tag);
stmt.bindString(2, tag);// 将tag绑定到SQL语句的第二个位置
}
//绑定noteUrl字段到SQL语句的第三个位置
String noteUrl = entity.getNoteUrl();
String noteUrl = entity.getNoteUrl(); // 获取noteUrl属性值
if (noteUrl != null) {
//将书籍URL绑定到SQL语句的第三个位置
stmt.bindString(3, noteUrl);
stmt.bindString(3, noteUrl);// 将noteUrl绑定到SQL语句的第三个位置
}
//绑定chapterUrl字段到SQL语句的第四个位置
String chapterUrl = entity.getChapterUrl();
String chapterUrl = entity.getChapterUrl();// 获取chapterUrl属性值
if (chapterUrl != null) {
//将章节URL绑定到SQL语句的第四个位置
stmt.bindString(4, chapterUrl);
stmt.bindString(4, chapterUrl);// 将chapterUrl绑定到SQL语句的第四个位置
}
//将最后刷新时间long 类型绑定到SQL语句的第五个位置
stmt.bindLong(5, entity.getFinalRefreshData());
stmt.bindLong(5, entity.getFinalRefreshData()); // 将finalRefreshData绑定到SQL语句的第五个位置
//绑定coverUrl字段到SQL语句的第六个位置
String coverUrl = entity.getCoverUrl();
String coverUrl = entity.getCoverUrl();// 获取coverUrl属性值
if (coverUrl != null) {
//将封面图URL绑定到SQL语句的第六个位置
stmt.bindString(6, coverUrl);
stmt.bindString(6, coverUrl);// 将coverUrl绑定到SQL语句的第六个位置
}
//绑定author字段到SQL语句的第七个位置
String author = entity.getAuthor();
String author = entity.getAuthor();// 获取author属性值
if (author != null) {
//将作者绑定到SQL语句的第七个位置
stmt.bindString(7, author);
stmt.bindString(7, author);// 将author绑定到SQL语句的第七个位置
}
//绑定introduce字段到SQL语句的第八个位置
String introduce = entity.getIntroduce();
String introduce = entity.getIntroduce();// 获取introduce属性值
if (introduce != null) {
//将简介绑定到SQL语句的第八个位置
stmt.bindString(8, introduce);
stmt.bindString(8, introduce);// 将introduce绑定到SQL语句的第八个位置
}
//绑定origin字段到SQL语句的第九个位置
String origin = entity.getOrigin();
String origin = entity.getOrigin();// 获取origin属性值
if (origin != null) {
//将来源绑定到SQL语句的第九个位置
stmt.bindString(9, origin);
stmt.bindString(9, origin);// 将origin绑定到SQL语句的第九个位置
}
}
@ -153,53 +153,53 @@ public class BookInfoBeanDao extends AbstractDao<BookInfoBean, String> {
stmt.clearBindings();
//绑定name字段值
String name = entity.getName();
String name = entity.getName();// 获取name属性值
if (name != null) {
stmt.bindString(1, name);
stmt.bindString(1, name);// 将name绑定到SQL语句的第一个位置
}
//绑定tag字段值
String tag = entity.getTag();
String tag = entity.getTag();// 获取tag属性值
if (tag != null) {
stmt.bindString(2, tag);
stmt.bindString(2, tag);// 将tag绑定到SQL语句的第二个位置
}
//绑定noteUrl字段值
String noteUrl = entity.getNoteUrl();
String noteUrl = entity.getNoteUrl();// 获取noteUrl属性值
if (noteUrl != null) {
stmt.bindString(3, noteUrl);
stmt.bindString(3, noteUrl);// 将noteUrl绑定到SQL语句的第三个位置
}
//绑定chapterUrl字段值
String chapterUrl = entity.getChapterUrl();
String chapterUrl = entity.getChapterUrl();// 获取chapterUrl属性值
if (chapterUrl != null) {
stmt.bindString(4, chapterUrl);
stmt.bindString(4, chapterUrl);// 将chapterUrl绑定到SQL语句的第四个位置
}
//绑定finalRefreshData字段值
stmt.bindLong(5, entity.getFinalRefreshData());
stmt.bindLong(5, entity.getFinalRefreshData());// 将finalRefreshData绑定到SQL语句的第五个位置
//绑定coverUrl字段值
String coverUrl = entity.getCoverUrl();
String coverUrl = entity.getCoverUrl();// 获取coverUrl属性值
if (coverUrl != null) {
stmt.bindString(6, coverUrl);
stmt.bindString(6, coverUrl);// 将coverUrl绑定到SQL语句的第六个位置
}
//绑定author字段值
String author = entity.getAuthor();
String author = entity.getAuthor();// 获取author属性值
if (author != null) {
stmt.bindString(7, author);
stmt.bindString(7, author);// 将author绑定到SQL语句的第七个位置
}
//绑定introduce字段值
String introduce = entity.getIntroduce();
String introduce = entity.getIntroduce();// 获取introduce属性值
if (introduce != null) {
stmt.bindString(8, introduce);
stmt.bindString(8, introduce);// 将introduce绑定到SQL语句的第八个参数
}
//绑定origin字段值
String origin = entity.getOrigin();
String origin = entity.getOrigin();// 获取来源属性值
if (origin != null) {
stmt.bindString(9, origin);
stmt.bindString(9, origin);// 将来源绑定到SQL语句的第九个参数
}
}
@ -207,7 +207,7 @@ public class BookInfoBeanDao extends AbstractDao<BookInfoBean, String> {
//从Cursor中读取主键
public String readKey(Cursor cursor, int offset) {
//主键在第三列(offset + 2)如果为空返回null否则返回字符串
return cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2);
return cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2);// 读取主键noteUrl字段
}
@Override
@ -226,28 +226,28 @@ public class BookInfoBeanDao extends AbstractDao<BookInfoBean, String> {
cursor.isNull(offset + 8) ? null : cursor.getString(offset + 8) // origin
);
//返回创建好的BookInfoBean对象
return entity;
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));//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
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();
return entity.getNoteUrl();// 返回用于更新的主键
}
@Override
@ -255,9 +255,9 @@ public class BookInfoBeanDao extends AbstractDao<BookInfoBean, String> {
public String getKey(BookInfoBean entity) {
if(entity != null) {
//返回noteUrl作为主键
return entity.getNoteUrl();
return entity.getNoteUrl();// 返回实体的主键noteUrl
} else {
return null;
return null;// 如果实体为空返回null
}
}
@ -265,7 +265,7 @@ public class BookInfoBeanDao extends AbstractDao<BookInfoBean, String> {
//是否可更新实体
protected final boolean isEntityUpdateable() {
//可更新
return true;
return true;// 表示该实体可更新
}
}

@ -36,12 +36,12 @@ public class BookShelfBeanDao extends AbstractDao<BookShelfBean, String> {
// 构造函数使用DaoConfig配置初始化Dao
public BookShelfBeanDao(DaoConfig config) {
super(config);
super(config);// 调用父类构造函数进行初始化
}
// 构造函数使用DaoConfig和DaoSession配置初始化Dao
public BookShelfBeanDao(DaoConfig config, DaoSession daoSession) {
super(config, daoSession);
super(config, daoSession);// 调用父类构造函数进行初始化
}
/** Creates the underlying database table.
@ -66,27 +66,27 @@ public class BookShelfBeanDao extends AbstractDao<BookShelfBean, String> {
// 删除BOOK_SHELF_BEAN表的方法
public static void dropTable(Database db, boolean ifExists) {
// 构造删除表的SQL语句
String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"BOOK_SHELF_BEAN\"";
String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"BOOK_SHELF_BEAN\"";// 如果指定ifExists则添加条件
// 执行SQL语句删除表
db.execSQL(sql);
db.execSQL(sql);// 执行删除表的SQL语句
}
@Override
// 将BookShelfBean实体对象的值绑定到DatabaseStatement语句中
protected final void bindValues(DatabaseStatement stmt, BookShelfBean entity) {
// 清除之前的绑定
stmt.clearBindings();
stmt.clearBindings();// 清除绑定,以保证每次都是干净的
String noteUrl = entity.getNoteUrl();
String noteUrl = entity.getNoteUrl(); // 获取noteUrl属性值
if (noteUrl != null) {
// 绑定noteUrl值到第一个参数位置
stmt.bindString(1, noteUrl);
stmt.bindString(1, noteUrl);// 将noteUrl绑定到SQL语句的第一个位置
}
stmt.bindLong(2, entity.getDurChapter()); // 绑定durChapter值到第二个参数位置
stmt.bindLong(3, entity.getDurChapterPage()); // 绑定durChapterPage值到第三个参数位置
stmt.bindLong(4, entity.getFinalDate()); // 绑定finalDate值到第四个参数位置
String tag = entity.getTag();
String tag = entity.getTag();// 获取tag属性值
if (tag != null) {
stmt.bindString(5, tag); // 绑定tag值到第五个参数位置
}
@ -96,21 +96,21 @@ public class BookShelfBeanDao extends AbstractDao<BookShelfBean, String> {
// 将BookShelfBean实体对象的值绑定到SQLiteStatement语句中与上一个方法功能类似只是对象不同
protected final void bindValues(SQLiteStatement stmt, BookShelfBean entity) {
// 清除之前的绑定
stmt.clearBindings();
stmt.clearBindings();// 清除绑定,以保证每次都是干净的
String noteUrl = entity.getNoteUrl();
String noteUrl = entity.getNoteUrl();// 获取noteUrl属性值
if (noteUrl != null) {
// 绑定noteUrl值到第一个参数位置
stmt.bindString(1, noteUrl);
stmt.bindString(1, noteUrl);// 将noteUrl绑定到SQL语句的第一个位置
}
stmt.bindLong(2, entity.getDurChapter()); // 绑定durChapter值到第二个参数位置
stmt.bindLong(3, entity.getDurChapterPage()); // 绑定durChapterPage值到第三个参数位置
stmt.bindLong(4, entity.getFinalDate()); // 绑定finalDate值到第四个参数位置
String tag = entity.getTag();
String tag = entity.getTag();// 获取tag属性值
if (tag != null) {
// 绑定tag值到第五个参数位置
stmt.bindString(5, tag);
stmt.bindString(5, tag);// 将tag绑定到SQL语句的第五个位置
}
}
@ -118,7 +118,7 @@ public class BookShelfBeanDao extends AbstractDao<BookShelfBean, String> {
// 从游标中读取主键
public String readKey(Cursor cursor, int offset) {
// 从偏移量offset+0的位置读取主键值如果为空返回null否则返回字符串
return cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0);
return cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0);// noteUrl是主键
}
@Override
@ -133,24 +133,24 @@ public class BookShelfBeanDao extends AbstractDao<BookShelfBean, String> {
cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4) // tag
);
// 返回创建好的BookShelfBean对象
return entity;
return entity;// 返回读取到的实体对象
}
@Override
// 将游标中的数据读取到已存在的BookShelfBean实体中
public void readEntity(Cursor cursor, BookShelfBean entity, int offset) {
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
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();
return entity.getNoteUrl();// 用于更新的主键值
}
@Override
@ -158,9 +158,9 @@ public class BookShelfBeanDao extends AbstractDao<BookShelfBean, String> {
public String getKey(BookShelfBean entity) {
if(entity != null) {
// 返回noteUrl作为主键
return entity.getNoteUrl();
return entity.getNoteUrl();// 返回实体的主键值
} else {
return null;
return null;// 如果实体为空则返回null
}
}
@ -168,7 +168,7 @@ public class BookShelfBeanDao extends AbstractDao<BookShelfBean, String> {
// 判断实体是否可更新
protected final boolean isEntityUpdateable() {
// 返回true表示实体可更新
return true;
return true;// 该实体类型支持更新
}
}

@ -11,22 +11,24 @@ import org.greenrobot.greendao.database.Database; // 导入Database类GreenDa
import org.greenrobot.greendao.database.DatabaseStatement; // 导入DatabaseStatement类用于执行SQL语句
// 导入ChapterListBean类这是数据库操作的对象
import com.monke.monkeybook.bean.ChapterListBean;
import com.monke.monkeybook.bean.ChapterListBean;// 导入ChapterListBean实体类
// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
/**
* DAO for table "CHAPTER_LIST_BEAN".
* "CHAPTER_LIST_BEAN"DAO
*/
// ChapterListBeanDao类继承AbstractDao操作ChapterListBean对象主键类型为String
public class ChapterListBeanDao extends AbstractDao<ChapterListBean, String> {
// 数据库表名常量
public static final String TABLENAME = "CHAPTER_LIST_BEAN";
public static final String TABLENAME = "CHAPTER_LIST_BEAN";// 定义常量,表示数据库表的名称
/**
* Properties of entity ChapterListBean.<br/>
* 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"); // 属性0noteUrlString类型非主键数据库列名NOTE_URL
public final static Property DurChapterIndex = new Property(1, int.class, "durChapterIndex", false, "DUR_CHAPTER_INDEX"); // 属性1durChapterIndexint类型非主键数据库列名DUR_CHAPTER_INDEX
public final static Property DurChapterUrl = new Property(2, String.class, "durChapterUrl", true, "DUR_CHAPTER_URL"); // 属性2durChapterUrlString类型主键数据库列名DUR_CHAPTER_URL
@ -37,19 +39,19 @@ public class ChapterListBeanDao extends AbstractDao<ChapterListBean, String> {
// 构造函数使用DaoConfig配置初始化Dao
public ChapterListBeanDao(DaoConfig config) {
super(config);
super(config);// 调用父类构造函数初始化DAO对象
}
// 构造函数使用DaoConfig和DaoSession配置初始化Dao
public ChapterListBeanDao(DaoConfig config, DaoSession daoSession) {
super(config, daoSession);
super(config, daoSession);// 调用父类构造函数初始化DAO对象
}
/** 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 ": "";
String constraint = ifNotExists? "IF NOT EXISTS ": "";// 如果ifNotExists为真则添加条件
// 执行SQL语句创建表
db.execSQL("CREATE TABLE " + constraint + "\"CHAPTER_LIST_BEAN\" (" + //
"\"NOTE_URL\" TEXT," + // NOTE_URL字段TEXT类型
@ -66,47 +68,47 @@ public class ChapterListBeanDao extends AbstractDao<ChapterListBean, String> {
// 删除CHAPTER_LIST_BEAN表的方法
public static void dropTable(Database db, boolean ifExists) {
// 构造删除表的SQL语句
String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"CHAPTER_LIST_BEAN\"";
String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"CHAPTER_LIST_BEAN\"";// 如果ifExists为真则加上条件
// 执行SQL语句删除表
db.execSQL(sql);
db.execSQL(sql);// 执行删除表的SQL语句
}
@Override
// 将ChapterListBean实体对象的值绑定到DatabaseStatement语句中
protected final void bindValues(DatabaseStatement stmt, ChapterListBean entity) {
// 清除之前的绑定
stmt.clearBindings();
stmt.clearBindings();// 确保每次绑定之前清除旧的绑定
String noteUrl = entity.getNoteUrl();
String noteUrl = entity.getNoteUrl();// 获取noteUrl属性值
if (noteUrl != null) {
// 绑定noteUrl值到第一个参数位置
stmt.bindString(1, noteUrl);
stmt.bindString(1, noteUrl);// 将noteUrl绑定到SQL语句的第一个位置
}
// 绑定durChapterIndex值到第二个参数位置
stmt.bindLong(2, entity.getDurChapterIndex());
stmt.bindLong(2, entity.getDurChapterIndex());// 将durChapterIndex绑定到SQL语句的第二个位置
String durChapterUrl = entity.getDurChapterUrl();
String durChapterUrl = entity.getDurChapterUrl();// 获取durChapterUrl属性值
if (durChapterUrl != null) {
// 绑定durChapterUrl值到第三个参数位置
stmt.bindString(3, durChapterUrl);
stmt.bindString(3, durChapterUrl);// 将durChapterUrl绑定到SQL语句的第三个位置
}
String durChapterName = entity.getDurChapterName();
String durChapterName = entity.getDurChapterName();// 获取durChapterName属性值
if (durChapterName != null) {
// 绑定durChapterName值到第四个参数位置
stmt.bindString(4, durChapterName);
stmt.bindString(4, durChapterName);// 将durChapterName绑定到SQL语句的第四个位置
}
String tag = entity.getTag();
String tag = entity.getTag();// 获取tag属性值
if (tag != null) {
// 绑定tag值到第五个参数位置
stmt.bindString(5, tag);
stmt.bindString(5, tag);// 将tag绑定到SQL语句的第五个位置
}
Boolean hasCache = entity.getHasCache();
Boolean hasCache = entity.getHasCache();// 获取hasCache属性值
if (hasCache != null) {
// 绑定hasCache值到第六个参数位置true为1false为0
stmt.bindLong(6, hasCache ? 1L: 0L);
stmt.bindLong(6, hasCache ? 1L: 0L);// 将hasCache绑定到SQL语句的第六个位置布尔值转为整数
}
}
@ -114,38 +116,38 @@ public class ChapterListBeanDao extends AbstractDao<ChapterListBean, String> {
// 将ChapterListBean实体对象的值绑定到SQLiteStatement语句中与上一个方法功能类似只是对象不同
protected final void bindValues(SQLiteStatement stmt, ChapterListBean entity) {
// 清除之前的绑定
stmt.clearBindings();
stmt.clearBindings();// 确保每次绑定之前清除旧的绑定
String noteUrl = entity.getNoteUrl();
String noteUrl = entity.getNoteUrl();// 获取noteUrl属性值
if (noteUrl != null) {
// 绑定noteUrl值到第一个参数位置
stmt.bindString(1, noteUrl);
stmt.bindString(1, noteUrl);// 将noteUrl绑定到SQL语句的第一个位置
}
// 绑定durChapterIndex值到第二个参数位置
stmt.bindLong(2, entity.getDurChapterIndex());
stmt.bindLong(2, entity.getDurChapterIndex());// 将durChapterIndex绑定到SQL语句的第二个位置
String durChapterUrl = entity.getDurChapterUrl();
String durChapterUrl = entity.getDurChapterUrl();// 获取durChapterUrl属性值
if (durChapterUrl != null) {
// 绑定durChapterUrl值到第三个参数位置
stmt.bindString(3, durChapterUrl);
stmt.bindString(3, durChapterUrl);// 将durChapterUrl绑定到SQL语句的第三个位置
}
String durChapterName = entity.getDurChapterName();
String durChapterName = entity.getDurChapterName();// 获取durChapterName属性值
if (durChapterName != null) {
// 绑定durChapterName值到第四个参数位置
stmt.bindString(4, durChapterName);
stmt.bindString(4, durChapterName);// 将durChapterName绑定到SQL语句的第四个位置
}
String tag = entity.getTag();
String tag = entity.getTag();// 获取tag属性值
if (tag != null) {
// 绑定tag值到第五个参数位置
stmt.bindString(5, tag);
stmt.bindString(5, tag);// 将tag绑定到SQL语句的第五个位置
}
Boolean hasCache = entity.getHasCache();
Boolean hasCache = entity.getHasCache();// 获取hasCache属性值
if (hasCache != null) {
// 绑定hasCache值到第六个参数位置true为1false为0
stmt.bindLong(6, hasCache ? 1L: 0L);
stmt.bindLong(6, hasCache ? 1L: 0L);// 将hasCache绑定到SQL语句的第六个位置布尔值转为整数
}
}
@ -153,7 +155,7 @@ public class ChapterListBeanDao extends AbstractDao<ChapterListBean, String> {
// 从游标中读取主键
public String readKey(Cursor cursor, int offset) {
// 从偏移量offset+2的位置读取主键值如果为空返回null否则返回字符串
return cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2);
return cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2);// 返回durChapterUrl作为主键
}
@Override
@ -169,7 +171,7 @@ public class ChapterListBeanDao extends AbstractDao<ChapterListBean, String> {
cursor.isNull(offset + 5) ? null : cursor.getShort(offset + 5) != 0 // hasCache 将short转换为boolean
);
// 返回创建好的ChapterListBean对象
return entity;
return entity;// 返回读取到的实体对象
}
@Override
@ -187,7 +189,7 @@ public class ChapterListBeanDao extends AbstractDao<ChapterListBean, String> {
// 插入数据后更新主键
protected final String updateKeyAfterInsert(ChapterListBean entity, long rowId) {
// 返回durChapterUrl作为主键
return entity.getDurChapterUrl();
return entity.getDurChapterUrl();// 用于更新的主键值
}
@Override
@ -195,9 +197,9 @@ public class ChapterListBeanDao extends AbstractDao<ChapterListBean, String> {
public String getKey(ChapterListBean entity) {
if(entity != null) {
// 返回durChapterUrl作为主键
return entity.getDurChapterUrl();
return entity.getDurChapterUrl();// 返回实体的主键值
} else {
return null;
return null; // 实体为空返回null
}
}
@ -205,7 +207,7 @@ public class ChapterListBeanDao extends AbstractDao<ChapterListBean, String> {
// 判断实体是否可更新
protected final boolean isEntityUpdateable() {
// 返回true表示实体可更新
return true;
return true;// 该实体类型支持更新
}
}

@ -20,7 +20,7 @@ import org.greenrobot.greendao.identityscope.IdentityScopeType; // 导入Identit
// DaoMaster类继承AbstractDaoMaster是所有DAO的管理者
public class DaoMaster extends AbstractDaoMaster {
// 数据库模式版本号
public static final int SCHEMA_VERSION = 1;
public static final int SCHEMA_VERSION = 1;// 定义常量,表示数据库的版本号
/** Creates underlying database table using DAOs. */
// 创建所有数据库表的方法
@ -55,19 +55,20 @@ public class DaoMaster extends AbstractDaoMaster {
// 创建DaoMaster实例
DaoMaster daoMaster = new DaoMaster(db);
// 创建DaoSession实例并返回
return daoMaster.newSession();
return daoMaster.newSession();// 创建并返回DaoSession
}
// 构造函数使用SQLiteDatabase对象初始化DaoMaster
public DaoMaster(SQLiteDatabase db) {
// 使用StandardDatabase包装SQLiteDatabase
this(new StandardDatabase(db));
this(new StandardDatabase(db));// 创建StandardDatabase对象并调用另一个构造函数
}
// 构造函数使用Database对象初始化DaoMaster
public DaoMaster(Database db) {
// 调用父类的构造函数
super(db, SCHEMA_VERSION);
// 注册所有DAO类供后续使用
registerDaoClass(BookContentBeanDao.class); // 注册BookContentBeanDao
registerDaoClass(BookInfoBeanDao.class); // 注册BookInfoBeanDao
registerDaoClass(BookShelfBeanDao.class); // 注册BookShelfBeanDao
@ -78,14 +79,14 @@ public class DaoMaster extends AbstractDaoMaster {
// 创建一个新的DaoSession使用默认的IdentityScopeType.Session
public DaoSession newSession() {
// 创建DaoSession实例
return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
// 创建DaoSession实例使用Session作用域
return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);// 返回新的DaoSession实例
}
// 创建一个新的DaoSession使用指定的IdentityScopeType
public DaoSession newSession(IdentityScopeType type) {
// 创建DaoSession实例
return new DaoSession(db, type, daoConfigMap);
// 创建DaoSession实例,使用指定的作用域类型
return new DaoSession(db, type, daoConfigMap);// 返回新的DaoSession实例
}
/**
@ -96,21 +97,21 @@ public class DaoMaster extends AbstractDaoMaster {
// 构造函数,指定上下文和数据库名称
public OpenHelper(Context context, String name) {
// 调用父类的构造函数
super(context, name, SCHEMA_VERSION);
super(context, name, SCHEMA_VERSION);/
}
// 构造函数,指定上下文、数据库名称和游标工厂
public OpenHelper(Context context, String name, CursorFactory factory) {
// 调用父类的构造函数
super(context, name, factory, SCHEMA_VERSION);
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);
createAllTables(db, false);// 调用创建所有表的方法
}
}
@ -120,13 +121,13 @@ public class DaoMaster extends AbstractDaoMaster {
// 构造函数,指定上下文和数据库名称
public DevOpenHelper(Context context, String name) {
// 调用父类的构造函数
super(context, name);
super(context, name); // 设置上下文和数据库名称
}
// 构造函数,指定上下文、数据库名称和游标工厂
public DevOpenHelper(Context context, String name, CursorFactory factory) {
// 调用父类的构造函数
super(context, name, factory);
super(context, name, factory);// 设置上下文、数据库名称和游标工厂
}
@Override
@ -137,7 +138,7 @@ public class DaoMaster extends AbstractDaoMaster {
// 删除所有数据库表
dropAllTables(db, true);
// 重新创建数据库表
onCreate(db);
onCreate(db);// 调用创建表的方法
}
}

@ -1,164 +1,181 @@
// 定义包名
package com.monke.monkeybook.widget;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.annotation.AttrRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StyleRes;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.monke.monkeybook.R;
import com.monke.monkeybook.bean.BookShelfBean;
import com.monke.monkeybook.view.adapter.ChapterListAdapter;
public class ChapterListView extends FrameLayout{
private TextView tvName;
private TextView tvListCount;
private RecyclerView rvList;
private RecyclerViewBar rvbSlider;
private FrameLayout flBg;
private LinearLayout llContent;
private ChapterListAdapter chapterListAdapter;
private Animation animIn;
private Animation animOut;
import android.annotation.TargetApi;// 导入 TargetApi 注解,标明特定的 API 级别
import android.content.Context;// 导入 Context 类,提供应用的上下文
import android.os.Build;// 导入 Build 类,用于获取当前 Android 版本信息
import android.support.annotation.AttrRes; // 导入 AttrRes 注解,表示属性资源的类型。
import android.support.annotation.NonNull;// 导入 NonNull 注解,表示不允许为 null。
import android.support.annotation.Nullable;// 导入 Nullable 注解,表示可以为 null。
import android.support.annotation.StyleRes;// 导入 StyleRes 注解,表示样式资源的类型。
import android.support.v7.widget.LinearLayoutManager;// 导入 LinearLayoutManager用于 RecyclerView 的线性布局。
import android.support.v7.widget.RecyclerView;// 导入 RecyclerView 类,适用于高效的列表展示。
import android.util.AttributeSet;// 导入 AttributeSet 类,用于访问 XML 布局中的自定义属性。
import android.view.LayoutInflater;// 导入 LayoutInflater 类,用于将布局文件转换为 View 对象。
import android.view.View;// 导入 View 类,所有 UI 控件的基类。
import android.view.animation.Animation;// 导入 Animation 类,用于创建动画效果。
import android.view.animation.AnimationUtils;// 导入 AnimationUtils 类,提供加载动画资源的方法。
import android.widget.FrameLayout;// 导入 FrameLayout 类,允许堆叠子视图的布局。
import android.widget.LinearLayout; // 导入 LinearLayout 类,允许在垂直或水平方向上排列子视图。
import android.widget.TextView;// 导入 TextView 类,用于显示文本。
import com.monke.monkeybook.R;// 导入应用资源类,访问资源文件。
import com.monke.monkeybook.bean.BookShelfBean;// 导入 BookShelfBean 类,书架数据模型。
import com.monke.monkeybook.view.adapter.ChapterListAdapter;// 导入 ChapterListAdapter 类,章节列表适配器。
public class ChapterListView extends FrameLayout{// 创建 ChapterListView 类,继承自 FrameLayout
private TextView tvName;// 显示书名的 TextView
private TextView tvListCount;// 显示章节数量的 TextView
private RecyclerView rvList;// 显示章节列表的 RecyclerView
private RecyclerViewBar rvbSlider;// 自定义的 RecyclerView 导航条
private FrameLayout flBg;// 背景 FrameLayout
private LinearLayout llContent;// 内容 LinearLayout
private ChapterListAdapter chapterListAdapter;// 章节列表适配器
private Animation animIn; // 动画进入效果
private Animation animOut; // 动画退出效果
// 默认构造函数
public ChapterListView(@NonNull Context context) {
this(context,null);
this(context,null);// 调用带参构造函数,传递 null
}
// 带 AttributeSet 的构造函数
public ChapterListView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
this(context, attrs,0);// 调用带风格属性的构造函数
}
// 带属性和风格的构造函数
public ChapterListView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
super(context, attrs, defStyleAttr);// 调用父类构造函数
init();// 调用初始化方法
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
// 具有更多参数的构造函数
@TargetApi(Build.VERSION_CODES.LOLLIPOP)// 指示支持 API 21 及以上
public ChapterListView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
super(context, attrs, defStyleAttr, defStyleRes);// 调用父类构造函数
init();// 调用初始化方法
}
// 初始化方法
private void init() {
setVisibility(INVISIBLE);
LayoutInflater.from(getContext()).inflate(R.layout.view_chapterlist,this,true);
initData();
initView();
setVisibility(INVISIBLE);// 默认隐藏视图
LayoutInflater.from(getContext()).inflate(R.layout.view_chapterlist, this, true); // 加载布局
initData(); // 初始化数据
initView(); // 初始化视图组件
}
// 初始化数据和动画
private void initData() {
// 加载进入动画
animIn = AnimationUtils.loadAnimation(getContext(),R.anim.anim_pop_chapterlist_in);
/
animIn.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
flBg.setOnClickListener(null);
flBg.setOnClickListener(null);// 动画开始时禁止点击
}
@Override
public void onAnimationEnd(Animation animation) {
flBg.setOnClickListener(new OnClickListener() {
flBg.setOnClickListener(new OnClickListener() {// 动画结束后允许点击
@Override
public void onClick(View v) {
dimissChapterList();
dimissChapterList();// 点击背景时关闭章节列表
}
});
}
@Override
public void onAnimationRepeat(Animation animation) {
// 动画重复时的反应(可根据需要实现
}
});
animOut = AnimationUtils.loadAnimation(getContext(),R.anim.anim_pop_chapterlist_out);
// 加载退出动画
animOut = AnimationUtils.loadAnimation(getContext(),R.anim.anim_pop_chapterlist_out);// 退出动画
// 设置退出动画监听器
animOut.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
flBg.setOnClickListener(null);
flBg.setOnClickListener(null);// 动画开始时禁止点击
}
@Override
public void onAnimationEnd(Animation animation) {
llContent.setVisibility(INVISIBLE);
setVisibility(INVISIBLE);
llContent.setVisibility(INVISIBLE); // 隐藏内容
setVisibility(INVISIBLE); // 隐藏视图
}
@Override
public void onAnimationRepeat(Animation animation) {
// 动画重复时的反应(可根据需要实现)
}
});
}
// 显示章节列表
public void show(int durChapter) {
chapterListAdapter.setIndex(durChapter);
((LinearLayoutManager) rvList.getLayoutManager()).scrollToPositionWithOffset(durChapter,0);
if(getVisibility()!=VISIBLE){
setVisibility(VISIBLE);
animOut.cancel();
animIn.cancel();
llContent.setVisibility(VISIBLE);
llContent.startAnimation(animIn);
chapterListAdapter.setIndex(durChapter); // 设置当前章节索引
((LinearLayoutManager) rvList.getLayoutManager()).scrollToPositionWithOffset(durChapter, 0); // 滚动到特定章节
if(getVisibility() != VISIBLE) {
setVisibility(VISIBLE); // 显示章节列表
animOut.cancel(); // 取消退出动画
animIn.cancel(); // 取消进入动画
llContent.setVisibility(VISIBLE); // 显示内容
llContent.startAnimation(animIn); // 开始进入动画
}
}
public interface OnItemClickListener{
public void itemClick(int index);
// 点击监听接口
public interface OnItemClickListener{// 点击监听接口
public void itemClick(int index);// 点击事件方法
}
private OnItemClickListener itemClickListener;
private BookShelfBean bookShelfBean;
private void initView() {
flBg = (FrameLayout) findViewById(R.id.fl_bg);
llContent = (LinearLayout) findViewById(R.id.ll_content);
tvName = (TextView) findViewById(R.id.tv_name);
tvListCount = (TextView) findViewById(R.id.tv_listcount);
rvList = (RecyclerView) findViewById(R.id.rv_list);
rvList.setLayoutManager(new LinearLayoutManager(getContext()));
rvList.setItemAnimator(null);
rvbSlider = (RecyclerViewBar) findViewById(R.id.rvb_slider);
private OnItemClickListener itemClickListener; // 用于监听点击事件的实例
private BookShelfBean bookShelfBean; // 书架数据类实例
// 初始化视图组件
private void initView() { // 初始化视图组件
flBg = (FrameLayout) findViewById(R.id.fl_bg); // 获取背景 FrameLayout
llContent = (LinearLayout) findViewById(R.id.ll_content); // 获取内容 LinearLayout
tvName = (TextView) findViewById(R.id.tv_name); // 获取书名 TextView
tvListCount = (TextView) findViewById(R.id.tv_listcount); // 获取章节数量 TextView
rvList = (RecyclerView) findViewById(R.id.rv_list); // 获取章节列表 RecyclerView
rvList.setLayoutManager(new LinearLayoutManager(getContext())); // 设置布局管理器
rvList.setItemAnimator(null); // 关闭动画效果
rvbSlider = (RecyclerViewBar) findViewById(R.id.rvb_slider); // 获取自定义滑动条
}
public void setData(BookShelfBean bookShelfBean,OnItemClickListener clickListener) {
this.itemClickListener = clickListener;
this.bookShelfBean = bookShelfBean;
tvName.setText(bookShelfBean.getBookInfoBean().getName());
tvListCount.setText("共"+bookShelfBean.getBookInfoBean().getChapterlist().size()+"章");
chapterListAdapter = new ChapterListAdapter(bookShelfBean, new OnItemClickListener() {
public void setData(BookShelfBean bookShelfBean, OnItemClickListener clickListener) { // 设置数据
this.itemClickListener = clickListener; // 设置点击监听器
this.bookShelfBean = bookShelfBean; // 设置书架数据
tvName.setText(bookShelfBean.getBookInfoBean().getName()); // 设置书名
tvListCount.setText("共" + bookShelfBean.getBookInfoBean().getChapterlist().size() + "章"); // 设置章节数量文本
chapterListAdapter = new ChapterListAdapter(bookShelfBean, new OnItemClickListener() { // 创建章节列表适配器
@Override
// 处理章节单击事件
public void itemClick(int index) {
if(itemClickListener!=null){
itemClickListener.itemClick(index);
rvbSlider.scrollToPositionWithOffset(index);
if(itemClickListener != null) {
itemClickListener.itemClick(index); // 回调点击事件
rvbSlider.scrollToPositionWithOffset(index); // 滚动到对应位置
}
}
});
rvList.setAdapter(chapterListAdapter);
rvbSlider.setRecyclerView(rvList);
rvList.setAdapter(chapterListAdapter); // 设置适配器到 RecyclerView
rvbSlider.setRecyclerView(rvList); // 设置滑动条的 RecyclerView
}
public Boolean dimissChapterList(){
if(getVisibility()!=VISIBLE){
return false;
}else{
animOut.cancel();
animIn.cancel();
llContent.startAnimation(animOut);
return true;
// 关闭章节列表
public Boolean dimissChapterList() {
if(getVisibility() != VISIBLE) {
return false; // 如果已经隐藏,返回 false
} else {
animOut.cancel(); // 取消退出动画
animIn.cancel(); // 取消进入动画
llContent.startAnimation(animOut); // 开始退出动画
return true; // 返回 true 表示正在关闭
}
}
}

@ -1,33 +1,43 @@
package com.monke.monkeybook.widget;
import android.content.Context;
import android.graphics.Canvas;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
// 导入必要的 Android 类
import android.content.Context;// 导入 Context 类,用于访问应用的环境信息。
import android.graphics.Canvas;// 导入 Canvas 类,用于进行自定义绘制和图形操作。
import android.text.Layout;// 导入 Layout 类,用于处理文本的布局,控制文本的排列和格式。
import android.text.StaticLayout;// 导入 StaticLayout 类,帮助绘制多行静态文本。
import android.text.TextPaint;// 导入 TextPaint 类,扩展了 Paint 类用于绘制文本并包含字体相关的信息。
import android.util.AttributeSet;// 导入 AttributeSet 类,处理 XML 布局中定义的自定义属性。
/**
* Created by ZQH on 2017/4/10.
*/
public class MTextView extends android.support.v7.widget.AppCompatTextView {
// 构造函数,接收上下文和属性集
public MTextView(Context context, AttributeSet attrs) {
super(context, attrs);
super(context, attrs);// 调用父类构造函数
}
// 布局方法,处理视图的布局改变
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
super.onLayout(changed, left, top, right, bottom);// 调用父类的方法
}
// 绘制方法,用于在画布上绘制文本
@Override
protected void onDraw(Canvas canvas) {
TextPaint paint = getPaint();
paint.setColor(getTextColors().getDefaultColor());
Layout layout = new StaticLayout(getText(), paint, canvas.getWidth(), Layout.Alignment.ALIGN_NORMAL, getLineSpacingMultiplier(), getLineSpacingExtra(), false);
TextPaint paint = getPaint();// 获取 TextPaint 对象
paint.setColor(getTextColors().getDefaultColor());// 设置文本颜色为默认颜色
// 创建 StaticLayout 用于处理多行文本的绘制
Layout layout = new StaticLayout(getText(),
paint,// 使用的 Paint 对象
canvas.getWidth(),// 输入的宽度
Layout.Alignment.ALIGN_NORMAL,// 文本对齐方式
getLineSpacingMultiplier(),// 行间距倍数
getLineSpacingExtra(),// 行间距额外值
false);// 是否需要添加额外的文本行
// 在画布上绘制文本
layout.draw(canvas);
}
}

@ -1,238 +1,270 @@
//定义类所在的包路径,表示该文件属于com.monke.monkeybook.widget包
package com.monke.monkeybook.widget;
// 导入必要的类
import android.animation.Animator; // 导入 Animator 类,用于实现动画效果的基础类。
import android.animation.ObjectAnimator; // 导入 ObjectAnimator 类,提供对对象属性的动画效果。
import android.annotation.TargetApi; // 导入 TargetApi 注解,用于指示特定的 API 级别。
import android.content.Context; // 导入 Context 类,用于访问应用的环境信息,如资源、数据库等。
import android.content.res.TypedArray; // 导入 TypedArray 类,帮助从 XML 文件中解析自定义属性。
import android.graphics.Rect; // 导入 Rect 类,用于表示矩形区域,通常用于图形绘制和布局。
import android.os.Build; // 导入 Build 类,提供有关当前 Android 版本的静态信息。
import android.os.CountDownTimer; // 导入 CountDownTimer 类,提供一个倒计时的计时器。
import android.support.annotation.Nullable; // 导入 Nullable 注解,表示某个元素可以为 null。
import android.support.v7.widget.LinearLayoutManager; // 导入 LinearLayoutManager用于 RecyclerView 的线性布局管理。
import android.support.v7.widget.RecyclerView; // 导入 RecyclerView 类,提供高效的视图列表。
import android.util.AttributeSet; // 导入 AttributeSet 类,处理 XML 布局中定义的自定义属性。
import android.view.MotionEvent; // 导入 MotionEvent 类,用于处理触摸事件。
import android.view.View; // 导入 View 类Android 中所有视图控件的基类。
import android.view.ViewGroup; // 导入 ViewGroup 类,所有视图组的基类,用于管理子视图。
import android.view.ViewTreeObserver; // 导入 ViewTreeObserver 类,提供对视图树的观察功能
import android.widget.FrameLayout; // 导入 FrameLayout 类,一个可堆叠子视图的布局。
import android.widget.ImageView; // 导入 ImageView 类,显示图片的控件。
import android.widget.LinearLayout; // 导入 LinearLayout 类,垂直或水平排列子视图的布局。
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.os.Build;
import android.os.CountDownTimer;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.monke.monkeybook.R;
import com.monke.monkeybook.utils.DensityUtil;
import com.monke.monkeybook.R; // 导入应用资源类,访问资源(如布局、字符串、颜色等)。
import com.monke.monkeybook.utils.DensityUtil; // 导入 DensityUtil 类,通常用于处理与屏幕密度相关的工具函数。
public class RecyclerViewBar extends LinearLayout {
// 定义滑动动画的时间常量
public static long SLIDE_ANIM_TIME = 800;
// 定义滑块的 ImageView
private ImageView ivSlider;
// 定义滑块的高度,默认为 35dp
private int sliderHeight = DensityUtil.dp2px(getContext(), 35f);
// 定义 RecyclerView
private RecyclerView recyclerView;
// 定义滑块显示和隐藏的动画对象
private Animator slideIn;
private Animator slideOut;
// 构造方法,初始化
public RecyclerViewBar(Context context) {
this(context, null);
}
// 带有属性集的构造方法
public RecyclerViewBar(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
// 带有样式属性的构造方法
public RecyclerViewBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
init(attrs);// 调用初始化方法
}
// 针对 Lollipop 及以上版本的构造方法
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public RecyclerViewBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs);
init(attrs);// 调用初始化方法
}
// 初始化方法
private void init(AttributeSet attrs) {
setOrientation(VERTICAL);
setOrientation(VERTICAL);// 设置垂直布局
// 获取自定义属性
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.RecyclerViewBar);
// 设置滑块高度
sliderHeight = a.getDimensionPixelSize(R.styleable.RecyclerViewBar_slider_height, sliderHeight);
// 设置滑块的左右内边距
int paddingLeft = a.getDimensionPixelSize(R.styleable.RecyclerViewBar_slider_paddingLeft, 0);
int paddingRight = a.getDimensionPixelSize(R.styleable.RecyclerViewBar_slider_paddingRight, 0);
// 创建滑块 ImageView
ivSlider = new ImageView(getContext());
ivSlider.setPadding(paddingLeft, 0, paddingRight, 0);
ivSlider.setAlpha(0f);
ivSlider.setClickable(true);
addView(ivSlider);
ivSlider.setPadding(paddingLeft, 0, paddingRight, 0);// 设置滑块内边距
ivSlider.setAlpha(0f);// 初始透明度为 0
ivSlider.setClickable(true);// 设置为可点击
addView(ivSlider);// 将 ImageView 添加到布局中
// 设置滑块的布局参数
LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, sliderHeight);
ivSlider.setLayoutParams(layoutParams);
ivSlider.setImageResource(R.drawable.icon_slider);
ivSlider.setScaleType(ImageView.ScaleType.FIT_XY);
ivSlider.setLayoutParams(layoutParams);// 应用布局参数
ivSlider.setImageResource(R.drawable.icon_slider);// 设置滑块图标
ivSlider.setScaleType(ImageView.ScaleType.FIT_XY);// 设置图像缩放模式
initIvSlider();
initIvSlider();// 初始化滑块的触摸事件
// 添加全局布局监听
RecyclerViewBar.this.getViewTreeObserver().addOnGlobalLayoutListener(layoutInitListener);
}
// 用于记录触摸的 final Y 坐标
private float finalY = -10000;
// 初始化滑块的触摸事件监听
private void initIvSlider() {
// 设置触摸事件监听器
ivSlider.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
int action = event.getAction();// 获取触摸事件类型
switch (action) {
case MotionEvent.ACTION_DOWN:
finalY = event.getY();
return true;
finalY = event.getY();// 记录初始 Y 坐标
return true;// 消耗事件
case MotionEvent.ACTION_MOVE:
if (finalY >= 0) {
float tempY = event.getY();
float durY = tempY - finalY;
updateSlider(durY);
if (finalY >= 0) {// 检查是否为合法的 Y 坐标
float tempY = event.getY();// 获取当前 Y 坐标
float durY = tempY - finalY;// 计算移动的距离
updateSlider(durY);// 更新滑块位置
showSlide();
showSlide(); // 显示滑块
} else {
finalY = event.getY();
finalY = event.getY();// 更新 finalY
}
return true;
return true;// 消耗事件
case MotionEvent.ACTION_UP:
if (finalY >= 0) {
finalY = -10000;
timeCountDown.cancel();
timeCountDown.start();
return true;
if (finalY >= 0) {// 检查是否为合法的 Y 坐标
finalY = -10000;// 重置 finalY
timeCountDown.cancel();// 取消计时器
timeCountDown.start();// 启动计时器
return true;// 消耗事件
}
break;
default:
if (finalY >= 0) {
finalY = -10000;
return true;
finalY = -10000;// 重置 finalY
return true;// 消耗事件
}
break;
}
return false;
return false;// 未消费事件
}
});
}
// 更新滑块位置
private void updateSlider(float durY) {
LayoutParams l = (LayoutParams) ivSlider.getLayoutParams();
float finalMarginTop = l.topMargin + durY;
LayoutParams l = (LayoutParams) ivSlider.getLayoutParams();// 获取滑块的布局参数
float finalMarginTop = l.topMargin + durY;// 计算新的顶部边距
// 限制滑块的位置
if (finalMarginTop < 0) {
finalMarginTop = 0;
} else if (finalMarginTop > getHeight() - sliderHeight) {
finalMarginTop = getHeight() - sliderHeight;
}
// 如果 RecyclerView 不为空
if (recyclerView != null) {
// 计算 RecyclerView 的滚动位置
int position = Math.round(finalMarginTop / (getHeight() - sliderHeight) * (recyclerView.getAdapter().getItemCount() - 1));
((LinearLayoutManager) recyclerView.getLayoutManager()).scrollToPositionWithOffset(position, 0);
((LinearLayoutManager) recyclerView.getLayoutManager()).scrollToPositionWithOffset(position, 0);// 滚动到指定位置
}
l.topMargin = Math.round(finalMarginTop);
ivSlider.setLayoutParams(l);
l.topMargin = Math.round(finalMarginTop);// 更新滑块的顶部边距
ivSlider.setLayoutParams(l);// 应用新的布局参数
}
// 设置 RecyclerView
public void setRecyclerView(RecyclerView recyclerView) {
this.recyclerView = recyclerView;
this.recyclerView = recyclerView;// 保存 RecyclerView 的引用
if (this.recyclerView != null) {
// 添加滚动监听
this.recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
// 如果滚动状态不为停止,则显示滑块
super.onScrollStateChanged(recyclerView, newState);
if (newState != 0) {
showSlide();
} else {
timeCountDown.cancel();
timeCountDown.start();
timeCountDown.cancel();// 停止计时器
timeCountDown.start();// 启动计时器
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
// 将滑块滚动到当前可见项目的位置
scrollToPositionWithOffset(((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition());
}
});
}
}
// 根据位置滚动并更新滑块位置
public void scrollToPositionWithOffset(int position) {
// 如果 RecyclerView 存在且位置合法
if (recyclerView != null && position < recyclerView.getAdapter().getItemCount()) {
float temp = position * 1.0f / recyclerView.getAdapter().getItemCount();
LayoutParams l = (LayoutParams) ivSlider.getLayoutParams();
l.topMargin = Math.round(((getHeight() - sliderHeight) * temp));
ivSlider.setLayoutParams(l);
LayoutParams l = (LayoutParams) ivSlider.getLayoutParams();// 获取滑块的布局参数
l.topMargin = Math.round(((getHeight() - sliderHeight) * temp));// 计算新的顶部边距
ivSlider.setLayoutParams(l);// 应用新的布局参数
}
}
// 显示滑块
private void showSlide() {
if (ivSlider.getAlpha() < 1) {
if (ivSlider.getAlpha() < 1) {// 如果透明度小于1
if (slideOut != null && slideOut.isRunning()) {
slideOut.cancel();
slideOut.cancel();// 如果滑出动画正在执行,取消它
}
if (slideIn == null) {
slideIn = ObjectAnimator.ofFloat(ivSlider, "alpha", ivSlider.getAlpha(), 1f);
slideIn.setDuration((long) (SLIDE_ANIM_TIME * (1f - ivSlider.getAlpha())));
slideIn = ObjectAnimator.ofFloat(ivSlider, "alpha", ivSlider.getAlpha(), 1f);// 创建滑入动画
slideIn.setDuration((long) (SLIDE_ANIM_TIME * (1f - ivSlider.getAlpha())));// 设置动画持续时间
}
if (!slideIn.isRunning()) {
slideIn.start();
slideIn.start();// 启动滑入动画
}
}
}
// 隐藏滑块
private void hideSlide() {
if (ivSlider.getAlpha() > 0) {
if (ivSlider.getAlpha() > 0) {// 如果透明度大于0
if (slideIn != null && slideIn.isRunning()) {
slideIn.cancel();
slideIn.cancel();// 如果滑入动画正在执行,取消它
}
if (slideOut == null) {
slideOut = ObjectAnimator.ofFloat(ivSlider, "alpha", ivSlider.getAlpha(), 0f);
slideOut.setDuration((long) (SLIDE_ANIM_TIME * ivSlider.getAlpha()));
slideOut = ObjectAnimator.ofFloat(ivSlider, "alpha", ivSlider.getAlpha(), 0f);// 创建滑出动画
slideOut.setDuration((long) (SLIDE_ANIM_TIME * ivSlider.getAlpha()));// 设置动画持续时间
}
if (!slideOut.isRunning()) {
slideOut.start();
slideOut.start();// 启动滑出动画
}
}
}
// 创建计时器,用于滑块的自动隐藏
private TimeCountDown timeCountDown = new TimeCountDown();
// 定义倒计时类
class TimeCountDown extends CountDownTimer {
// 构造方法,设置计时器
public TimeCountDown() {
this(1000, 1000);
this(1000, 1000);// 1000 毫秒后完成,间隔 1000 毫秒
}
public TimeCountDown(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
super(millisInFuture, countDownInterval);// 调用父类构造方法
}
@Override
public void onTick(long millisUntilFinished) {
// 每次计时器滴答时调用(可选实现,暂时为空)
}
@Override
public void onFinish() {
hideSlide();
hideSlide();// 计时结束时隐藏滑块
}
}
// 用于存储视图的高度
private int height = 0;
// 全局布局监听器
private ViewTreeObserver.OnGlobalLayoutListener layoutInitListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if(getHeight()>0){
if(getHeight()>0){// 确保当前高度大于0
if (height == 0) {
height = getHeight();
height = getHeight();// 保存初始高度
} else {
int diff = height - getHeight();
int diff = height - getHeight();// 计算高度差
if (diff != 0) {
LayoutParams l = (LayoutParams) ivSlider.getLayoutParams();
LayoutParams l = (LayoutParams) ivSlider.getLayoutParams();// 获取滑块的布局参数
l.topMargin = (int) ((l.topMargin*1.0f/(height-sliderHeight))*(getHeight()-sliderHeight));
ivSlider.setLayoutParams(l);
height = getHeight();
ivSlider.setLayoutParams(l);// 更新滑块的顶部边距
height = getHeight();// 更新高度
}
}
}

@ -1,40 +1,45 @@
package com.monke.monkeybook.widget.refreshview;
package com.monke.monkeybook.widget.refreshview; // 定义包名
import android.content.Context;
import android.content.res.TypedArray;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.content.Context; // 导入 Context 类,提供应用的上下文
import android.content.res.TypedArray; // 导入 TypedArray 类,用于处理自定义属性
import android.support.v7.widget.LinearLayoutManager; // 导入 LinearLayoutManager 类,用于线性布局管理器
import android.support.v7.widget.RecyclerView; // 导入 RecyclerView 类,支持高效的列表展示
import android.util.AttributeSet; // 导入 AttributeSet 类,用于访问 XML 布局中的自定义属性
import android.view.LayoutInflater; // 导入 LayoutInflater 类,用于将布局文件转换为 View 对象
import android.view.MotionEvent; // 导入 MotionEvent 类,处理触摸事件
import android.view.View; // 导入 View 类,所有 UI 控件的基类
import android.widget.FrameLayout; // 导入 FrameLayout 类,按层次排列子视图
import com.monke.monkeybook.R;
import com.monke.monkeybook.R; // 导入应用资源类,访问资源文件
// 创建 RefreshRecyclerView 类,继承自 FrameLayout
public class RefreshRecyclerView extends FrameLayout {
private View view;
private RefreshProgressBar rpb;
private RecyclerView recyclerView;
private View view; // 根视图
private RefreshProgressBar rpb; // 自定义进度条
private RecyclerView recyclerView; // RecyclerView 控件
private View noDataView;
private View refreshErrorView;
private View noDataView; // 无数据视图
private View refreshErrorView; // 刷新错误视图
// 构造函数
public RefreshRecyclerView(Context context) {
this(context, null);
this(context, null); // 调用带有 AttributeSet 的构造函数
}
public RefreshRecyclerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
this(context, attrs, 0); // 调用带有默认样式的构造函数
}
public RefreshRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 从 XML 布局中加载视图
view = LayoutInflater.from(context).inflate(R.layout.view_refresh_recyclerview, this, false);
// 初始化 UI 组件
rpb = (RefreshProgressBar) view.findViewById(R.id.rpb);
recyclerView = (RecyclerView) view.findViewById(R.id.rv);
// 获取自定义属性并设置给 RefreshProgressBar
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RefreshProgressBar);
rpb.setSpeed(a.getDimensionPixelSize(R.styleable.RefreshProgressBar_speed, rpb.getSpeed()));
rpb.setMaxProgress(a.getInt(R.styleable.RefreshProgressBar_max_progress, rpb.getMaxProgress()));
@ -42,235 +47,253 @@ public class RefreshRecyclerView extends FrameLayout {
rpb.setBgColor(a.getColor(R.styleable.RefreshProgressBar_bg_color, rpb.getBgColor()));
rpb.setSecondColor(a.getColor(R.styleable.RefreshProgressBar_second_color, rpb.getSecondColor()));
rpb.setFontColor(a.getColor(R.styleable.RefreshProgressBar_font_color, rpb.getFontColor()));
a.recycle();
a.recycle(); // 释放 TypedArray
bindEvent();
addView(view);
bindEvent(); // 绑定事件
addView(view); // 将根视图添加到 FrameLayout 中
}
private float durTouchY = -1000000;
private BaseRefreshListener baseRefreshListener;
private float durTouchY = -1000000; // 记录触摸 Y 坐标
private BaseRefreshListener baseRefreshListener; // 刷新监听器
// 设置刷新监听器
public void setBaseRefreshListener(BaseRefreshListener baseRefreshListener) {
this.baseRefreshListener = baseRefreshListener;
}
private OnLoadMoreListener loadMoreListener;
private OnLoadMoreListener loadMoreListener; // 加载更多监听器
// 设置加载更多监听器
public void setLoadMoreListener(OnLoadMoreListener loadMoreListener) {
this.loadMoreListener = loadMoreListener;
}
// 绑定事件
private void bindEvent() {
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
super.onScrollStateChanged(recyclerView, newState); // 调用父类方法
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).canLoadMore() && ((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).getItemCount() - 1 == ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition()) {
if(!((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).getLoadMoreError()){
super.onScrolled(recyclerView, dx, dy); // 调用父类方法
// 判断是否可以加载更多
if (((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).canLoadMore() &&
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).getItemCount() - 1
== ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition()) {
// 如果没有加载错误,开始加载更多
if (!((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).getLoadMoreError()) {
if (null != loadMoreListener) {
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsRequesting(2, false);
loadMoreListener.startLoadmore();
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsRequesting(2, false); // 设置为正在加载更多状态
loadMoreListener.startLoadmore(); // 调用加载更多方法
}
}
}
}
});
recyclerView.setOnTouchListener(refreshTouchListener);
recyclerView.setOnTouchListener(refreshTouchListener); // 设置触摸事件监听器
}
// 获取自定义进度条
public RefreshProgressBar getRpb() {
return rpb;
}
// 获取 RecyclerView
public RecyclerView getRecyclerView() {
return recyclerView;
}
// 处理刷新错误
public void refreshError() {
rpb.setIsAutoLoading(false);
rpb.clean();
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsRequesting(0, true);
rpb.setIsAutoLoading(false); // 设置为非自动加载状态
rpb.clean(); // 清空进度
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsRequesting(0, true); // 设置请求状态为未请求
if (noDataView != null) {
noDataView.setVisibility(GONE);
noDataView.setVisibility(GONE); // 隐藏无数据视图
}
if (refreshErrorView != null) {
refreshErrorView.setVisibility(VISIBLE);
refreshErrorView.setVisibility(VISIBLE); // 显示刷新错误视图
}
}
// 开始刷新
public void startRefresh() {
if (baseRefreshListener != null && baseRefreshListener instanceof OnRefreshWithProgressListener) {
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsAll(false, false);
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsRequesting(1, false);
rpb.setSecondDurProgress(rpb.getSecondMaxProgress());
rpb.setMaxProgress(((OnRefreshWithProgressListener) baseRefreshListener).getMaxProgress());
// 带有进度的刷新
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsAll(false, false); // 设置还有更多数据
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsRequesting(1, false); // 设置为正在刷新状态
rpb.setSecondDurProgress(rpb.getSecondMaxProgress()); // 设置当前进度为最大进度
rpb.setMaxProgress(((OnRefreshWithProgressListener) baseRefreshListener).getMaxProgress()); // 获取最大进度
} else {
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsRequesting(1, true);
rpb.setIsAutoLoading(true);
// 非带进度的刷新
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsRequesting(1, true); // 设置为正在刷新状态
rpb.setIsAutoLoading(true); // 设置为自动加载
if (noDataView != null) {
noDataView.setVisibility(GONE);
noDataView.setVisibility(GONE); // 隐藏无数据视图
}
if (refreshErrorView != null) {
refreshErrorView.setVisibility(GONE);
refreshErrorView.setVisibility(GONE); // 隐藏刷新错误视图
}
}
}
// 完成刷新
public void finishRefresh(Boolean needNoti) {
finishRefresh(((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).getItemcount() == 0, needNoti);
finishRefresh(((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).getItemcount() == 0, needNoti); // 判断是否有数据并完成刷新
}
// 完成刷新,判断是否还有更多数据
public void finishRefresh(Boolean isAll, Boolean needNoti) {
rpb.setDurProgress(0);
rpb.setDurProgress(0); // 重置进度条
if (isAll) {
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsRequesting(0, false);
rpb.setIsAutoLoading(false);
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsAll(isAll, needNoti);
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsRequesting(0, false); // 设置请求状态为未请求
rpb.setIsAutoLoading(false); // 设置为非自动加载状态
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsAll(isAll, needNoti); // 设置没有更多数据
} else {
rpb.setIsAutoLoading(false);
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsRequesting(0, needNoti);
rpb.setIsAutoLoading(false); // 设置为非自动加载状态
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsRequesting(0, needNoti); // 设置请求状态为未请求
}
// 根据数据情况显示无数据视图
if (isAll) {
if (noDataView != null) {
if (((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).getItemcount() == 0)
noDataView.setVisibility(VISIBLE);
noDataView.setVisibility(VISIBLE); // 如果没有数据,则显示无数据视图
else
noDataView.setVisibility(GONE);
noDataView.setVisibility(GONE); // 否则隐藏无数据视图
}
if (refreshErrorView != null) {
refreshErrorView.setVisibility(GONE);
refreshErrorView.setVisibility(GONE); // 隐藏刷新错误视图
}
}
}
// 完成加载更多
public void finishLoadMore(Boolean isAll, Boolean needNoti) {
if (isAll) {
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsRequesting(0, false);
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsAll(isAll, needNoti);
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsRequesting(0, false); // 设置请求状态为未请求
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsAll(isAll, needNoti); // 设置没有更多数据
} else {
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsRequesting(0, needNoti);
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsRequesting(0, needNoti); // 设置请求状态为未请求
}
if (noDataView != null) {
noDataView.setVisibility(GONE);
noDataView.setVisibility(GONE); // 隐藏无数据视图
}
if (refreshErrorView != null) {
refreshErrorView.setVisibility(GONE);
refreshErrorView.setVisibility(GONE); // 隐藏刷新错误视图
}
}
// 设置适配器
public void setRefreshRecyclerViewAdapter(RefreshRecyclerViewAdapter refreshRecyclerViewAdapter, RecyclerView.LayoutManager layoutManager) {
refreshRecyclerViewAdapter.setClickTryAgainListener(new RefreshRecyclerViewAdapter.OnClickTryAgainListener() {
@Override
public void loadMoreErrorTryAgain() {
if (loadMoreListener != null)
loadMoreListener.loadMoreErrorTryAgain();
loadMoreListener.loadMoreErrorTryAgain(); // 处理加载更多错误重试
}
});
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(refreshRecyclerViewAdapter);
recyclerView.setLayoutManager(layoutManager); // 设置布局管理器
recyclerView.setAdapter(refreshRecyclerViewAdapter); // 设置适配器
}
// 加载更多错误
public void loadMoreError() {
rpb.setIsAutoLoading(false);
rpb.clean();
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setLoadMoreError(true, true);
rpb.setIsAutoLoading(false); // 设置为非自动加载状态
rpb.clean(); // 清空进度
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setLoadMoreError(true, true); // 设置加载错误状态
}
// 设置无数据视图和刷新错误视图
public void setNoDataAndrRefreshErrorView(View noData, View refreshError) {
if (noData != null) {
noDataView = noData;
// noDataView.setOnTouchListener(refreshTouchListener);
noDataView.setVisibility(GONE);
addView(noDataView, getChildCount() - 1);
noDataView = noData; // 设置无数据视图
noDataView.setVisibility(GONE); // 隐藏无数据视图
addView(noDataView, getChildCount() - 1); // 将视图添加到 FrameLayout 中
}
if (refreshError != null) {
refreshErrorView = refreshError;
// refreshErrorView.setOnTouchListener(refreshTouchListener);
addView(refreshErrorView, 2);
refreshErrorView.setVisibility(GONE);
refreshErrorView = refreshError; // 设置刷新错误视图
addView(refreshErrorView, 2); // 将视图添加到 FrameLayout 中
refreshErrorView.setVisibility(GONE); // 隐藏刷新错误视图
}
}
private OnTouchListener refreshTouchListener = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
int action = event.getAction(); // 获取触摸事件动作
switch (action) {
case MotionEvent.ACTION_DOWN:
durTouchY = event.getY();
case MotionEvent.ACTION_DOWN: // 触摸开始时
durTouchY = event.getY(); // 记录 Y 坐标
break;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_MOVE: // 触摸移动时
if (durTouchY == -1000000)
durTouchY = event.getY();
float dY = event.getY() - durTouchY; //>0下拉
durTouchY = event.getY();
durTouchY = event.getY(); // 初始化 Y 坐标
float dY = event.getY() - durTouchY; // 计算下拉距离
durTouchY = event.getY(); // 更新 Y 坐标
// 判断是否可以刷新
if (baseRefreshListener != null && ((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).getIsRequesting() == 0 && rpb.getSecondDurProgress() == rpb.getSecondFinalProgress()) {
if (rpb.getVisibility() != View.VISIBLE) {
rpb.setVisibility(View.VISIBLE);
rpb.setVisibility(View.VISIBLE); // 显示进度条
}
if (recyclerView.getAdapter().getItemCount() > 0) {
if (0 == ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstCompletelyVisibleItemPosition()) {
rpb.setSecondDurProgress((int) (rpb.getSecondDurProgress() + dY));
rpb.setSecondDurProgress((int) (rpb.getSecondDurProgress() + dY)); // 更新进度
}
} else {
rpb.setSecondDurProgress((int) (rpb.getSecondDurProgress() + dY));
rpb.setSecondDurProgress((int) (rpb.getSecondDurProgress() + dY)); // 更新进度
}
if (rpb.getSecondDurProgress() <= 0) {
return false;
return false; // 返回 false 继续处理
} else {
return true;
return true; // 返回 true 终止触摸事件继续处理
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_UP: // 触摸抬起时
// 判断是否能开始刷新
if (baseRefreshListener != null && rpb.getSecondMaxProgress() > 0 && rpb.getSecondDurProgress() > 0) {
if (rpb.getSecondDurProgress() >= rpb.getSecondMaxProgress() && ((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).getIsRequesting() == 0) {
if (baseRefreshListener instanceof OnRefreshWithProgressListener) {
//带有进度的
//执行刷新响应
// 带有进度的刷新
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsAll(false, false);
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsRequesting(1, true);
rpb.setMaxProgress(((OnRefreshWithProgressListener) baseRefreshListener).getMaxProgress());
baseRefreshListener.startRefresh();
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsRequesting(1, true); // 设置为正在刷新状态
rpb.setMaxProgress(((OnRefreshWithProgressListener) baseRefreshListener).getMaxProgress()); // 获取最大进度
baseRefreshListener.startRefresh(); // 执行刷新
if (noDataView != null) {
noDataView.setVisibility(GONE);
noDataView.setVisibility(GONE); // 隐藏无数据视图
}
if (refreshErrorView != null) {
refreshErrorView.setVisibility(GONE);
refreshErrorView.setVisibility(GONE); // 隐藏刷新错误视图
}
} else {
//不带进度的
// 不带进度的刷新
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsAll(false, false);
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsRequesting(1, true);
baseRefreshListener.startRefresh();
((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).setIsRequesting(1, true); // 设置为正在刷新状态
baseRefreshListener.startRefresh(); // 执行刷新
if (noDataView != null) {
noDataView.setVisibility(GONE);
noDataView.setVisibility(GONE); // 隐藏无数据视图
}
if (refreshErrorView != null) {
refreshErrorView.setVisibility(GONE);
refreshErrorView.setVisibility(GONE); // 隐藏刷新错误视图
}
rpb.setIsAutoLoading(true);
rpb.setIsAutoLoading(true); // 设置为自动加载状态
}
} else {
if (((RefreshRecyclerViewAdapter) recyclerView.getAdapter()).getIsRequesting() != 1)
rpb.setSecondDurProgressWithAnim(0);
rpb.setSecondDurProgressWithAnim(0); // 动画归零
}
}
durTouchY = -1000000;
durTouchY = -1000000; // 重置 Y 坐标
break;
}
return false;
return false; // 返回 false 继续处理触摸事件
}
};
}

@ -1,177 +1,204 @@
// 定义包名
package com.monke.monkeybook.widget.refreshview;
import android.os.Handler;
import android.os.Looper;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.os.Handler; // 导入 Handler 类,用于处理线程间通信和执行任务
import android.os.Looper; // 导入 Looper 类,用于控制线程的消息循环
import android.support.v7.widget.RecyclerView; // 导入 RecyclerView 类,支持高效的列表展示
import android.util.Log; // 导入 Log 类,用于调试日志
import android.view.LayoutInflater; // 导入 LayoutInflater 类,用于将布局文件转换为 View 对象
import android.view.View; // 导入 View 类,所有 UI 控件的基类
import android.view.ViewGroup; // 导入 ViewGroup 类,视图的容器类
import android.widget.FrameLayout; // 导入 FrameLayout 类,允许堆叠子视图的布局
import android.widget.TextView; // 导入 TextView 类,用于显示文本
import com.monke.monkeybook.R;
import com.monke.monkeybook.R; // 导入应用资源类,访问资源文件
// 创建抽象类 RefreshRecyclerViewAdapter继承自 RecyclerView.Adapter
public abstract class RefreshRecyclerViewAdapter extends RecyclerView.Adapter {
private final int LOADMORETYPE = 2001;
private final int LOADMORETYPE = 2001; // 加载更多的视图类型
private Handler handler;
private int isRequesting = 0; //0是未执行网络请求 1是正在下拉刷新 2是正在加载更多
private Boolean needLoadMore = false;
private Boolean isAll = false; //判断是否还有更多
private Boolean loadMoreError = false;
private Handler handler; // 处理线程间通信的 Handler
private int isRequesting = 0; // 当前请求状态: 0-未请求1-正在下拉刷新2-正在加载更多
private Boolean needLoadMore = false; // 是否需要加载更多
private Boolean isAll = false; // 判断是否还有更多数据
private Boolean loadMoreError = false; // 判断是否加载更多时发生错误
private OnClickTryAgainListener clickTryAgainListener;
private OnClickTryAgainListener clickTryAgainListener; // 加载失败重试监听器接口
// 点击重试的监听器接口
public interface OnClickTryAgainListener {
public void loadMoreErrorTryAgain();
public void loadMoreErrorTryAgain();// 加载失败时的重试方法
}
// 构造函数
public RefreshRecyclerViewAdapter(Boolean needLoadMore) {
this.needLoadMore = needLoadMore;
handler = new Handler();
this.needLoadMore = needLoadMore; // 设置是否需要加载更多
handler = new Handler(); // 初始化 Handler
}
// 获取请求状态
public int getIsRequesting() {
return isRequesting;
return isRequesting; // 返回当前请求状态
}
// 设置请求状态
public void setIsRequesting(int isRequesting, Boolean needNoti) {
this.isRequesting = isRequesting;
if (this.isRequesting == 1) {
isAll = false;
this.isRequesting = isRequesting; // 更新请求状态
if (this.isRequesting == 1) { // 如果正在请求刷新
isAll = false; // 设置为没有更多数据
}
// 根据需要通知数据变化
if (needNoti) {
if (Looper.myLooper() == Looper.getMainLooper()) {
notifyItemRangeChanged(getItemCount(), getItemCount() - getItemcount());
notifyItemRangeChanged(getItemCount(), getItemCount() - getItemcount()); // 通知数据范围变化
} else {
handler.post(new Runnable() {
@Override
public void run() {
notifyDataSetChanged();
notifyDataSetChanged(); // 在主线程中通知数据已更新
}
});
}
}
}
// 创建视图持有者
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == LOADMORETYPE) {
if (viewType == LOADMORETYPE) { // 如果是加载更多类型
return new LoadMoreViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.view_refresh_loadmore, parent, false));
} else
return onCreateViewholder(parent, viewType);
} else {
return onCreateViewholder(parent, viewType); // 否则调用抽象方法创建其他类型的视图持有者
}
}
// 抽象方法,创建其他类型的视图持有者
public abstract RecyclerView.ViewHolder onCreateViewholder(ViewGroup parent, int viewType);
// 绑定视图数据
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
if (holder.getItemViewType() == LOADMORETYPE) {
if (holder.getItemViewType() == LOADMORETYPE) { // 如果是加载更多视图
if (!loadMoreError) {
((LoadMoreViewHolder) holder).tvLoadMore.setText("正在加载...");
((LoadMoreViewHolder) holder).tvLoadMore.setText("正在加载..."); // 显示加载中
} else {
((LoadMoreViewHolder) holder).tvLoadMore.setText("加载失败,点击重试");
((LoadMoreViewHolder) holder).tvLoadMore.setText("加载失败,点击重试"); // 显示加载失败信息
}
// 设置点击事件
((LoadMoreViewHolder) holder).tvLoadMore.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (null != clickTryAgainListener && loadMoreError) {
clickTryAgainListener.loadMoreErrorTryAgain();
loadMoreError = false;
((LoadMoreViewHolder) holder).tvLoadMore.setText("正在加载...");
clickTryAgainListener.loadMoreErrorTryAgain(); // 调用重试方法
loadMoreError = false; // 重置加载错误状态
((LoadMoreViewHolder) holder).tvLoadMore.setText("正在加载..."); // 更新加载状态
}
}
});
} else
onBindViewholder(holder, position);
} else {
onBindViewholder(holder, position); // 绑定其他类型视图的数据
}
}
// 抽象方法,绑定其他类型视图的数据
public abstract void onBindViewholder(RecyclerView.ViewHolder holder, int position);
// 获取视图类型
@Override
public int getItemViewType(int position) {
if (needLoadMore && isRequesting != 1 && !isAll && position == getItemCount() - 1 && getItemcount() > 0) {
return LOADMORETYPE;
return LOADMORETYPE; // 如果满足条件,返回加载更多视图类型
} else {
return getItemViewtype(position);
return getItemViewtype(position); // 否则返回其他视图类型
}
}
// 抽象方法,获取其他类型视图的类型
public abstract int getItemViewtype(int position);
// 获取项计数
@Override
public int getItemCount() {
if (needLoadMore && isRequesting != 1 && !isAll && getItemcount() > 0) {
return getItemcount() + 1;
} else
return getItemcount();
return getItemcount() + 1; // 如果需要加载更多,返回总数 + 1加载更多视图
} else {
return getItemcount(); // 返回正常的项计数
}
}
public abstract int getItemcount();
public abstract int getItemcount(); // 抽象方法,获取正常的项计数
// 设置是否有更多数据
public void setIsAll(Boolean isAll, Boolean needNoti) {
this.isAll = isAll;
this.isAll = isAll; // 更新是否有更多数据
// 根据需要通知数据变化
if (needNoti) {
if (Looper.myLooper() == Looper.getMainLooper()) {
// notifyItemRangeChanged(getItemCount(),getItemCount()-getItemcount());
if (getItemCount() > getItemcount()) {
notifyItemRangeChanged(getItemCount(), getItemCount() - getItemcount());
} else
notifyItemRemoved(getItemCount() + 1);
notifyItemRangeChanged(getItemCount(), getItemCount() - getItemcount()); // 通知数据范围变化
} else {
notifyItemRemoved(getItemCount() + 1); // 通知加载更多视图移除
}
} else {
handler.post(new Runnable() {
@Override
public void run() {
notifyDataSetChanged();
notifyDataSetChanged(); // 在主线程中通知数据已更新
}
});
}
}
}
// 加载更多视图持有者类
class LoadMoreViewHolder extends RecyclerView.ViewHolder {
FrameLayout llLoadMore;
TextView tvLoadMore;
FrameLayout llLoadMore; // 加载更多的容器
TextView tvLoadMore; // 加载更多的文本视图
// 构造函数
public LoadMoreViewHolder(View itemView) {
super(itemView);
llLoadMore = (FrameLayout) itemView.findViewById(R.id.ll_loadmore);
tvLoadMore = (TextView) itemView.findViewById(R.id.tv_loadmore);
llLoadMore = (FrameLayout) itemView.findViewById(R.id.ll_loadmore); // 获取加载更多的容器
tvLoadMore = (TextView) itemView.findViewById(R.id.tv_loadmore); // 获取加载更多文本视图
}
}
// 判断是否可以加载更多
public Boolean canLoadMore() {
return needLoadMore && isRequesting == 0 && !isAll && getItemcount() > 0;
return needLoadMore && isRequesting == 0 && !isAll && getItemcount() > 0; // 返回是否满足加载更多条件
}
// 获取重试监听器
public OnClickTryAgainListener getClickTryAgainListener() {
return clickTryAgainListener;
return clickTryAgainListener; // 返回重试监听器
}
// 设置重试监听器
public void setClickTryAgainListener(OnClickTryAgainListener clickTryAgainListener) {
this.clickTryAgainListener = clickTryAgainListener;
this.clickTryAgainListener = clickTryAgainListener; // 赋值给重试监听器
}
// 获取加载更多错误状态
public Boolean getLoadMoreError() {
return loadMoreError;
return loadMoreError; // 返回加载更多错误状态
}
// 设置加载更多错误
public void setLoadMoreError(Boolean loadMoreError, Boolean needNoti) {
this.isRequesting = 0;
this.loadMoreError = loadMoreError;
this.isRequesting = 0; // 重置请求状态
this.loadMoreError = loadMoreError; // 设置加载错误状态
// 根据需要通知数据变化
if (needNoti) {
if (Looper.myLooper() == Looper.getMainLooper()) {
notifyDataSetChanged();
notifyDataSetChanged(); // 在主线程中通知数据已更新
} else {
handler.post(new Runnable() {
@Override
public void run() {
notifyDataSetChanged();
notifyDataSetChanged(); // 在主线程中通知数据已更新
}
});
}
}
}
}
}

@ -1,101 +1,117 @@
// 定义包名
package com.monke.monkeybook.widget.refreshview;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ScrollView;
import android.annotation.TargetApi; // 导入 TargetApi 注解,标明特定的 API 级别
import android.content.Context; // 导入 Context 类,提供应用的上下文
import android.os.Build; // 导入 Build 类,用于获取当前 Android 版本信息
import android.support.annotation.NonNull; // 导入 NonNull 注解,表示不允许为 null
import android.util.AttributeSet; // 导入 AttributeSet 类,用于访问 XML 布局中的自定义属性
import android.view.MotionEvent; // 导入 MotionEvent 类,处理触摸事件
import android.view.View; // 导入 View 类,所有 UI 控件的基类
import android.widget.ScrollView; // 导入 ScrollView 类,支持滚动的布局
// 创建 RefreshScrollView 类,继承自 ScrollView
public class RefreshScrollView extends ScrollView{
private RefreshProgressBar rpb;
private float durTouchY = -1000000;
private BaseRefreshListener baseRefreshListener;
private Boolean isRefreshing = false;
private OnTouchListener touchListener;
private RefreshProgressBar rpb; // 自定义的刷新进度条
private float durTouchY = -1000000; // 记录触摸Y坐标
private BaseRefreshListener baseRefreshListener; // 刷新监听器实例
private Boolean isRefreshing = false; // 是否正在刷新
private OnTouchListener touchListener; // 触摸事件监听器
// 构造函数
public RefreshScrollView(Context context) {
this(context,null);
this(context,null);// 调用带有 AttributeSet 的构造函数
}
public RefreshScrollView(Context context, AttributeSet attrs) {
this(context, attrs,0);
this(context, attrs,0);// 调用带有风格属性的构造函数
}
public RefreshScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
super(context, attrs, defStyleAttr);// 调用父类构造函数
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@TargetApi(Build.VERSION_CODES.LOLLIPOP)// 指示支持 API 21 及以上
public RefreshScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
super(context, attrs, defStyleAttr, defStyleRes);// 调用父类构造函数
}
// 设置刷新进度条
public void setRpb(@NonNull RefreshProgressBar rpb) {
this.rpb = rpb;
init();
this.rpb = rpb; // 赋值给 rpb
init(); // 初始化
}
// 初始化方法
private void init() {
// 设置触摸事件监听器
touchListener = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
int action = event.getAction(); // 获取当前触摸事件的动作
switch (action) {
case MotionEvent.ACTION_DOWN:
durTouchY = event.getY();
case MotionEvent.ACTION_DOWN: // 触摸开始时
durTouchY = event.getY(); // 记录初始触摸Y坐标
break;
case MotionEvent.ACTION_MOVE:
if (durTouchY == -1000000)
durTouchY = event.getY();
float dY = event.getY() - durTouchY; //>0下拉
durTouchY = event.getY();
if (baseRefreshListener != null && !isRefreshing && rpb.getSecondDurProgress() == rpb.getSecondFinalProgress() && getScrollY()<=0) {
case MotionEvent.ACTION_MOVE: // 触摸移动时
if (durTouchY == -1000000) // 确保初始触摸坐标已记录
durTouchY = event.getY(); // 更新触摸Y坐标
float dY = event.getY() - durTouchY; // 计算移动的Y距离向下则为正值
durTouchY = event.getY(); // 更新初始触摸坐标
// 如果满足刷新条件
if (baseRefreshListener != null && !isRefreshing && rpb.getSecondDurProgress() == rpb.getSecondFinalProgress() && getScrollY() <= 0) {
// 设置刷新进度条可见
if (rpb.getVisibility() != View.VISIBLE) {
rpb.setVisibility(View.VISIBLE);
}
// 更新进度条的当前进度
rpb.setSecondDurProgress((int) (rpb.getSecondDurProgress() + dY));
// 判断当前进度是否小于等于0
if (rpb.getSecondDurProgress() <= 0) {
return false;
return false; // 返回 false 以允许 ScrollView 处理事件
} else {
return true;
return true; // 返回 true 表示事件被处理
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_UP: // 触摸抬起时
// 如果满足刷新条件且当前进度大于0
if (baseRefreshListener != null && rpb.getSecondMaxProgress() > 0 && rpb.getSecondDurProgress() > 0) {
// 如果当前进度达到最大值且不在刷新状态,则开始刷新
if (rpb.getSecondDurProgress() >= rpb.getSecondMaxProgress() && !isRefreshing) {
startRefresh();
} else {
// 否则,将进度条动画归零
rpb.setSecondDurProgressWithAnim(0);
}
}
durTouchY = -1000000;
durTouchY = -1000000; // 重置触摸坐标
break;
}
return false;
return false; // 事件未完全处理,允许 ScrollView 继续处理
}
};
this.setOnTouchListener(touchListener);
this.setOnTouchListener(touchListener); // 设置触摸监听器
}
// 设置刷新监听器
public void setBaseRefreshListener(BaseRefreshListener baseRefreshListener) {
this.baseRefreshListener = baseRefreshListener;
this.baseRefreshListener = baseRefreshListener; // 赋值给监听器
}
public void startRefresh(){
if(baseRefreshListener!=null){
isRefreshing = true;
rpb.setIsAutoLoading(true);
baseRefreshListener.startRefresh();
// 开始刷新
public void startRefresh() {
if (baseRefreshListener != null) {
isRefreshing = true; // 设置为正在刷新状态
rpb.setIsAutoLoading(true); // 设置进度条为自动加载状态
baseRefreshListener.startRefresh(); // 调用监听器的刷新方法
}
}
public void finishRefresh(){
isRefreshing = false;
rpb.setDurProgress(0);
rpb.setIsAutoLoading(false);
// 完成刷新
public void finishRefresh() {
isRefreshing = false; // 设置为未刷新状态
rpb.setDurProgress(0); // 重置进度条当前进度
rpb.setIsAutoLoading(false); // 退出自动加载状态
}
}

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?> <!-- 声明此 XML 文档的版本和编码格式 -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500">
android:duration="500"> <!-- 定义动画的持续时间为500毫秒 -->
<translate
android:fromYDelta="100%"
android:toYDelta="0" />
<!-- 动画开始时Y轴位置为100%(即视图完全在屏幕下方) -->
<!-- 动画结束时Y轴位置为0即视图返回到其原始位置 -->
</set>

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?> <!-- 声明此 XML 文档的版本和编码格式 -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500">
android:duration="500"> <!-- 动画持续时间为500毫秒 -->
<translate
android:fromYDelta="0"
android:toYDelta="100%" />
<!-- 动画开始时视图在 Y 轴的位置为 0其原始位置 -->
<!-- 动画结束时视图在 Y 轴上的位置为其自身高度的 100%(完全下移到视图下方) -->
</set>

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?> <!-- 声明此 XML 文档的版本和编码格式 -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="600">
android:duration="600"> <!-- 定义整个动画的持续时间为600毫秒 -->
<translate
android:fromYDelta="100%"
android:toYDelta="0"/>
android:fromYDelta="100%" <!-- 动画开始时,视图在 Y 轴的起始位置为其高度的 100%(即在屏幕底部之外) -->
android:toYDelta="0"/> <!-- 动画结束时,视图在 Y 轴的目标位置为 0即回到其原始位置 -->
</set>

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?> <!-- 声明此 XML 文档的版本和编码格式 -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<translate android:fromXDelta="-100%"
android:toXDelta="0"/>
android:duration="200"> <!-- 定义整个动画的持续时间为200毫秒 -->
<translate android:fromXDelta="-100%" <!-- X -100% -->
android:toXDelta="0"/> <!-- 动画结束时,视图在 X 轴的目标位置为0即回到其原始位置 -->
</set>

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<translate android:fromXDelta="0"
android:toXDelta="-100%"/>
<?xml version="1.0" encoding="utf-8"?><!-- 声明 XML 文档的版本和编码格式 -->
<set xmlns:android="http://schemas.android.com/apk/res/android"<!-- 使 Android -->
android:duration="200"> <!-- 设置动画的持续时间为 200 毫秒 -->
<translate android:fromXDelta="0" <!-- X 0 -->
android:toXDelta="-100%"/> <!-- 设置动画的结束 X 坐标变化量为 -100%,表示向左移动至视图的左侧 -->
</set>

@ -1,12 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?> <!-- 声明此 XML 文档的版本和编码格式 -->
<!-- 定义动画集合,使用 Android 的命名空间 -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300">
android:duration="300"> <!-- 设置动画的持续时间为 300 毫秒 -->
<!-- 定义缩放动画的起始 X 轴缩放比为 0.6 (60%) -->
<scale android:fromXScale="0.6"
<!-- 定义缩放动画的起始 Y 轴缩放比为 0.6 (60%) -->
android:fromYScale="0.6"
<!-- 定义缩放动画的结束 X 轴缩放比为 1.0 (100%) -->
android:toXScale="1.0"
<!-- 定义缩放动画的结束 Y 轴缩放比为 1.0 (100%) -->
android:toYScale="1.0"
<!-- 设置缩放的 X 轴支点位置为视图宽度的 50% (中心点) -->
android:pivotX="50%"
<!-- 设置缩放的 Y 轴支点位置为视图高度的 50% (中心点) -->
android:pivotY="50%"/>
<!-- 定义透明度动画的起始透明度为 0完全透明 -->
<alpha android:fromAlpha="0"
<!-- 定义透明度动画的结束透明度为 1不透明 -->
android:toAlpha="1"/>
</set>

@ -1,12 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?> <!-- 声明此 XML 文档的版本和编码格式 -->
<!-- 定义动画集合,使用 Android 的命名空间 -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300">
<scale android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="0.6"
android:toYScale="0.6"
android:pivotX="50%"
android:pivotY="50%"/>
<alpha android:fromAlpha="1"
android:toAlpha="0"/>
android:duration="300"> <!-- 设置整个动画的持续时间为 300 毫秒 -->
<scale android:fromXScale="1.0" <!-- X 1.0 (100%) -->
android:fromYScale="1.0" <!-- 定义缩放动画的起始 Y 轴缩放比为 1.0 (100%) -->
android:toXScale="0.6" <!-- 定义缩放动画的结束 X 轴缩放比为 0.6 (60%) -->
android:toYScale="0.6" <!-- 定义缩放动画的结束 Y 轴缩放比为 0.6 (60%) -->
android:pivotX="50%" <!-- 设置缩放的 X 轴支点位置为视图宽度的 50% (中心点) -->
android:pivotY="50%"/> <!-- 设置缩放的 Y 轴支点位置为视图高度的 50% (中心点) -->
<alpha android:fromAlpha="1" <!-- 1 -->
android:toAlpha="0"/> <!-- 定义透明度动画的结束透明度为 0完全透明 -->
</set>

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?> <!-- 声明此 XML 文档的版本和编码格式 -->
<!-- 定义动画集合,使用 Android 的命名空间 -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<translate android:fromXDelta="100%"
android:toXDelta="0"/>
android:duration="200"> <!-- 设置整个动画的持续时间为 200 毫秒 -->
<translate android:fromXDelta="100%" <!-- 100% -->
android:toXDelta="0"/><!-- 定义平移动画的结束位置为 0视图移动到它的原始位置 -->
</set>

@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?> <!-- 声明 XML 版本和编码方式 -->
<!-- 定义 XML 的命名空间指定Android属性 -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
android:duration="200"> <!-- 设置整体动画的持续时间为200毫秒 -->
<translate android:fromXDelta="0"
android:toXDelta="100%"/>
<!-- 动画开始时的水平偏移量从0开始 -->
<!-- 动画结束时的水平偏移量移动到视图宽度的100% -->
</set>

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?> <!-- 声明 XML 版本和编码方式 -->
<!-- 定义 XML 的命名空间 -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
android:duration="200"><!-- 设置整体动画的持续时间为200毫秒 -->
<!--<translate-->
<!--android:fromYDelta="100%"-->
<!--android:toYDelta="0" />-->
@ -9,4 +10,9 @@
android:fromXScale="1"
android:toXScale="1"
android:pivotY="100%"/>
<!-- 动画开始时在Y轴上的缩放比例初始为0不可见 -->
<!-- 动画结束时在Y轴上的缩放比例设置为1正常大小 -->
<!-- X轴上的缩放比例保持为1不缩放 -->
<!-- X轴缩放结束时仍然为1不缩放 -->
<!-- 设置缩放的中心点在Y轴的100%位置(底部) -->
</set>

@ -1,14 +1,18 @@
// 声明当前类的包名
package com.monke.monkeybook;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.Test; // 导入JUnit的@Test注解用于标识测试方法
import static org.junit.Assert.*;// 导入JUnit的断言静态方法用于验证测试结果
/**
* To work on unit tests, switch the Test Artifact in the Build Variants view.
*
*/
public class ExampleUnitTest {
// 标识这是一个JUnit测试方法
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
// 使用assertEquals方法检查两个值是否相等
assertEquals(4, 2 + 2);// 断言2 + 2的结果应该等于4
}
}

@ -1,13 +1,14 @@
// 声明包名,指明该类属于 com.monke.basemvplib 包
package com.monke.basemvplib;
import android.app.Application;
import android.test.ApplicationTestCase;
// 导入需要的类
import android.app.Application;// 导入 Android 的 Application 类,使得可以对应用程序进行测试
import android.test.ApplicationTestCase;// 导入 ApplicationTestCase 类,这是用于测试 Application 的基类
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
public class ApplicationTest extends ApplicationTestCase<Application> {// 定义一个公共类 ApplicationTest继承自 ApplicationTestCase指定测试的类为 Application
public ApplicationTest() {// 定义构造函数
super(Application.class);// 调用父类的构造函数,传入 Application.class告知需要测试的应用类是 Application
}
}

@ -1,5 +1,6 @@
// 定义包名
package com.monke.basemvplib;
// 导入所需的类
import android.app.Activity;
import com.monke.basemvplib.impl.BaseActivity;
@ -12,18 +13,25 @@ import java.util.List;
* Activity,Activity
*/
public class AppActivityManager {
// 使用静态变量存储Activity的弱引用列表避免内存泄漏
private static List<WeakReference<Activity>> activities;
private AppActivityManager(){
activities = new ArrayList<>();
private AppActivityManager(){// 私有构造函数,确保外部无法直接实例化
activities = new ArrayList<>();// 初始化Activity列表
}
// 声明一个volatile变量用于保存AppActivityManager的实例确保线程安全
private static volatile AppActivityManager instance;
/**
* AppActivityManager
* 使线
*/
public static AppActivityManager getInstance(){
// 如果instance为空则进入同步代码块
if(null == instance){
synchronized (AppActivityManager.class){
// 再次检查instance是否为空避免多线程同时创建多个实例
if(null == instance){
instance = new AppActivityManager();
}
@ -32,36 +40,46 @@ public class AppActivityManager {
return instance;
}
/**
* Activity
* @return Activity
*/
public List<WeakReference<Activity>> getActivities() {
return activities;
return activities;// 返回存储Activity弱引用的列表
}
/*
Activity
Activity
@param activity Activity
*/
public void add(Activity activity){
activities.add(new WeakReference<Activity>(activity));
activities.add(new WeakReference<Activity>(activity));// 将Activity的弱引用添加到列表中
}
/*
Activity
@param activity Activity
*/
public void remove(Activity activity){
for(WeakReference<Activity> temp :activities){
if(null != temp.get() && temp.get() == activity){
activities.remove(temp);
break;
for(WeakReference<Activity> temp :activities){// 遍历活动列表
if(null != temp.get() && temp.get() == activity){// 如果找到了对应的Activity
activities.remove(temp);// 从列表中移除
break;// 结束循环
}
}
}
/*
Activity
@param activityClass Activity
*/
public void remove(Class<?> activityClass){
for(Iterator<WeakReference<Activity>> iterator = activities.iterator();iterator.hasNext();){
// 获取下一个活动的弱引用
WeakReference<Activity> item = iterator.next();
// 判断是否匹配
if(null != item && null != item.get() && item.get().getClass() == activityClass){
// 移除匹配的Activity
iterator.remove();
}
}
@ -69,47 +87,53 @@ public class AppActivityManager {
/*
activity
@param activities Activity
*/
public void finishActivity(BaseActivity... activities){
for(int i=0;i<activities.length;i++){
if(null != activities[i]){
activities[i].finish();
for(int i=0;i<activities.length;i++){// 遍历传入的Activity数组
if(null != activities[i]){// 如果Activity不为null
activities[i].finish();// 关闭Activity
}
}
}
/*
activity(class)
@param activityClasses Activity
*/
public void finishActivity(Class<?>... activityClasses){
// 存储待关闭的Activity
ArrayList<WeakReference<Activity>> waitfinish = new ArrayList<>();
for(WeakReference<Activity> temp :activities){
for(int i=0;i<activityClasses.length;i++){
if(null != temp.get() && temp.get().getClass() == activityClasses[i]){
waitfinish.add(temp);
break;
for(WeakReference<Activity> temp :activities){// 遍历当前管理的Activity
for(int i=0;i<activityClasses.length;i++){// 遍历需要关闭的Activity类数组
if(null != temp.get() && temp.get().getClass() == activityClasses[i]){// 判断是否匹配
waitfinish.add(temp);// 添加到待关闭列表
break;// 结束内层循环
}
}
}
// 遍历待关闭的Activity
for(WeakReference<Activity> activityWeakReference:waitfinish){
if(null != activityWeakReference.get()){
activityWeakReference.get().finish();
if(null != activityWeakReference.get()){// 如果弱引用有效
activityWeakReference.get().finish();// 关闭Activity
}
}
}
/*
Activity
param activityClass Activity
@return
*/
public Boolean isExist(Class<?> activityClass){
Boolean result = false;
for(Iterator<WeakReference<Activity>> iterator = activities.iterator();iterator.hasNext();){
WeakReference<Activity> item = iterator.next();
if(null != item && null != item.get() && item.get().getClass() == activityClass){
result = true;
break;
Boolean result = false;// 默认不存在
for(Iterator<WeakReference<Activity>> iterator = activities.iterator();iterator.hasNext();){// 遍历活动列表
WeakReference<Activity> item = iterator.next();// 获取下一个活动的弱引用
if(null != item && null != item.get() && item.get().getClass() == activityClass){// 如果找到目标Activity
result = true;// 标记为存在
break;// 结束循环
}
}
return result;
return result;// 返回是否存在的结果
}
}

@ -1,10 +1,12 @@
// 定义包名
package com.monke.basemvplib;
// 导入所需的类
import android.app.Application;// 导入Android框架中的Application类
import android.app.Application;
public class BaseApplication extends Application{
public class BaseApplication extends Application{//定义一个扩展自Application的基类
@Override
public void onCreate() {
// 调用父类的方法
super.onCreate();
}
}

@ -1,43 +1,52 @@
// 定义包名
package com.monke.basemvplib;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import okhttp3.ResponseBody;
import okio.BufferedSource;
import okio.Okio;
import retrofit2.Converter;
import retrofit2.Retrofit;
// 导入所需的类
import java.io.IOException;// 导入 IOException 类,处理输入输出异常
import java.lang.annotation.Annotation;// 导入 Annotation 类的定义,使用 Java 反射时需要这个类
import java.lang.reflect.Type;// 导入 Type 接口,表示 Java 类型的反射信息
import java.nio.charset.Charset;// 导入 Charset 类,处理字符集
import okhttp3.ResponseBody;// 导入 OkHttp 库中的 ResponseBody 类,用于处理网络响应体
import okio.BufferedSource;// 导入 Okio 库中的 BufferedSource 类,用于高效处理流数据
import okio.Okio;// 导入 Okio 库中的 Okio 类,提供工具方法
import retrofit2.Converter;// 导入 Retrofit 库中的 Converter 类,为响应转换器提供基类
import retrofit2.Retrofit;// 导入 Retrofit 库中的 Retrofit 类,表示 Retrofit 的核心类
/**
* Retrofit ResponseBody
*/
public class EncodoConverter extends Converter.Factory {
// 默认的编码方式
private String encode = "utf-8";
private EncodoConverter(){
private EncodoConverter(){// 私有构造函数,禁止无参实例化
}
private EncodoConverter(String encode){
private EncodoConverter(String encode){// 带编码方式的构造函数
this.encode = encode;
}
public static EncodoConverter create(){
public static EncodoConverter create(){// 静态方法,创建默认编码的 EncodoConverter 实例
return new EncodoConverter();
}
public static EncodoConverter create(String en){
public static EncodoConverter create(String en){// 静态方法,创建指定编码的 EncodoConverter 实例
return new EncodoConverter(en);
}
// 重写 responseBodyConverter 方法,返回自定义的转换器
@Override
public Converter<ResponseBody, String> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
// 返回一个新的字符串转换器
return new Converter<ResponseBody, String>() {
// 实现转换逻辑
@Override
public String convert(ResponseBody value) throws IOException {
// 使用 Okio 读取 ResponseBody 数据
BufferedSource bufferedSource = Okio.buffer(value.source());
// 以指定编码读取字符串
String responseData = bufferedSource.readString(Charset.forName(encode));
return responseData;
return responseData;// 返回读取的字符串
}
};
}

@ -1,7 +1,11 @@
// 定义包名
package com.monke.basemvplib;
// 导入支持库中的 NonNull 注解,用于标记非空参数
import android.support.annotation.NonNull;
/**
* IPresenter Presenter
*/
public interface IPresenter {
/**
* View使View

@ -1,7 +1,16 @@
// 定义包名
package com.monke.basemvplib;
// 导入 Context 类,这是 Android 中的一个基础类,表示应用程序的环境信息
import android.content.Context;
/**
* IView View
*/
public interface IView {
/**
* Context
* @return View
*/
public Context getContext();
}

@ -1,79 +1,96 @@
// 定义包名
package com.monke.basemvplib.impl;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.view.View;
import com.monke.basemvplib.AppActivityManager;
import com.monke.basemvplib.IPresenter;
import com.monke.basemvplib.IView;
// 导入需要的类和接口
import android.app.ActivityOptions;// 导入android.app.ActivityOptions类用于在Android 5.0Lollipop及以后的版本中创建Activity之间的转场动画。
import android.content.Context;// 导入android.content.Context类这是一个抽象类提供了一个接口来访问应用程序的全局信息。
import android.content.Intent;// 导入android.content.Intent类它是一种携带请求的消息对象用于在组件之间传递指令
import android.os.Build;// 导入android.os.Build类提供了一个接口来访问关于Android构建版本的信息。
import android.os.Bundle;// 导入android.os.Bundle类它是一个存储键值对的数据结构通常用于在Activity、Fragment等组件间传递数据。
import android.support.annotation.NonNull;// 导入android.support.annotation.NonNull注解用于标记方法参数或返回值不允许为null。
import android.view.View;// 导入android.view.View类它是所有UI组件的基类代表屏幕上的一个矩形区域。
import com.monke.basemvplib.AppActivityManager;// 导入com.monke.basemvplib.AppActivityManager类这是一个管理所有Activity的类可能用于管理Activity的生命周期等。
import com.monke.basemvplib.IPresenter;// 导入com.monke.basemvplib.IPresenter接口它定义了MVP模式中的Presenter层的行为。
import com.monke.basemvplib.IView;// 导入com.monke.basemvplib.IView接口它定义了MVP模式中的View层的行为。
// 导入com.trello.rxlifecycle2.components.support.RxAppCompatActivity类这是一个支持RxJava生命周期处理的Activity基类。
import com.trello.rxlifecycle2.components.support.RxAppCompatActivity;
// 定义 BaseActivity 类,它是一个抽象类,用于继承 RxAppCompatActivity 并实现 IView 接口
public abstract class BaseActivity<T extends IPresenter> extends RxAppCompatActivity implements IView {
// 定义一个常量字符串用于在Intent中传递是否需要启动共享元素动画的标志
public final static String start_share_ele= "start_with_share_ele";
protected Bundle savedInstanceState;
protected T mPresenter;
private Boolean startShareAnim = false;
protected Bundle savedInstanceState;// 定义一个Bundle类型的变量用于存储保存的实例状态
protected T mPresenter;// 定义一个泛型变量mPresenter用于存储与当前Activity相关联的Presenter
private Boolean startShareAnim = false;// 定义一个布尔变量,用于标记是否启动共享元素动画
@Override
// onCreate 方法是 Activity 生命周期中的第一个回调方法,用于初始化 Activity
protected void onCreate(Bundle savedInstanceState) {
// 调用超类的 onCreate 方法
super.onCreate(savedInstanceState);
// 将传入的 savedInstanceState 赋值给成员变量
this.savedInstanceState = savedInstanceState;
// 如果 getIntent() 不为空,则从 Intent 中获取是否需要启动共享元素动画的标志
if(getIntent()!=null){
startShareAnim = getIntent().getBooleanExtra(start_share_ele,false);
}
// 将当前 Activity 添加到 AppActivityManager 的管理列表中
AppActivityManager.getInstance().add(this);
// 初始化 SDK
initSDK();
// 调用抽象方法 onCreateActivity用于设置布局内容
onCreateActivity();
// 调用抽象方法 initInjector用于初始化 Presenter
mPresenter = initInjector();
// 将 View 绑定到 Presenter
attachView();
initData();
bindView();
bindEvent();
firstRequest();
initData();// 调用抽象方法 initData用于初始化数据
bindView();// 调用方法 bindView用于绑定视图
bindEvent();// 调用方法 bindEvent用于绑定事件
firstRequest();// 调用方法 firstRequest用于执行首次逻辑操作
}
/**
*
*/
protected void firstRequest() {
protected void firstRequest() { // 首次逻辑操作的方法,子类需要根据需要实现具体的逻辑
}
/**
*
*/
protected void bindEvent() {
protected void bindEvent() {// 事件触发绑定的方法,子类需要根据需要实现具体的事件绑定逻辑
}
/**
*
*/
protected void bindView() {
protected void bindView() {// 控件绑定的方法,子类需要根据需要实现具体的控件绑定逻辑
}
/**
* PV
*/
private void attachView() {
private void attachView() {// P层绑定V层的方法将 Presenter 和 View 绑定在一起
// 如果 mPresenter 不为空,则调用其 attachView 方法,将当前 View 绑定到 Presenter
if (null != mPresenter) {
mPresenter.attachView(this);
}
}
// onPause 方法是 Activity 生命周期中的回调方法,当 Activity 不可见时调用
@Override
protected void onPause() {
// 调用超类的 onPause 方法
super.onPause();
}
/**
* PV
*/
private void detachView() {
private void detachView() {// P层解绑V层的方法将 Presenter 和 View 解绑
// 如果 mPresenter 不为空,则调用其 detachView 方法,将当前 View 从 Presenter 解绑
if (null != mPresenter) {
mPresenter.detachView();
}
@ -82,7 +99,7 @@ public abstract class BaseActivity<T extends IPresenter> extends RxAppCompatActi
/**
* SDK
*/
protected void initSDK() {
protected void initSDK() {// SDK初始化的方法子类需要根据需要实现具体的 SDK 初始化逻辑
}
@ -91,64 +108,73 @@ public abstract class BaseActivity<T extends IPresenter> extends RxAppCompatActi
*
* @return
*/
protected abstract T initInjector();
protected abstract T initInjector();// P层绑定的方法子类需要根据需要实现具体的 Presenter 初始化逻辑,并返回对应的 Presenter 实例
/**
* setContentView()
*/
protected abstract void onCreateActivity();
protected abstract void onCreateActivity(); // 布局载入的方法,子类需要根据需要实现具体的布局载入逻辑
/**
*
*/
protected abstract void initData();
protected abstract void initData();// 数据初始化的方法,子类需要根据需要实现具体的数据初始化逻辑
@Override
protected void onResume() {
super.onResume();
protected void onResume() {// onResume 方法是 Activity 生命周期中的回调方法,当 Activity 可见时调用
super.onResume();// 调用超类的 onResume 方法
}
@Override
protected void onDestroy() {
super.onDestroy();
detachView();
AppActivityManager.getInstance().remove(this);
protected void onDestroy() {// onDestroy 方法是 Activity 生命周期中的回调方法,当 Activity 销毁时调用
super.onDestroy();// 调用超类的 onDestroy 方法
detachView();// 调用 detachView 方法,将 View 从 Presenter 解绑
AppActivityManager.getInstance().remove(this);// 将当前 Activity 从 AppActivityManager 的管理列表中移除
}
////////////////////////////////启动Activity转场动画/////////////////////////////////////////////
protected void startActivityForResultByAnim(Intent intent, int requestCode, int animIn, int animExit) {
startActivityForResult(intent, requestCode);
overridePendingTransition(animIn, animExit);
protected void startActivityForResultByAnim(Intent intent, int requestCode, int animIn, int animExit) {// 通过动画启动一个新的 Activity并指定进入和退出的动画资源 ID
startActivityForResult(intent, requestCode);// 调用 startActivityForResult 方法启动新的 Activity并指定请求码
overridePendingTransition(animIn, animExit);// 调用 overridePendingTransition 方法设置进入和退出的动画
}
protected void startActivityByAnim(Intent intent, int animIn, int animExit) {
startActivity(intent);
overridePendingTransition(animIn, animExit);
protected void startActivityByAnim(Intent intent, int animIn, int animExit) {// 通过动画启动一个新的 Activity并指定进入和退出的动画资源 ID
startActivity(intent);// 调用 startActivity 方法启动新的 Activity
overridePendingTransition(animIn, animExit);// 调用 overridePendingTransition 方法设置进入和退出的动画
}
// 通过共享元素动画启动一个新的 Activity并指定进入和退出的动画资源 ID
protected void startActivityForResultByAnim(Intent intent, int requestCode, @NonNull View view, @NonNull String transitionName, int animIn, int animExit) {
// 如果当前系统版本大于等于 LOLLIPOP则使用共享元素动画
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// 调用 startActivityForResult 方法启动新的 Activity并指定请求码和共享元素动画的参数
startActivityForResult(intent, requestCode, ActivityOptions.makeSceneTransitionAnimation(this, view, transitionName).toBundle());
} else {
// 如果当前系统版本小于 LOLLIPOP则使用普通动画启动 Activity
startActivityForResultByAnim(intent, requestCode, animIn, animExit);
}
}
// 通过共享元素动画启动一个新的 Activity并指定进入和退出的动画资源 ID
protected void startActivityByAnim(Intent intent, @NonNull View view, @NonNull String transitionName, int animIn, int animExit) {
// 如果当前系统版本大于等于 LOLLIPOP则使用共享元素动画
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// 将是否需要启动共享元素动画的标志放入 Intent
intent.putExtra(start_share_ele,true);
// 调用 startActivity 方法启动新的 Activity并指定共享元素动画的参数
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this, view, transitionName).toBundle());
} else {
// 如果当前系统版本小于 LOLLIPOP则使用普通动画启动 Activity
startActivityByAnim(intent, animIn, animExit);
}
}
public Context getContext(){
public Context getContext(){// 获取当前 Context 对象的方法
return this;
}
public Boolean getStart_share_ele() {
public Boolean getStart_share_ele() {// 获取是否需要启动共享元素动画的标志的方法
return startShareAnim;
}
}

@ -1,67 +1,82 @@
// 定义包名
package com.monke.basemvplib.impl;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.monke.basemvplib.IPresenter;
import com.monke.basemvplib.IView;
// 导入需要的类和接口
import android.os.Bundle;// 导入android.os.Bundle类它是一个存储键值对的数据结构通常用于在Activity、Fragment等组件间传递数据。
import android.support.annotation.Nullable;// 导入android.support.annotation.Nullable注解用于标记方法参数、返回值或字段可以为null。
import android.view.LayoutInflater;// 导入android.view.LayoutInflater类它用于在Activity或Fragment中实例化视图。
import android.view.View;// 导入android.view.View类它是所有UI组件的基类代表屏幕上的一个矩形区域。
import android.view.ViewGroup;// 导入android.view.ViewGroup类它是一个视图的容器可以包含其他的View或ViewGroup。
import com.monke.basemvplib.IPresenter;// 导入com.monke.basemvplib.IPresenter接口它定义了MVP模式中的Presenter层的行为。
import com.monke.basemvplib.IView;// 导入com.monke.basemvplib.IView接口它定义了MVP模式中的View层的行为。
// 导入com.trello.rxlifecycle2.components.RxFragment类这是一个支持RxJava生命周期处理的Fragment基类。
import com.trello.rxlifecycle2.components.RxFragment;
// 定义 BaseFragment 类,它是一个抽象类,用于继承 RxFragment 并实现 IView 接口
public abstract class BaseFragment<T extends IPresenter> extends RxFragment implements IView{
// 定义一个 View 类型的变量,用于存储 Fragment 的视图
protected View view;
// 定义一个 Bundle 类型的变量,用于存储保存的实例状态
protected Bundle savedInstanceState;
// onCreateView 方法是 Fragment 生命周期中的第一个回调方法,用于初始化 Fragment
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// 将传入的 savedInstanceState 赋值给成员变量
this.savedInstanceState = savedInstanceState;
// 初始化第三方 SDK
initSDK();
// 调用抽象方法 createView用于创建视图
view = createView(inflater, container);
// 调用方法 initData用于初始化数据
initData();
// 调用方法 bindView用于绑定视图
bindView();
// 调用方法 bindEvent用于绑定事件
bindEvent();
// 调用方法 firstRequest用于执行首次逻辑操作
firstRequest();
// 返回创建的视图
return view;
}
/**
*
*/
protected void bindEvent() {
protected void bindEvent() {// 事件触发绑定的方法,子类需要根据需要实现具体的事件绑定逻辑
}
/**
*
*/
protected void bindView() {
protected void bindView() {// 控件绑定的方法,子类需要根据需要实现具体的控件绑定逻辑
}
/**
*
*/
protected void initData() {
protected void initData() {// 数据初始化的方法,子类需要根据需要实现具体的数据初始化逻辑
}
/**
*
*/
protected void firstRequest() {
protected void firstRequest() { // 首次逻辑操作的方法,子类需要根据需要实现具体的首次逻辑操作
}
/**
*
*/
// 加载布局的抽象方法,子类需要根据需要实现具体的布局加载逻辑
protected abstract View createView(LayoutInflater inflater, ViewGroup container);
/**
* SDK
*/
protected void initSDK() {
protected void initSDK() {// 第三方SDK初始化的方法子类需要根据需要实现具体的第三方 SDK 初始化逻辑
}
}

@ -1,5 +1,6 @@
// 定义包名
package com.monke.basemvplib.impl;
// 导入所需的类
import com.monke.basemvplib.EncodoConverter;
import java.util.concurrent.TimeUnit;
@ -9,31 +10,33 @@ import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;
public class BaseModelImpl {
public class BaseModelImpl {// 定义 BaseModelImpl 类
// 定义一个 OkHttpClient.Builder 类型的保护变量,用于构建 OkHttpClient 实例
protected OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.addNetworkInterceptor(new RetryIntercepter(1));
.connectTimeout(10, TimeUnit.SECONDS)// 设置连接超时时间为10秒
.writeTimeout(10, TimeUnit.SECONDS)// 设置写超时时间为10秒
.readTimeout(10, TimeUnit.SECONDS)// 设置读超时时间为10秒
.addNetworkInterceptor(new RetryIntercepter(1));// 添加一个网络拦截器,用于重试机制
protected Retrofit getRetrofitObject(String url) {
// 创建 Retrofit 实例,设置 baseUrl、转换工厂、调用适配器和客户端
return new Retrofit.Builder().baseUrl(url)
//增加返回值为字符串的支持(以实体类返回)
.addConverterFactory(ScalarsConverterFactory.create())
//增加返回值为Oservable<T>的支持
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(clientBuilder.build())
.build();
.client(clientBuilder.build())// 设置客户端
.build();// 构建 Retrofit 实例
}
protected Retrofit getRetrofitString(String url, String encode) {
// 创建 Retrofit 实例,设置 baseUrl、转换工厂、调用适配器和客户端
return new Retrofit.Builder().baseUrl(url)
//增加返回值为字符串的支持(以实体类返回)
//增加返回值为字符串的支持(以实体类返回),使用自定义的编码转换器
.addConverterFactory(EncodoConverter.create(encode))
//增加返回值为Oservable<T>的支持
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(clientBuilder.build())
.build();
.client(clientBuilder.build()) // 设置客户端
.build();// 构建 Retrofit 实例
}
}

@ -1,14 +1,17 @@
// 定义包名
package com.monke.basemvplib.impl;
// 导入所需的类和接口
import android.support.annotation.NonNull;// 导入android.support.annotation.NonNull注解用于标记方法参数或返回值不允许为null。
import com.monke.basemvplib.IPresenter;// 导入com.monke.basemvplib.IPresenter接口它定义了MVP模式中的Presenter层的行为和职责。
import com.monke.basemvplib.IView;// 导入com.monke.basemvplib.IView接口它定义了MVP模式中的View层的行为和职责。
import android.support.annotation.NonNull;
import com.monke.basemvplib.IPresenter;
import com.monke.basemvplib.IView;
public abstract class BasePresenterImpl<T extends IView> implements IPresenter{
// 定义 BasePresenterImpl 类,它是一个抽象类,用于实现 IPresenter 接口
public abstract class BasePresenterImpl<T extends IView> implements IPresenter{// 定义一个泛型变量 mView它是 IView 接口的子类,用于持有 View 层的引用
protected T mView;
@Override
public void attachView(@NonNull IView iView) {
// 将传入的 IView 类型参数强制转换为泛型 T 类型,并赋值给 mView
mView = (T) iView;
}
}

@ -1,27 +1,35 @@
// 定义包名
package com.monke.basemvplib.impl;
// 导入所需的类
import java.io.IOException;// 导入java.io.IOException类它是所有I/O错误的基类用于在网络请求过程中捕获和处理I/O异常。
import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Interceptor;// 导入okhttp3.Interceptor类它是OkHttp库中用于拦截和处理网络请求和响应的接口。
import okhttp3.Request;// 导入okhttp3.Request类它表示一个HTTP请求包括请求方法、URL、头部和正文。
import okhttp3.Response;// 导入okhttp3.Response类它表示一个HTTP响应包括状态码、头部、正文和一个可以关闭连接的方法。
// 定义 RetryIntercepter 类,实现 Interceptor 接口
public class RetryIntercepter implements Interceptor {
public int maxRetry;//最大重试次数
private int retryNum = 0;//假如设置为3次重试的话则最大可能请求4次默认1次+3次重试
public RetryIntercepter(int maxRetry) {
public RetryIntercepter(int maxRetry) {// 构造函数,用于初始化最大重试次数
this.maxRetry = maxRetry;
}
@Override
public Response intercept(Chain chain) throws IOException {
// 获取请求对象
Request request = chain.request();
// 执行请求并获取响应
Response response = chain.proceed(request);
// 当响应不成功且重试次数小于最大重试次数时,进行重试
while (!response.isSuccessful() && retryNum < maxRetry) {
// 重试次数加1
retryNum++;
// 再次执行请求
response = chain.proceed(request);
}
// 返回最终的响应对象
return response;
}
}

@ -1,15 +1,18 @@
//导入包名
package com.monke.basemvplib;
import org.junit.Test;
import org.junit.Test;// 导入JUnit的@Test注解用于标记测试方法
import static org.junit.Assert.*;
import static org.junit.Assert.*;// 导入JUnit的断言静态方法方便在测试中使用各种断言
/**
* To work on unit tests, switch the Test Artifact in the Build Variants view.
*
*/
public class ExampleUnitTest {
@Test
@Test // 使用 @Test 注解标识这个方法是一个测试方法
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
// 使用assertEquals方法检查两个值是否相等
assertEquals(4, 2 + 2);// 断言2 + 2的结果应该等于4
}
}
Loading…
Cancel
Save