空手撸一个Android阅读器,无第三方支持

开篇

几个月前,晚上梦到在研究翻页动画,次日上班居然想起了昨天晚上的梦。search了相关信息,大概要用到数学计算、canvas绘图等相关内容。对我来说手动绘图这方面的实践还是0,想想短时间内应该搞不定,便将此事记在了待做清单内。(公司电脑一个单子,自己Mac一个单子,增加行数的速度远比删除的速度快。)

十一长假前夕手头没什么事情,看着清单顶部的第一项(时间排序),便照着例文开始绘图。画到后面,想想就一张图片多无聊。算了!加点功能做个带翻页动画的阅读器吧!

《空手撸一个Android阅读器,无第三方支持》 preview1
《空手撸一个Android阅读器,无第三方支持》 preview2
《空手撸一个Android阅读器,无第三方支持》 preview3

项目地址:GitHub
觉得好的话star一下

文章顺序:

  • 翻页
  • 内容
  • 工具栏

翻页

主要是按照
Android自定义View——从零开始实现书籍翻页效果
的逻辑,自己手敲了一遍完成的翻页动画,并对代码进行了调整。
期间也学习了
各个击破搞明白PorterDuff.Mode
一起看画布Android Canvas
对代码理解和调整起到了很大帮助。

这里就不赘述以上文章的内容,直接说明调整后的代码逻辑。

《空手撸一个Android阅读器,无第三方支持》

自定义控件继承自View,通过重写

  • onMeasure 来获取控件的宽高,取得纸张各点的初始值
  • onDraw 用来绘制纸张、阴影、内容
  • onTouchEvent 获取用户的手势事件,重计算各点并绘制,达到翻页的动效
  • computeScroll 用到了Scroller,来执行手势事件后的翻页、恢复效果

初始化

《空手撸一个Android阅读器,无第三方支持》

因为视图是通过Canvas绘制出来的,因此控件初始化时要将

  • Point 书页运动的关键点,用于完成翻页动画
  • Paint 内容画笔
  • Path A、B、C区域的路径,用于翻页时路径的裁剪
  • Gradient 阴影

初始化,并打开setClickable(true),用以接收手势事件

测量

《空手撸一个Android阅读器,无第三方支持》

按View的生命周期,到了onMeasure

设置BookPageView的宽高后,便有了关键点的初始值。通过宽高值,初始化A、B两个不同区域的Bitmap。

绘制

《空手撸一个Android阅读器,无第三方支持》

具备了Point、Paint、Path、Gradient、Bitmap,便可以开始绘制Canvas。

将准备好的纸图片,绘制到Canvas当全局背景,用以填充裁剪所漏出的区域。

具体的绘制过程需要根据翻页的方向进行区分,因为翻页的方向不同,导致各关键点的计算公式、阴影位置不同。

触点

《空手撸一个Android阅读器,无第三方支持》 区域划分
《空手撸一个Android阅读器,无第三方支持》

ACTION_DOWN时判断翻页的位置,预先加载前一页or后一页的内容到Bitmap上,并计算各点坐标。

《空手撸一个Android阅读器,无第三方支持》

正常状况下滑动不予翻页,避免突兀到翻页效果。

《空手撸一个Android阅读器,无第三方支持》

翻页结束,根据预设条件判断翻页还是恢复,并调用Scroller.startScroll。

《空手撸一个Android阅读器,无第三方支持》
《空手撸一个Android阅读器,无第三方支持》

内容

由于翻页相关的内容主体逻辑已经在引用中有了详细的解释,所以没必要用篇幅再去讲解一遍。

但内容相关的代码会详细介绍,涉及到字体大小所影响的页面显示、TXT内容的读取策略等

根据最少知识原则设计内部类ContentController,用以读取文件、缓存内容、获取当前内容。

《空手撸一个Android阅读器,无第三方支持》

根据TXT小说的大体估计,平均一行约500汉字,共占1000字节约1K。

每页约1~3行,由每行字数多少决定。

缓存策略为缓存50页:当前页的前10页、包括当前页的后40页。

获取指定页内容(待优化)

《空手撸一个Android阅读器,无第三方支持》

从缓存中取,若指定页为阀值,则更新缓存。

待优化点:

  • 未预先缓存数据,现在显示正常是因为onMeasure的多次执行,导致initBitmapA多次执行,从而代替了预先缓存。如果内容不变的话initBitmapA不应该多次执行。

更新指定页前后的内容(待优化)

《空手撸一个Android阅读器,无第三方支持》

根据当前页计算,需缓存的前后页码。

待优化点:

  • 重复内容并没有重用,(现缓存1~50,需缓存37~87,则37~50内容重复)。但限于读取的方式,暂没想到好的解决办法,见下。

获取指定范围的内容(重点)

《空手撸一个Android阅读器,无第三方支持》

初始数组,获取输入流

《空手撸一个Android阅读器,无第三方支持》

标记第一页,根据字体大小计算每页及每行可容纳的字数。

标准IO操作,读取每行内容,计算当前行长度+当前页内容长度,是否超过当页容量,超过则放到下一页。当这页已满,stringBuffer.toString()放入result数组,页数加一。

由于每次读取都是从第1页开始,若缓存30~80页,则先读取前30页的内容并抛弃,然后才存到数组缓存。

所以要判断

《空手撸一个Android阅读器,无第三方支持》

待优化点:

  • 现读取的是raw中的文件,可优化选择文件功能
  • 每次读文件都是从第一行开始读,读字节的话又会造成,前后页之间的文字乱码。(暂未想到解决办法)
  • 当一页的第一行只有1个或很少的字数并换行,且接下来的段落每行字数都很满,则会造成最后一行绘出屏幕

工具栏

一个完整的书页,需要有工具栏来设置字体、字号、背景色等多种功能。这里只提供了工具栏,至于具体的工具待填充。

《空手撸一个Android阅读器,无第三方支持》

由于没有工具,所以

《空手撸一个Android阅读器,无第三方支持》
《空手撸一个Android阅读器,无第三方支持》

这里完全可以如上所说代码布局,我省个事!!

《空手撸一个Android阅读器,无第三方支持》

在onFinishInflate,获取对应控件。不必担心回调优先执行导致控件空指针,此处控件已初始化完成,并setText,在onMeasure和onLayout中也只会对控件进行拉伸显示

《空手撸一个Android阅读器,无第三方支持》

对控件进行测量,切记注意EXACTLY、AT_MOST与实际xml中填写项的对应。

《空手撸一个Android阅读器,无第三方支持》

这里布局子View,需要注意layout时,不要只在乎左上角的点,右下角同样重要,否则控件会被拉伸。

《空手撸一个Android阅读器,无第三方支持》
《空手撸一个Android阅读器,无第三方支持》

动画效果采用了Animator,属性动画,通过改变自定义属性,不停的layout达到效果

    原文作者:s1991721
    原文地址: https://www.jianshu.com/p/b3f744370a02
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞