本文正在参与「金石计划」
前言
上上星期的时分,和大伙分享了双仿真页,最近又完善了一些,作用愈加丝滑。
由于自己是一个拍摄爱好者,所以简略做了一个相册的Demo,话不多说,看最终作用:
图片中的作用来自三星的网页模拟器。
一、基础知识
这一块儿的基础知识已经在《如何写一个炫酷的大屏仿真页》和咱们说过:
- 贝塞尔曲线
- Canvas相关的Api
- Matrix
如果你对贝塞尔曲线仍是不了解,能够阅览我之前的文章:《从阅览仿真页看贝塞尔曲线》
简略的把握了这些,咱们就能够很方便的制作出仿真页。
二、架构 & 全体流程
看一下全体的结构:
全体的结构仍是比较简略的:
- 外层的
DoubleFlipView
担任处理接触事件 - 中心的
DoubleRealFlipView
担任动画和制作整个流程的把握 -
BaseDirectDrawAction
和下面的Leftxxx
和Rightxxx
都是抽象类,制作的详细施行方是它们,依据不同的方向又能够分为下面的四种,分别是左下页、左上页、右上页和右下页滑动。
简略的了解一下我的整个制作流程,从我的代码中也能够看出:
- 制作非翻转页
- 制作翻转页的基本内容
- 制作两页之间的暗影
- 制作翻转页下一层页显露的内容和暗影
- 制作翻转页的两侧暗影
- 翻转背部的内容
简略的用图片标示一下,进程对应图中的数字:
三、View转Bitmap
xml 文件转 Bitmap,看代码:
private fun createBitmapTwo(index: Int): Bitmap {
val root: View = LayoutInflater.from(this).inflate(R.layout.view_album_style_two, null, false)
//... 省略
root.measure(measureWidth, measureHeight)
root.layout(0, 0, root.measuredWidth, root.measuredHeight)
val bitmap = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
canvas.drawColor(Color.WHITE)
root.draw(canvas)
return bitmap
}
需求先对 View 进行 measure 和 layout 的进程,最终运用 View#draw
办法将 View 的内容制作在 Canvas 上面,最终返回咱们的 Bitmap 即可。
四、翻页机制
整个翻页的中心机制是这样的:
- 左右滑动决议翻页的进度
- 上下滑动决议翻页的视点
不太清楚?没关系,看图!
左右滑动:
纵向滑动:
纵向滑动的时分,实质上便是环绕滑动点坐圆,求到这个极值就能够,极值怎样算:
仔细分析一下:
C点 -> A点 直线滚动
DC = DA
c点已知,D点已知,B点X坐标已知,B点很轻松就能够求出来。
写双仿真的难点,很大一部分就来自于将这些动作转化成实际的数学公式,头快秃了~
五、制作基本内容
总算到咱们的制作环节了,制作的中心便是运用贝塞尔曲线构建Path,一切区域的选择都是基于这个Path。
1. 创立基础的Path
Path路径: A – E – EG曲线 – G – B – H – HF曲线 – F – A
关于 Path 中的每个点,我在之前的文章都讲过,咱们能够检查之前的文章:《从阅览仿真页看贝塞尔曲线》
尽管图片是单仿真,但是单仿真页和双仿真在贝塞尔这块的原理其实都共同。
2. 制作非翻转页
仍是用的上图:
进程1对应的页面就对错翻转页,化繁为简,整个制作区域不做处理,制作整个Bitmap。
3. 制作翻转页基础部分
其实便是制作进程2,对应图片中的部分蓝色三角形部分(标示的有点粗糙,见谅)。
进程便是:将左页原来的矩形制作区域,扣除之前的贝塞尔曲线,得到图中进程2对应的蓝色三角形。
选取一点代码:
override fun drawFlipPageContent(
canvas: Canvas,
reUsePath: Path,
flipPath: Path,
r: Int
) {
canvas.save()
reUsePath.reset()
reUsePath.moveTo(mLeftPageRBPoint.x - r, mLeftPageLTPoint.y)
reUsePath.arcTo( mLeftPageRBPoint.x - 2 * r, mLeftPageLTPoint.y, mLeftPageRBPoint.x, mLeftPageLTPoint.y + 2 * r, -90f, 90f, false)
reUsePath.lineTo(mLeftPageRBPoint.x, mLeftPageRBPoint.y - r)
reUsePath.arcTo(mLeftPageRBPoint.x - 2 * r, mLeftPageRBPoint.y - 2 * r,mLeftPageRBPoint.x, mLeftPageRBPoint.y, 0f, 90f, false)
reUsePath.lineTo(mLeftPageLTPoint.x, mLeftPageRBPoint.y)
reUsePath.lineTo(mLeftPageLTPoint.x, mLeftPageLTPoint.y)
reUsePath.close()
canvas.clipPath(reUsePath)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
canvas.clipOutPath(flipPath)
} else {
canvas.clipPath(flipPath, Region.Op.DIFFERENCE)
}
mLeftTopBitmap?.let {
canvas.drawBitmap(it, mLeftPageLTPoint.x, mLeftPageLTPoint.y, null)
}
canvas.restore()
}
中心区域的静态暗影没什么好说的,直接跳过。
4. 制作底层显露的内容
底层内容对应单仿真页中的 KLB 中的三角形,咱们能够:
- 先在 Canvas 抠出对应的贝塞尔区域
- 进一步抠出 KLB 三角形对应的区域
然后将下一层内容独自制作在这一块儿区域,依然是 Canvas 结合 Path,制作 Bitmap,没什么好说的。
5. 制作页边暗影
制作页边的暗影也不是件很简单的事,首先,它是分为两个图层画的,上下的暗影为一个图层,左右的暗影为一个图层,所以你能够看见,Canvas
的 save
和 restore
办法我调用了两遍。
另外一个麻烦的事便是视点的核算,由于有四个方向的翻页,每种方向翻页的时分视点核算都有点不同,所以你去视点核算的时分或许会有点头疼~
6. 制作背部内容
总算来到最终一步了。
抠涂层的过程也是一样:
- 先抠贝塞尔Path
- 去除 KLB 三角形对应的区域
之后便是制作内容,运用 Matrix
。
之前看单仿真,看代码先用了对称,然后旋转 + 平移,在写双仿真的时分,惊奇的发现,只用旋转和平移的方法就够了。
看图,我特意将第三张图和第五张图换成一样的:
我相信你能够很轻松的了解。完了,运用 Matrix 能够轻松的完结这些东西。
总结
总的来说,双仿真看着不是特别难,但是你去看代码的时分,或许不是一件特别简单的事,特别是运用各种数学公式的时分。
限制于时间的关系,后半部分文章没有贴更多的代码,主要我觉得贴代码也不太利于去了解,感兴趣的同学能够自己去看代码,周六一天时间都花在收拾 Demo 和写文章了。
Demo地址:github.com/mCyp/Double…
这段时间先学习 Opengles 了,想写个双仿真试试。