效果展示
前言
最近做了个看小说软件。
因为我老婆喜欢看小说,而免费的小说总在奇奇怪怪的网站,看起来广告多体验差。
她是苹果手机,基本找不到那种可以换源的小说软件。
最后决定利用flutter自己做一个,IOS打包给他直连电脑运行release包就行了。
规划
计划要做一个很全能的app。
- 可以支持自定义书源
- 可以支持看小说
- 可以支持看资讯
- 可以支持看漫画
- 可以支持Android IOS linux windows macos
- 如果可能的话,甚至还想实现看视频
名字就叫做信息阅读
开发技术直接使用Flutter,完美跨平台,轻松完成安卓,苹果和桌面端的app产出。
书源
要想实现自定义书源。就需要一套通用的规则。市面上自定义书源规则做的最好的小说软件自然是阅读。
所以我用flutter实现了90%兼容阅读书源规则的方案。
直接把阅读APP的书源拷贝导入信息阅读APP就基本上能使用了。
并且预置了一些常用书源可以直接导入。
因为dart没有jsoup这样可以功能强大的css selector 选择器。所以我使用flutter_html 加 正则表达式进行规则解析。
具体代码在RuleUtil.ElementExt中。
///[rule] 匹配规则
///当前支持
/// - && 连接多个不同的匹配结果
/// - @ 分割css 选择器匹配
/// - ## 后面跟随正则表达式
/// 比如ol.am-breadcrumb@li:nth-of-type(2)@text
///[matchReplace] 是否处理图片url等需要替换的值
///比如 dt@a@href##.*/(\\d+)/(\\d+)/##https://style.31xs.net/img/$1/$2/$2s.jpg
///从 https://m.31xs.com/201/201996/ url中提取出 两个数字关键词 201, 201996
///将其作为$1和$2替换到结果中
///
String? parseRule(String? rule, [AttrBean? attrBean = null]) {
if (rule?.isNotEmpty != true) return null;
if (attrBean != null) {
rule = attrBean.dealRes(rule);
}
///正则 处理 ##分割
var regexSpan = rule!.split('##');
String? regex;
String? replace;
if (regexSpan.length > 1) {
regex = regexSpan[1];
}
if (regexSpan.length > 2) {
replace = regexSpan[2];
}
String? resultStr = _selectElement(regexSpan[0], regex, replace, rule.endsWith("###"));
return resultStr;
}
资讯
对于资讯类别的源,展示的很简单。只是列表和详情页的展示。
常用资讯源中内置了《福利吧》源。
待办
[ ] 新增关键内容提取规则(针对需要提取一些链接内容的情况)
小说
小说阅读器的实现,最难点的地方在于段落,文字的计算。将一章节的内容均匀分布到每一页,要计算的恰到好处,才有良好的阅读体验。
这一块参考了lwlizhe大佬的开源阅读器flutter_novel。
自己取了其中的段落字数计算代码,实现了横向翻页能力。效果如下。
文字绘制主要分这么三步。
- 根据文字大小,段落间距,展示区域大小,计算出章节中每一页的所有文字。
- 绘制当前页面的文字到画布中(
CustomPainter)。滑动的时候会绘制两页 - 翻页完成之后根据需要预先加载下一章节的内容,以及计算分页。
关键的计算代码如下:
///算出给定区域大小内,能完全绘制出来的最后文字偏移量
int endOffset = textPainter.getPositionForOffset(Offset(width, limitHeight - currentHeight)).offset;
通过算出来的偏移量就知道文字需不需要多页绘制,和每一页能绘制到哪个文字。
漫画
当前漫画的实现比较简单,只是列表的展示。
漫画往前翻页没有实现,往后翻页勉强可用。还没有想到好的实现方案。想实现bilibili漫画那样的效果这一块技术难度还挺大的,需要我好好学习才行。
最后
通过开发一个软件来学习一些知识,不失为可取的方式。
成功自己还能使用,何乐而不为