├── .gitignore
├── .idea
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── dictionaries
│ ├── Administrator.xml
│ └── pro.xml
└── encodings.xml
├── LICENSE
├── README-ZH.md
├── README.md
├── app
├── build.gradle
├── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── juziml
│ │ │ │ └── read
│ │ │ │ ├── base
│ │ │ │ └── BaseActivity.kt
│ │ │ │ ├── business
│ │ │ │ ├── BookMockData.java
│ │ │ │ ├── BookPaperAdapter.kt
│ │ │ │ ├── SimpleBookAct.kt
│ │ │ │ ├── StartupAct.kt
│ │ │ │ └── read
│ │ │ │ │ ├── anim
│ │ │ │ │ ├── AnimHelper.java
│ │ │ │ │ ├── CoverAnimationEffecter.java
│ │ │ │ │ ├── IAnimationEffecter.java
│ │ │ │ │ └── SimulationAnimationEffecter.java
│ │ │ │ │ └── view
│ │ │ │ │ ├── AnimParentView.java
│ │ │ │ │ ├── BookLayoutManager.java
│ │ │ │ │ ├── BookRecyclerView.java
│ │ │ │ │ ├── BookView.java
│ │ │ │ │ ├── EventProxy.java
│ │ │ │ │ ├── FPoint.java
│ │ │ │ │ ├── PaperLayout.java
│ │ │ │ │ ├── PuppetView.java
│ │ │ │ │ ├── RVInnerItemFunction.java
│ │ │ │ │ └── RVOuterFunction.java
│ │ │ │ ├── core
│ │ │ │ ├── App.java
│ │ │ │ ├── ArchApplication.kt
│ │ │ │ └── ArchConfig.kt
│ │ │ │ └── utils
│ │ │ │ ├── DLog.java
│ │ │ │ └── TraceUtils.java
│ │ └── res
│ │ │ ├── drawable-xxhdpi
│ │ │ ├── ic_add_chat.png
│ │ │ ├── ic_chat_gpt.png
│ │ │ ├── ic_chat_sakura_flower.png
│ │ │ ├── ic_close_pink_128.png
│ │ │ ├── ic_empty_new.png
│ │ │ ├── ic_head.png
│ │ │ ├── ic_launcher_20.xml
│ │ │ ├── ic_launcher_xx.xml
│ │ │ ├── ic_loading_cycle.png
│ │ │ ├── ic_send.png
│ │ │ ├── ic_voice_dark.png
│ │ │ ├── ic_voice_light.png
│ │ │ └── icon_net_error.png
│ │ │ ├── drawable
│ │ │ ├── bg_app_window.xml
│ │ │ ├── ic_launcher.xml
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── shadow.xml
│ │ │ ├── sp_chat_item_item_bg_bot.xml
│ │ │ ├── sp_chat_item_item_bg_user.xml
│ │ │ ├── sp_chat_session_list_item_bg.xml
│ │ │ ├── sp_item_bg.xml
│ │ │ └── test_img.jpg
│ │ │ ├── layout
│ │ │ ├── act_simple.xml
│ │ │ └── item_read.xml
│ │ │ ├── values-sw1024dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw1280dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw1365dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw240dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw320dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw384dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw392dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw400dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw410dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw411dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw432dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw480dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw533dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw592dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw600dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw640dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw662dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw720dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw768dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw800dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw811dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw820dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw960dp
│ │ │ └── dimens.xml
│ │ │ ├── values-sw961dp
│ │ │ └── dimens.xml
│ │ │ ├── values
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── ids.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ │ └── xml
│ │ │ └── network_security_config.xml
│ └── release
│ │ └── res
│ │ └── xml
│ │ └── strings.xml
└── test.jks
├── build.gradle
├── buildSrc
├── build.gradle.kts
└── src
│ └── main
│ └── java
│ └── com
│ └── buildsrc
│ └── kts
│ ├── AndroidConfig.kt
│ ├── DebugThirdSdkConfig.kt
│ ├── Dependencies.kt
│ ├── EnterpriseWeChatConfig.kt
│ ├── GlobalConfig.kt
│ ├── PropertiesUtils.kt
│ ├── Repositories.kt
│ ├── ThirdSdkConfig.kt
│ └── Utils.kt
├── gpu_test
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── juziml
│ │ └── content
│ │ └── gpu_test
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── juziml
│ │ │ └── content
│ │ │ └── gpu_test
│ │ │ ├── FPoint.java
│ │ │ ├── GpuTestAct.java
│ │ │ └── GpuTestCurlAnimView.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ └── act_gputest.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── juziml
│ └── content
│ └── gpu_test
│ └── ExampleUnitTest.java
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── images
├── Book_view_desc.jpg
└── demo_pic.png
├── screenMatch.properties
├── settings.gradle
└── signing.properties
/.gitignore:
--------------------------------------------------------------------------------
1 | # //////////////////////通用 且必须的忽略//////////////////////
2 |
3 | # 所有的iml 文件
4 | *.iml
5 |
6 | # .idea 下的所有文件与目录,可与!互斥
7 | .idea/*
8 |
9 | # 避免忽略 codeStyle
10 | !codeStyles/
11 |
12 | # 避免忽略 dictionaries //代码字典,屏蔽拼写错误提示
13 | !dictionaries/
14 |
15 | # 避免忽略 encodings.xml 编码
16 | !encodings.xml
17 |
18 | # 所有的build 文件夹
19 | build
20 |
21 | # 内存快照
22 | *.hprof
23 |
24 | # 截图快照
25 | /captures
26 | # MAC 磁盘文件 具体是啥不知道
27 | .DS_Store
28 |
29 | # native 编译缓存
30 | .externalNativeBuild
31 | .cxx
32 |
33 | # 整个 .gradle 文件夹
34 | .gradle
35 |
36 | ## 1 2级 目录的 release文件夹
37 | /release
38 | */release
39 | ## 1 2级 目录的 debug文件夹
40 | /debug
41 | */debug
42 |
43 | #本地配置参数
44 | local.properties
45 |
46 | # //////////////////////第三方//////////////////////
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | xmlns:android
19 |
20 | ^$
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | xmlns:.*
30 |
31 | ^$
32 |
33 |
34 | BY_NAME
35 |
36 |
37 |
38 |
39 |
40 |
41 | .*:id
42 |
43 | http://schemas.android.com/apk/res/android
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | .*:name
53 |
54 | http://schemas.android.com/apk/res/android
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | name
64 |
65 | ^$
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | style
75 |
76 | ^$
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | .*
86 |
87 | ^$
88 |
89 |
90 | BY_NAME
91 |
92 |
93 |
94 |
95 |
96 |
97 | .*
98 |
99 | http://schemas.android.com/apk/res/android
100 |
101 |
102 | ANDROID_ATTRIBUTE_ORDER
103 |
104 |
105 |
106 |
107 |
108 |
109 | .*
110 |
111 | .*
112 |
113 |
114 | BY_NAME
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/dictionaries/Administrator.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | priv
5 | prouter
6 | zhihu
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/dictionaries/pro.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | appid
5 | btns
6 | cheng
7 | datas
8 | gson
9 | shence
10 | unknow
11 | workorder
12 | xpxh
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Western-parotia
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README-ZH.md:
--------------------------------------------------------------------------------
1 | [English Doc](./README.md)
2 |
3 | # 介绍
4 |
5 | 
6 |
7 | * app module:阅读器demo
8 | * gpu_test module:上图纯色demo,作为独立的仿真动画实现最小demo,就一个类,包含完整的仿真动画算法与标点
9 |
10 | # 已知问题反馈
11 |
12 | 提供一种阅读器的实现思路,注意是实现思路不是完整的解决方案。一些反馈的问题我会先记录在这里,最近忙完了我会更新。
13 | 在这之前欢迎提PR处理
14 |
15 | * 内存增长问题,可通过增加bitmap缓存解决。bitmap创建的代码位置:2023-10-29 添加缓存策略
16 |
17 | # 实现思路介绍
18 |
19 | 采用木偶View将渲染, Paper页面布局、事件、动画完全分离。PaperLayout继承成自LinearLayout,
20 | 支持放入图片,视频等元素,但完全无需关心翻页动画的渲染。(不包含文字的处理,以后应该也不会添加)
21 |
22 | https://user-images.githubusercontent.com/13959965/230751166-a72e1f4b-317b-47a7-aa1c-bbc70ca34f13.mp4
23 |
24 | 核心类就4个,它们的职责跟它们的名字很相近。
25 |
26 | * BookView 摆放 RecyclerView 与 PuppetView
27 | * BookRecyclerView 作为底层容器,接受滑动事件,完成页面更换,接管无动画的事件
28 | * PaperLayout 作为页面卡片根布局,也就是设置给 RecyclerView.Adapter 加载的布局,接管仿真动画与覆盖动画的事件
29 | * PuppetView 本身不处理任何事件,只是展示动画
30 |
31 | ## 手绘一张图,呈现实现原理
32 |
33 | > 如果用文字来阐述原理难免要长篇大论,何况这里涉及到Z轴View堆叠,借此机会展示下我的绘画能力吧🐶(瞎搞)
34 |
35 |
36 | 
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [中文文档](./README-ZH.md)
2 |
3 | This is a reader project for the Android platform.
4 | It is based on a custom RecyclerView within a ViewGroup and uses a custom LayoutManager.
5 | By arranging elements along the Z-axis, it achieves complete separation of reader animations and
6 | data binding,
7 | allowing for full customization of the ItemView.
8 |
9 | It also supports embedding image and video advertisements. Please see the video below for an example
10 | of the effects:
11 |
12 | https://user-images.githubusercontent.com/13959965/230751166-a72e1f4b-317b-47a7-aa1c-bbc70ca34f13.mp4
13 |
14 | 
15 |
16 | # Usage
17 |
18 | Just like using a regular RecyclerView:
19 |
20 | ```kotlin
21 | bookView.setAdapter(RecyclerView.Adapter)
22 | bookView.setFlipMode(@BookLayoutManager.BookFlipMode)
23 |
24 | ```
25 |
26 | # Directory structure
27 |
28 | * app module:BookView demo
29 |
30 | * gpu_test module:A demo of pure curl animations
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | import com.buildsrc.kts.AndroidConfig
2 | import com.buildsrc.kts.Dependencies
3 |
4 | apply plugin: 'com.android.application'
5 | apply plugin: 'kotlin-android'
6 | apply plugin: "kotlin-parcelize"
7 |
8 | Properties properties = new Properties()
9 | properties.load(new FileInputStream(file(rootProject.file("signing.properties"))))
10 |
11 | android {
12 | compileSdkVersion AndroidConfig.compileSdkVersion
13 |
14 | useLibrary 'org.apache.http.legacy'
15 | viewBinding {
16 | enabled = true
17 | }
18 | signingConfigs {
19 | release {
20 | keyAlias properties['keyAlias']
21 | keyPassword properties['keyPassword']
22 | storeFile file(properties['storeFile'])
23 | storePassword properties['storePassword']
24 | }
25 | }
26 |
27 | defaultConfig {
28 | setApplicationId(AndroidConfig.AppInfo.applicationId)
29 | setVersionCode(AndroidConfig.AppInfo.versionCode)
30 | setVersionName(AndroidConfig.AppInfo.versionName)
31 | minSdkVersion AndroidConfig.minSdkVersion
32 | targetSdkVersion AndroidConfig.targetSdkVersion
33 | ndk {
34 | //flutter 目前只支持:'armeabi-v7a', 'arm64-v8a', 'x86_64'
35 | abiFilters "arm64-v8a", 'armeabi-v7a' //, 'x86', 'mips',
36 | }
37 | multiDexEnabled true
38 |
39 | javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }
40 |
41 | //app名
42 | resValue("string", "app_name", AndroidConfig.AppInfo.appName)
43 | }
44 |
45 | buildTypes {
46 | release {
47 | minifyEnabled AndroidConfig.AppInfo.releaseObfuscate
48 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
49 | debuggable false
50 | renderscriptDebuggable true
51 | zipAlignEnabled true
52 | signingConfig signingConfigs.release
53 | }
54 |
55 | debug {
56 | minifyEnabled false
57 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
58 | debuggable true
59 | renderscriptDebuggable true
60 | zipAlignEnabled false
61 | signingConfig signingConfigs.release
62 | }
63 |
64 | debugProguard {
65 | //从release出会导致一些key是release的
66 | initWith(debug)
67 | minifyEnabled AndroidConfig.AppInfo.debugProguardObfuscate
68 | zipAlignEnabled true
69 | }
70 | }
71 |
72 | compileOptions {
73 | sourceCompatibility JavaVersion.VERSION_1_8
74 | targetCompatibility JavaVersion.VERSION_1_8
75 | }
76 | kotlinOptions {
77 | jvmTarget = JavaVersion.VERSION_1_8.toString()
78 | //为java继承的kotlin interface default方法添加实现
79 | freeCompilerArgs += ["-Xjvm-default=all-compatibility"]
80 | }
81 |
82 | dexOptions {
83 | javaMaxHeapSize "4G"
84 | // //关闭预编译
85 | // preDexLibraries false
86 | // dexInProcess = false
87 | }
88 |
89 | lintOptions {
90 | //禁用掉丢失多国语言的错误提示
91 | disable 'MissingTranslation'
92 | //禁用掉manifest仅支持竖屏的错误提示
93 | disable 'LockedOrientationActivity'
94 | //禁用掉代码setText必须使用string res的警告
95 | disable 'SetTextI18n'
96 | //禁用掉xml的text必须使用string res的警告
97 | disable 'HardcodedText'
98 | //禁用掉xml的ImageView必须添加描述的警告
99 | disable 'ContentDescription'
100 | //禁用掉text使用dp的警告
101 | disable 'SpUsage'
102 |
103 | abortOnError false
104 | }
105 |
106 | }
107 |
108 |
109 | dependencies {
110 | implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
111 |
112 | implementation Dependencies.AndroidX.appcompat
113 | implementation Dependencies.OpenSourceLibrary.junit
114 | implementation Dependencies.OpenSourceLibrary.glide
115 | implementation Dependencies.Kotlin.kotlin_stdlib
116 | implementation Dependencies.AndroidX.constraintLayout
117 | api Dependencies.AndroidX.core_ktx
118 | implementation(Dependencies.Material.material)
119 | implementation(Dependencies.OpenSourceLibrary.recyclerView)
120 | implementation(Dependencies.OpenSourceLibrary.chadAdapter)
121 | implementation(Dependencies.Foundation.activityFragment)
122 | implementation(Dependencies.Foundation.view_binding_helper)
123 | implementation(Dependencies.Foundation.rv_adapter)
124 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
19 |
24 |
25 |
26 |
27 |
28 |
29 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/base/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | package com.juziml.read.base
2 |
3 | import android.os.Bundle
4 | import com.foundation.app.arc.activity.BaseFragmentManagerActivity
5 |
6 | abstract class BaseActivity : BaseFragmentManagerActivity() {
7 | override fun afterSuperOnCreate(savedInstanceState: Bundle?) {
8 | }
9 |
10 | override fun beforeSuperOnCreate(savedInstanceState: Bundle?) {
11 | }
12 |
13 | override fun initViewModel() {
14 |
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/business/BookMockData.java:
--------------------------------------------------------------------------------
1 | package com.juziml.read.business;
2 |
3 | /**
4 | * create by zhusw on 2020-03-27 14:51
5 | */
6 | public class BookMockData {
7 | public String content = "由于国外大部分公司仍在远程办公,所以部分服务与疫情之前相比有所下降,比如 Netflix、YouTube、Facebook 先后宣布下调欧洲视频流质量,以帮助缓解新冠病毒疫情期间任何潜在的网络拥堵,谷歌此前宣布暂停 Chrome 浏览器更新等。然而,黑客在此期间却动作频频,先后曝出世卫组织遭到不明黑客攻击、GitHub pages 被攻击等消息。" +
8 | "\n此前,业内就有专家预测近期可能会有很多攻击出现,尤其是医疗行业。医疗机构一般防御措施有限,一旦被攻击会造成严重后果。随着更多的黑客组织在此刻关注到相关热点,很有可能会有更多攻击手法出现。如果再出现像去年针对医疗行业的针对性勒索攻击的话,将会对相关目标造成灾难性后果,可直接造成整个医院业务停摆。如果相关的攻击发生在疫情严重地区的话,其后果不堪想象。建议采取以下防范措施:" +
9 | "1、及时升级操作系统以及应用软件,打全补丁,尤其是 MS17-010、CVE-2019-0708 等高危漏洞的补丁。由于 Windows 7 操作系统已经停止推送更新补丁,建议有条件的更新到 Windows 10 操作系统。\n" +
10 | "\n" +
11 | "2、及时更新已部署的终端、边界防护产品规则。\n" +
12 | "\n" +
13 | "3、尽量减少各种外部服务的暴露面(如 RDP,VNC 等远程服务),如果一定要开启的话,需要设置白名单访问策略,设置足够强壮的登陆密码,避免黑客利用远程服务攻入。\n" +
14 | "\n" +
15 | "4、增强人员的网络安全意识,不打开不明邮件,邮件中的不明链接、附件等。\n" +
16 | "\n" +
17 | "5、常用办公软件应保持严格的安全策略,如禁止运行 Office 宏等。\n" +
18 | "\n" +
19 | "很多企业纷纷开始远程办公模式,需要对暴露在互联网上的主机应采取一些必要的防范措施。\n" +
20 | "\n" +
21 | "1、及时升级相关主机的补丁,尤其是 2019 年出现的 CVE-2019-0708 等一系列 RDP 漏洞。\n" +
22 | "\n" +
23 | "2、设置足够强壮的登陆密码,并开启强制身份认证,避免黑客利用远程服务攻入。";
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/business/BookPaperAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.juziml.read.business
2 |
3 | import com.foundation.widget.crvadapter.viewbinding.ViewBindingQuickAdapter
4 | import com.foundation.widget.crvadapter.viewbinding.ViewBindingViewHolder
5 | import com.juziml.read.R
6 | import com.juziml.read.databinding.ItemReadBinding
7 |
8 | class BookPaperAdapter : ViewBindingQuickAdapter() {
9 |
10 | override fun convertVB(
11 | holder: ViewBindingViewHolder,
12 | vb: ItemReadBinding,
13 | item: BookMockData
14 | ) {
15 | vb.tvContent.text = item.content
16 | vb.tvContent2.text = item.content
17 |
18 | holder.addOnClickListener(R.id.ir2d_iv)
19 | holder.addOnClickListener(R.id.ir2d_btn1)
20 | holder.addOnClickListener(R.id.ir2d_btn2)
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/business/SimpleBookAct.kt:
--------------------------------------------------------------------------------
1 | package com.juziml.read.business
2 |
3 | import android.os.Bundle
4 | import android.widget.Toast
5 | import com.foundation.app.arc.utils.ext.lazyAtomic
6 | import com.juziml.read.R
7 | import com.juziml.read.base.BaseActivity
8 | import com.juziml.read.business.read.view.BookLayoutManager
9 | import com.juziml.read.databinding.ActSimpleBinding
10 |
11 | private const val DATA_SIZE = 20
12 |
13 | class SimpleBookAct : BaseActivity() {
14 | private val vb by lazyAndSetRoot()
15 | var position = 0
16 | private val bookPaperAdapter by lazyAtomic {
17 | BookPaperAdapter()
18 | }
19 |
20 | override fun init(savedInstanceState: Bundle?) {
21 | bookPaperAdapter.apply {
22 | setOnItemChildClickListener { _, view, position ->
23 | when (view.id) {
24 | R.id.ir2d_iv -> {
25 | showToast("点击图片:ir2d_iv")
26 | }
27 | R.id.ir2d_btn1 -> {
28 | showToast("点击按钮:ir2d_btn1")
29 | }
30 | R.id.ir2d_btn2 -> {
31 | showToast("点击按钮:ir2d_btn2")
32 | }
33 | }
34 | }
35 |
36 | }
37 | vb.bookView.apply {
38 | setAdapter(bookPaperAdapter)
39 | setOnPositionChangedListener { _, curPosition ->
40 | position = curPosition
41 | }
42 | setFlipMode(BookLayoutManager.BookFlipMode.MODE_CURL)
43 | setOnClickMenuListener {
44 | showToast("点击菜单")
45 | }
46 |
47 | }
48 |
49 | vb.btnCover.setOnClickListener {
50 | vb.bookView.setFlipMode(BookLayoutManager.BookFlipMode.MODE_COVER)
51 | }
52 | vb.btnCurl.setOnClickListener {
53 | vb.bookView.setFlipMode(BookLayoutManager.BookFlipMode.MODE_CURL)
54 | }
55 | vb.btnNormal.setOnClickListener {
56 | vb.bookView.setFlipMode(BookLayoutManager.BookFlipMode.MODE_NORMAL)
57 | }
58 | vb.btnPrevious.setOnClickListener {
59 | if (position > 0) {
60 | position -= 1
61 | vb.bookView.scrollToPosition(position)
62 | }
63 | }
64 | vb.btnNext.setOnClickListener {
65 | if (position < DATA_SIZE - 1) {
66 | position += 1
67 | vb.bookView.scrollToPosition(position)
68 | }
69 | }
70 | }
71 |
72 | override fun bindData() {
73 | bookPaperAdapter.setNewData(createData(DATA_SIZE))
74 | }
75 |
76 | private fun createData(paperSize: Int): List {
77 | var size = paperSize
78 | if (size <= 0) size = 5
79 | val data: MutableList = ArrayList()
80 | for (i in 0 until size) {
81 | val book = BookMockData()
82 | book.content = buildString(i)
83 | data.add(book)
84 | }
85 | return data
86 | }
87 |
88 | private fun buildString(i: Int): String? {
89 | val builder = StringBuilder()
90 | for (f in 0..999) {
91 | builder.append(i)
92 | builder.append("-")
93 | }
94 | return builder.toString()
95 | }
96 |
97 | private fun showToast(text: String) {
98 | Toast.makeText(this@SimpleBookAct, text, Toast.LENGTH_LONG).show()
99 | }
100 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/business/StartupAct.kt:
--------------------------------------------------------------------------------
1 | package com.juziml.read.business
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import com.juziml.read.base.BaseActivity
6 |
7 | class StartupAct : BaseActivity() {
8 | override fun bindData() {
9 | }
10 |
11 | override fun init(savedInstanceState: Bundle?) {
12 | startActivity(Intent(this, SimpleBookAct::class.java))
13 | }
14 |
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/business/read/anim/AnimHelper.java:
--------------------------------------------------------------------------------
1 | package com.juziml.read.business.read.anim;
2 |
3 | import android.graphics.drawable.GradientDrawable;
4 |
5 | /**
6 | * create by zhusw on 2020-08-06 15:53
7 | */
8 | public final class AnimHelper {
9 |
10 | public final static float MOVE_SLOP = 1;
11 |
12 | public final static int SLID_DIRECTION_UNKNOWN = 0;
13 |
14 | public final static int SLID_DIRECTION_LEFT = 1;
15 |
16 | public final static int SLID_DIRECTION_RIGHT = 2;
17 |
18 | public final static int RELAY_ANIM_DURATION = 400;
19 | public final static int CANCEL_ANIM_DURATION = 200;
20 |
21 | private GradientDrawable topLeftGradientDrawable;
22 | private GradientDrawable topRightGradientDrawable;
23 |
24 | private GradientDrawable bottomLeftGradientDrawable;
25 | private GradientDrawable bottomRightGradientDrawable;
26 |
27 | private GradientDrawable topBGradientDrawable;
28 | private GradientDrawable bottomBGradientDrawable;
29 |
30 | private GradientDrawable topCGradientDrawable;
31 | private GradientDrawable bottomCGradientDrawable;
32 |
33 | private GradientDrawable coverGradientDrawable;
34 |
35 | public AnimHelper() {
36 | initGradient();
37 | }
38 |
39 | private void initGradient() {
40 | int deepColor = 0x33333333;
41 | int lightColor = 0x01333333;
42 | int[] gradientColors = new int[]{lightColor, deepColor};//渐变颜色数组
43 | topLeftGradientDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, gradientColors);
44 | topLeftGradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
45 | bottomLeftGradientDrawable = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, gradientColors);
46 | bottomLeftGradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
47 |
48 | deepColor = 0x22333333;
49 | lightColor = 0x01333333;
50 | gradientColors = new int[]{deepColor, lightColor, lightColor};
51 | topRightGradientDrawable = new GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, gradientColors);
52 | topRightGradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
53 | bottomRightGradientDrawable = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, gradientColors);
54 | bottomRightGradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
55 |
56 | deepColor = 0x55111111;
57 | lightColor = 0x00111111;
58 | gradientColors = new int[]{deepColor, lightColor};
59 | topBGradientDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, gradientColors);
60 | topBGradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
61 | bottomBGradientDrawable = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, gradientColors);
62 | bottomBGradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
63 |
64 |
65 | deepColor = 0x55333333;
66 | lightColor = 0x00333333;
67 | gradientColors = new int[]{lightColor, deepColor};
68 | topCGradientDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, gradientColors);
69 | topCGradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
70 | bottomCGradientDrawable = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, gradientColors);
71 | bottomCGradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
72 |
73 | deepColor = 0x55333333;
74 | lightColor = 0x00333333;
75 | gradientColors = new int[]{deepColor, lightColor};
76 | coverGradientDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, gradientColors);
77 | coverGradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
78 | }
79 |
80 |
81 | public GradientDrawable getTopLeftGradientDrawable() {
82 | return topLeftGradientDrawable;
83 | }
84 |
85 | public GradientDrawable getTopRightGradientDrawable() {
86 | return topRightGradientDrawable;
87 | }
88 |
89 | public GradientDrawable getBottomLeftGradientDrawable() {
90 | return bottomLeftGradientDrawable;
91 | }
92 |
93 | public GradientDrawable getBottomRightGradientDrawable() {
94 | return bottomRightGradientDrawable;
95 | }
96 |
97 | public GradientDrawable getTopBGradientDrawable() {
98 | return topBGradientDrawable;
99 | }
100 |
101 | public GradientDrawable getBottomBGradientDrawable() {
102 | return bottomBGradientDrawable;
103 | }
104 |
105 | public GradientDrawable getTopCGradientDrawable() {
106 | return topCGradientDrawable;
107 | }
108 |
109 | public GradientDrawable getBottomCGradientDrawable() {
110 | return bottomCGradientDrawable;
111 | }
112 |
113 | public GradientDrawable getCoverGradientDrawable() {
114 | return coverGradientDrawable;
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/business/read/anim/CoverAnimationEffecter.java:
--------------------------------------------------------------------------------
1 | package com.juziml.read.business.read.anim;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Paint;
5 | import android.graphics.Path;
6 | import android.graphics.RectF;
7 | import android.graphics.drawable.GradientDrawable;
8 | import android.view.MotionEvent;
9 | import android.view.animation.AccelerateDecelerateInterpolator;
10 | import android.widget.Scroller;
11 |
12 | import com.juziml.read.business.read.view.PuppetView;
13 | import com.juziml.read.utils.DLog;
14 |
15 | import java.util.LinkedList;
16 | import java.util.List;
17 |
18 | /**
19 | * create by zhusw on 2020-08-24 14:06
20 | */
21 | public class CoverAnimationEffecter implements IAnimationEffecter {
22 | private final static int DOWN_AREA_NONE = -1;
23 | private final static int DOWN_AREA_MENU = 1;
24 | private final static int DOWN_AREA_LEFT = 2;
25 | private final static int DOWN_AREA_RIGHT = 3;
26 |
27 | int vWidth = 1;
28 | int vHeight = 1;
29 | private final PuppetView puppetView;
30 |
31 | private boolean isCancelFlip = false;
32 |
33 | private boolean coverAnimationRunning = false;
34 |
35 | private boolean isTouching = false;
36 |
37 | private final Scroller scroller;
38 | private final ScrollRunnable scrollRunnable;
39 | private final RectF menuBounds;
40 |
41 | private final Path pathA;
42 | private final Path pathB;
43 | private final Paint paint;
44 | private final int shadowWidth;
45 |
46 | public CoverAnimationEffecter(PuppetView readAnimView) {
47 | this.puppetView = readAnimView;
48 | scroller = new Scroller(readAnimView.getContext(), new AccelerateDecelerateInterpolator());
49 | scrollRunnable = new ScrollRunnable();
50 | menuBounds = new RectF();
51 | pathA = new Path();
52 | pathB = new Path();
53 | paint = new Paint(Paint.ANTI_ALIAS_FLAG);
54 | shadowWidth = 20;
55 | }
56 |
57 | private int downArea = DOWN_AREA_NONE;
58 | private float downX = 0F;
59 | private int coverSlideDirection = AnimHelper.SLID_DIRECTION_UNKNOWN;
60 | private final List moveSampling = new LinkedList<>();
61 | private final int MAX_COUNT = 5;
62 | private boolean prepareDrawCoverAnimEffect = false;
63 |
64 | private float currentX = -1;
65 |
66 | @Override
67 | public void handlerEvent(MotionEvent event) {
68 | if (coverAnimationRunning) return;
69 | float x = event.getRawX();
70 | float y = event.getY();
71 | switch (event.getAction()) {
72 | case MotionEvent.ACTION_DOWN:
73 | moveSampling.clear();
74 | downX = x;
75 | prepareDrawCoverAnimEffect = false;
76 | isTouching = true;
77 | currentX = -1;
78 | downArea = DOWN_AREA_NONE;
79 | coverSlideDirection = AnimHelper.SLID_DIRECTION_UNKNOWN;
80 | if (x > menuBounds.left && y > menuBounds.top
81 | && x < menuBounds.right && y < menuBounds.bottom) {
82 | downArea = DOWN_AREA_MENU;
83 | } else if (x < vWidth / 2F) {
84 | downArea = DOWN_AREA_LEFT;
85 | } else {
86 | downArea = DOWN_AREA_RIGHT;
87 | }
88 | break;
89 | case MotionEvent.ACTION_MOVE:
90 | isTouching = true;
91 | float curDistance = x - downX;
92 | if (coverSlideDirection == AnimHelper.SLID_DIRECTION_UNKNOWN && checkDownArea(downArea)) {
93 | if (curDistance > 0) {
94 | coverSlideDirection = AnimHelper.SLID_DIRECTION_RIGHT;
95 | } else {
96 | coverSlideDirection = AnimHelper.SLID_DIRECTION_LEFT;
97 | }
98 | puppetView.buildBitmap(coverSlideDirection);
99 | prepareDrawCoverAnimEffect = checkAnimCondition(coverSlideDirection);
100 | }
101 |
102 | if (prepareDrawCoverAnimEffect) {
103 | if (moveSampling.size() == 0
104 | || x != moveSampling.get(moveSampling.size() - 1)) {
105 | moveSampling.add(x);
106 | }
107 | if (moveSampling.size() > MAX_COUNT) {
108 | moveSampling.remove(0);
109 | }
110 | currentX = x;
111 | invalidate();
112 | }
113 | break;
114 | case MotionEvent.ACTION_CANCEL:
115 | isTouching = false;
116 | break;
117 | case MotionEvent.ACTION_UP:
118 | currentX = x;
119 | if (prepareDrawCoverAnimEffect) {
120 | if (moveSampling.size() > 0) {
121 | float lastMoveX = moveSampling.get(moveSampling.size() - 1);
122 | float firstMoveX = moveSampling.get(0);
123 | float finallyMoveX = lastMoveX - firstMoveX;
124 | if (coverSlideDirection == AnimHelper.SLID_DIRECTION_LEFT) {
125 | boolean lastFingerLeftSlop = finallyMoveX < 10;
126 | touchUp(lastFingerLeftSlop);
127 | } else if (coverSlideDirection == AnimHelper.SLID_DIRECTION_RIGHT) {
128 | finallyMoveX = lastMoveX - firstMoveX;
129 | touchUp(finallyMoveX < 0);
130 | }
131 | } else {
132 | touchUp(false);
133 | }
134 | } else if (downArea == DOWN_AREA_MENU) {
135 | if (x > menuBounds.left && x < menuBounds.right
136 | && y > menuBounds.top && y < menuBounds.bottom) {
137 | puppetView.onClickMenuArea();
138 | }
139 | } else if (downArea != DOWN_AREA_NONE) {
140 | if (x == downX && downX >= vWidth / 2F) {//下一页
141 | coverSlideDirection = AnimHelper.SLID_DIRECTION_LEFT;
142 | puppetView.buildBitmap(coverSlideDirection);
143 | if (checkAnimCondition(coverSlideDirection)) {
144 | touchUp(true);
145 | }
146 | } else if (x == downX && downX < vWidth / 2F) {//上一页
147 | coverSlideDirection = AnimHelper.SLID_DIRECTION_RIGHT;
148 | puppetView.buildBitmap(coverSlideDirection);
149 | if (checkAnimCondition(coverSlideDirection)) {
150 | touchUp(false);
151 | }
152 | }
153 | }
154 | moveSampling.clear();
155 | isTouching = false;
156 | break;
157 | default:
158 | break;
159 | }
160 | }
161 |
162 | private void touchUp(boolean lastFingerLeftSlop) {
163 | DLog.log("touchUp coverAnimationRunning=%s", coverAnimationRunning);
164 | coverAnimationRunning = true;
165 | isCancelFlip = (coverSlideDirection == AnimHelper.SLID_DIRECTION_RIGHT && lastFingerLeftSlop)
166 | || (coverSlideDirection == AnimHelper.SLID_DIRECTION_LEFT && !lastFingerLeftSlop);
167 |
168 | int duration = isCancelFlip ? AnimHelper.CANCEL_ANIM_DURATION : AnimHelper.RELAY_ANIM_DURATION;
169 | duration = (int) (duration * 0.7F);
170 | int startX = (int) currentX;
171 | int startY = 0;
172 | int dy = 0;
173 | int dx;
174 |
175 | if (lastFingerLeftSlop) {
176 |
177 | dx = (int) -(vWidth - (downX - currentX));
178 | } else {
179 | dx = vWidth - (int) currentX;
180 | }
181 | scroller.startScroll(startX, startY, dx, dy, duration);
182 | invalidate();
183 | }
184 |
185 |
186 | @Override
187 | public void draw(Canvas canvas) {
188 | if (currentX == -1) {
189 | DLog.log("CoverAnimationEffect draw 1");
190 | return;
191 | }
192 | if (coverSlideDirection != AnimHelper.SLID_DIRECTION_LEFT && coverSlideDirection != AnimHelper.SLID_DIRECTION_RIGHT) {
193 | DLog.log("CoverAnimationEffect draw 2");
194 | return;
195 | }
196 | if (coverSlideDirection == AnimHelper.SLID_DIRECTION_LEFT
197 | && (null == puppetView.getCurrentBitmap() || null == puppetView.getNextBitmap())) {
198 | DLog.log("CoverAnimationEffect draw 3");
199 | return;
200 | }
201 | if (coverSlideDirection == AnimHelper.SLID_DIRECTION_RIGHT && null == puppetView.getPreviousBitmap()) {
202 | DLog.log("CoverAnimationEffect draw 4");
203 | return;
204 | }
205 | DLog.log("CoverAnimationEffect draw 5");
206 | if (coverSlideDirection == AnimHelper.SLID_DIRECTION_LEFT) {
207 | float offset = downX - currentX;
208 | offset = Math.max(0, offset);
209 | canvas.save();
210 | canvas.clipPath(getPathAToLeft());
211 | canvas.drawBitmap(puppetView.getCurrentBitmap(), -offset, 0, paint);
212 | canvas.restore();
213 | canvas.save();
214 | canvas.clipPath(getPathB());
215 | canvas.drawBitmap(puppetView.getNextBitmap(), 0, 0, paint);
216 | canvas.restore();
217 | drawShadow((int) (vWidth - offset), canvas);
218 | } else {
219 | float leftOffset = vWidth - currentX;
220 | canvas.save();
221 | canvas.clipPath(getPathAToRight());
222 | canvas.drawBitmap(puppetView.getPreviousBitmap(), -leftOffset, 0, paint);
223 | canvas.restore();
224 | drawShadow((int) currentX, canvas);
225 | }
226 | }
227 |
228 | private void drawShadow(int left, Canvas canvas) {
229 | GradientDrawable drawable = puppetView.getAnimHelper().getCoverGradientDrawable();
230 | drawable.setBounds(left, 0, left + shadowWidth, vHeight);
231 | drawable.draw(canvas);
232 | }
233 |
234 | private Path getPathAToLeft() {
235 | pathA.reset();
236 | float x = vWidth - (downX - currentX);
237 | x = Math.min(vWidth, x);
238 | pathA.lineTo(x, 0);
239 | pathA.lineTo(x, vHeight);
240 | pathA.lineTo(0, vHeight);
241 | pathA.close();
242 | return pathA;
243 | }
244 |
245 | private Path getPathB() {
246 | pathB.reset();
247 | float x = vWidth - (downX - currentX);
248 | x = Math.min(vWidth, x);
249 | pathB.moveTo(x, 0);
250 | pathB.lineTo(vWidth, 0);
251 | pathB.lineTo(vWidth, vHeight);
252 | pathB.lineTo(x, vHeight);
253 | pathB.close();
254 | return pathB;
255 | }
256 |
257 | private Path getPathAToRight() {
258 | pathA.reset();
259 | pathA.lineTo(currentX, 0);
260 | pathA.lineTo(currentX, vHeight);
261 | pathA.lineTo(0, vHeight);
262 | pathA.close();
263 | return pathA;
264 | }
265 |
266 |
267 | private boolean checkDownArea(int downArea) {
268 | return downArea != DOWN_AREA_MENU && downArea != DOWN_AREA_NONE;
269 | }
270 |
271 | private boolean checkAnimCondition(int slideDirection) {
272 | if (slideDirection == AnimHelper.SLID_DIRECTION_LEFT && (null != puppetView.getNextBitmap() && null != puppetView.getCurrentBitmap())) {
273 | return true;
274 | } else if (slideDirection == AnimHelper.SLID_DIRECTION_RIGHT && null != puppetView.getPreviousBitmap()) {
275 | return true;
276 | }
277 | return false;
278 | }
279 |
280 | @Override
281 | public boolean animInEffect() {
282 | return isTouching || coverAnimationRunning;
283 | }
284 |
285 | @Override
286 | public void onViewSizeChanged(int vWidth, int vHeight) {
287 | this.vWidth = vWidth;
288 | this.vHeight = vHeight;
289 | menuBounds.left = vWidth / 3F;
290 | menuBounds.top = vHeight / 3F;
291 | menuBounds.right = vWidth * 2 / 3F;
292 | menuBounds.bottom = vHeight * 2 / 3F;
293 | }
294 |
295 | @Override
296 | public void onViewAttachedToWindow() {
297 |
298 | }
299 |
300 | @Override
301 | public void onViewDetachedFromWindow() {
302 | puppetView.removeCallbacks(scrollRunnable);
303 | }
304 |
305 | private void invalidate() {
306 | puppetView.postInvalidate();
307 | }
308 |
309 | @Override
310 | public void onScroll() {
311 | if (scroller.computeScrollOffset()) {
312 |
313 | int x = scroller.getCurrX();
314 | int y = scroller.getCurrY();
315 | if (x == scroller.getFinalX() && y == scroller.getFinalY()) {
316 | scroller.forceFinished(true);
317 | //补一点时间,避免动画太快结束,提供两次动画触发间隔
318 | DLog.log("coverAnimationRunning coverAnimationRunning=%s 结束,延时开启 状态重置", coverAnimationRunning);
319 | puppetView.post(scrollRunnable);
320 | } else {
321 | currentX = x;
322 | invalidate();
323 | }
324 | }
325 |
326 | }
327 |
328 |
329 | protected class ScrollRunnable implements Runnable {
330 | @Override
331 | public void run() {
332 | puppetView.reset();
333 | coverAnimationRunning = false;
334 | if (!isCancelFlip) {
335 | if (coverSlideDirection == AnimHelper.SLID_DIRECTION_LEFT) {
336 | puppetView.onExpectNext();
337 | } else if (coverSlideDirection == AnimHelper.SLID_DIRECTION_RIGHT) {
338 | puppetView.onExpectPrevious();
339 | }
340 | }
341 | invalidate();
342 | }
343 | }
344 | }
345 |
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/business/read/anim/IAnimationEffecter.java:
--------------------------------------------------------------------------------
1 | package com.juziml.read.business.read.anim;
2 |
3 | import android.graphics.Canvas;
4 | import android.view.MotionEvent;
5 |
6 | /**
7 | * create by zhusw on 2020-08-24 14:05
8 | */
9 | public interface IAnimationEffecter {
10 |
11 | void onScroll();
12 |
13 | void handlerEvent(MotionEvent event);
14 |
15 | void draw(Canvas canvas);
16 |
17 | boolean animInEffect();
18 |
19 | void onViewSizeChanged(int vWidth, int vHeight);
20 |
21 | void onViewAttachedToWindow();
22 |
23 | void onViewDetachedFromWindow();
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/business/read/view/AnimParentView.java:
--------------------------------------------------------------------------------
1 | package com.juziml.read.business.read.view;
2 |
3 | import android.graphics.Bitmap;
4 |
5 | import com.juziml.read.business.read.anim.AnimHelper;
6 |
7 | /**
8 | * create by zhusw on 2020-08-03 11:28
9 | */
10 | public interface AnimParentView {
11 | void onExpectNext();
12 |
13 | void onExpectPrevious();
14 |
15 | Bitmap getPreviousBitmap();
16 |
17 | Bitmap getCurrentBitmap();
18 |
19 | Bitmap getNextBitmap();
20 |
21 | int getBackgroundColor();
22 |
23 | AnimHelper getAnimHelper();
24 |
25 | void onClickMenuArea();
26 |
27 | /**
28 | * 只在非卷曲模式下调用
29 | */
30 | void onClickNextArea();
31 |
32 | /**
33 | * 只在非卷曲模式下调用
34 | */
35 | void onClickPreviousArea();
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/business/read/view/BookLayoutManager.java:
--------------------------------------------------------------------------------
1 | package com.juziml.read.business.read.view;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.ValueAnimator;
6 | import android.content.Context;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.view.animation.LinearInterpolator;
10 |
11 | import androidx.annotation.IntDef;
12 | import androidx.recyclerview.widget.RecyclerView;
13 |
14 | import java.lang.annotation.ElementType;
15 | import java.lang.annotation.Retention;
16 | import java.lang.annotation.RetentionPolicy;
17 | import java.lang.annotation.Target;
18 | import java.util.List;
19 |
20 |
21 | /**
22 | * create by zhusw on 2020-03-29 17:11
23 | */
24 | public class BookLayoutManager extends RecyclerView.LayoutManager {
25 | private int bookFlipMode = BookFlipMode.MODE_CURL;
26 |
27 | private Context context;
28 | /**
29 | * 一次完整的聚焦滑动所需要的移动距离
30 | */
31 | private float onceCompleteScrollLength = -1;
32 |
33 | /**
34 | * 屏幕可见第一个view的position
35 | */
36 | private int firstPos;
37 |
38 | /**
39 | * 屏幕可见的最后一个view的position
40 | */
41 | private int lastPos;
42 |
43 | /**
44 | * 水平方向累计偏移量
45 | */
46 | private long horizontalOffset;
47 |
48 | private int childWidth = 0;
49 | private ValueAnimator selectAnimator;
50 |
51 | private boolean autoLeftScroll = false;
52 |
53 | private OnStopScroller onStopScroller;
54 | private OnForceLayoutCompleted onForceLayoutCompleted;
55 |
56 | public BookLayoutManager(Context context) {
57 | this.context = context;
58 | }
59 |
60 | public void setonStopScroller(OnStopScroller onStopScroller) {
61 | this.onStopScroller = onStopScroller;
62 | }
63 |
64 | @Override
65 | public RecyclerView.LayoutParams generateDefaultLayoutParams() {
66 | return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
67 | ViewGroup.LayoutParams.WRAP_CONTENT);
68 | }
69 |
70 | public void setBookFlipMode(@BookFlipMode int bookFlipMode) {
71 | this.bookFlipMode = bookFlipMode;
72 | requestLayout();
73 | }
74 |
75 | public int getBookFlipMode() {
76 | return bookFlipMode;
77 | }
78 |
79 | public void setAutoLeftScroll(boolean autoLeftScroll) {
80 | this.autoLeftScroll = autoLeftScroll;
81 | }
82 |
83 | public void setOnForceLayoutCompleted(OnForceLayoutCompleted onForceLayoutCompleted) {
84 | this.onForceLayoutCompleted = onForceLayoutCompleted;
85 | }
86 |
87 | //确认允许水平滚动
88 | @Override
89 | public boolean canScrollHorizontally() {
90 | return true;
91 | }
92 |
93 |
94 | @Override
95 | public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
96 | // 返回用于布局的view数量,!= adapter 的 元素个数
97 | if (state.getItemCount() == 0) {
98 | removeAndRecycleAllViews(recycler);
99 | return;
100 | }
101 | // 分离全部已有的view 放入临时缓存 mAttachedScrap 集合中
102 | detachAndScrapAttachedViews(recycler);
103 | //确定布局,类似于 viewGroup的 onLayout
104 | layout(recycler, 0);
105 | }
106 |
107 |
108 | private int layout(RecyclerView.Recycler recycler, int dx) {
109 | int resultDelta = horizontalLayout(recycler, dx);
110 | recycleChildren(recycler);
111 | return resultDelta;
112 | }
113 |
114 | /**
115 | * 最大偏移量
116 | *
117 | * @return
118 | */
119 | private float getMaxOffset() {
120 | if (childWidth == 0 || getItemCount() == 0) return 0;
121 | return (childWidth) * (getItemCount() - 1);
122 | }
123 |
124 | @Override
125 | public void onScrollStateChanged(int state) {
126 | switch (state) {
127 | case RecyclerView.SCROLL_STATE_DRAGGING://滑动手势开始
128 | cancelAnimator();
129 | break;
130 |
131 | case RecyclerView.SCROLL_STATE_IDLE://停止滑动
132 | final int selectPosition = findShouldSelectPosition();
133 | smoothScrollToPosition(selectPosition);
134 | if (null != onStopScroller) {
135 | onStopScroller.onStop(autoLeftScroll, selectPosition);
136 | }
137 | break;
138 | default:
139 | break;
140 | }
141 |
142 | }
143 |
144 | /**
145 | * 强制滚动到指定位置
146 | *
147 | * @param position
148 | */
149 | public void forceScrollToPosition(int position) {
150 | if (position > -1 && position < getItemCount()) {
151 | scrollToPosition(position, false);
152 | }
153 | }
154 |
155 | /**
156 | * 平滑滚动到某个位置
157 | *
158 | * @param position 目标Item索引
159 | */
160 | public void smoothScrollToPosition(int position) {
161 | if (position > -1 && position < getItemCount()) {
162 | scrollToPosition(position, true);
163 | }
164 | }
165 |
166 | public int findShouldSelectPosition() {
167 | if (onceCompleteScrollLength == -1 || firstPos == -1) {
168 | return -1;
169 | }
170 | int position;
171 | if (autoLeftScroll) {
172 | position = (int) (Math.abs(horizontalOffset) / childWidth);
173 | // int remainder = (int) (Math.abs(horizontalOffset) % childWidth);
174 | // 超过临界距离 选中下一页,108 为 1/10的 1080 屏幕
175 | //固定临界值,避免屏幕越大需要滑动的距离越远
176 | // if (remainder >= ReadRecyclerView.MOVE_LEFT_MIN) {
177 | if (position + 1 <= getItemCount() - 1) {
178 | return position + 1;
179 | }
180 | // }
181 | } else {
182 | position = (int) (Math.abs(horizontalOffset) / childWidth);
183 | }
184 |
185 | return position;
186 | }
187 |
188 | private float getScrollToPositionOffset(int position) {
189 | return position * childWidth - Math.abs(horizontalOffset);
190 | }
191 |
192 | private void scrollToPosition(final int position, boolean withAnim) {
193 | cancelAnimator();
194 | final float distance = getScrollToPositionOffset(position);
195 | if (distance == 0) return;
196 | if (withAnim) {
197 |
198 | long minDuration = 100;
199 | long maxDuration = 300;
200 | long duration;
201 |
202 | float distanceFraction = (Math.abs(distance) / childWidth);
203 |
204 | if (distance <= childWidth) {
205 | duration = (long) (minDuration + (maxDuration - minDuration) * distanceFraction);
206 | } else {
207 | duration = (long) (maxDuration * distanceFraction);
208 | }
209 | selectAnimator = ValueAnimator.ofFloat(0.0f, distance);
210 | selectAnimator.setDuration(duration);
211 | selectAnimator.setInterpolator(new LinearInterpolator());
212 | final float startedOffset = horizontalOffset;
213 | selectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
214 | @Override
215 | public void onAnimationUpdate(ValueAnimator animation) {
216 | float value = (float) animation.getAnimatedValue();
217 | horizontalOffset = (long) (startedOffset + value);
218 | requestLayout();
219 | if (value == distance) {//主动给一个滚动回调,因为不会处罚onScrollstop
220 | if (null != onStopScroller) {
221 | onStopScroller.onStop(distance > 0, position);
222 | }
223 | }
224 | }
225 | });
226 | selectAnimator.addListener(new AnimatorListenerAdapter() {
227 | @Override
228 | public void onAnimationEnd(Animator animation) {
229 | super.onAnimationEnd(animation);
230 | }
231 | });
232 | selectAnimator.start();
233 |
234 | } else {
235 | horizontalOffset += (long) distance;
236 | requestLayout();
237 | if (null != onForceLayoutCompleted) {
238 | onForceLayoutCompleted.onLayoutCompleted(position);
239 | }
240 | }
241 |
242 |
243 | }
244 |
245 | public void cancelAnimator() {
246 | if (selectAnimator != null && (selectAnimator.isStarted() || selectAnimator.isRunning())) {
247 | selectAnimator.cancel();
248 | }
249 | }
250 |
251 | public boolean animIsRunning() {
252 | return selectAnimator != null && (selectAnimator.isStarted() || selectAnimator.isRunning());
253 | }
254 |
255 | private int horizontalLayout(RecyclerView.Recycler recycler, int dx) {
256 | //-----------------------1 边界检测---------------
257 | //已达左边界
258 | if (dx < 0) {
259 | if (horizontalOffset < 0) {
260 | dx = 0;
261 | horizontalOffset = dx;
262 | }
263 | }
264 | //确认右边界
265 | if (dx > 0) {
266 | if (horizontalOffset >= getMaxOffset()) {
267 | horizontalOffset = (long) getMaxOffset();
268 | dx = 0;
269 | }
270 | }
271 | // 分离全部的view,加入到临时缓存,这里调用,因为在滑动过程中,可显view 可能发生改变
272 | detachAndScrapAttachedViews(recycler);
273 |
274 | //-----------------------2 计算用于 view 确定位置的参数---------------
275 |
276 | float layoutX = 0;
277 | float fraction = 0;
278 |
279 | View tempView = null;
280 |
281 | int tempPosition = -1;
282 |
283 | if (onceCompleteScrollLength == -1) {
284 | // 因为firstVisiPos在下面可能被改变,所以用tempPosition暂存一下
285 | tempPosition = firstPos;
286 | tempView = recycler.getViewForPosition(tempPosition);
287 | measureChildWithMargins(tempView, 0, 0);
288 | //以第一个子view宽度为计算标准,这样就不支持 itemType 了。全部item要保持宽度一样,margin参数一样
289 | childWidth = getDecoratedMeasurementHorizontal(tempView);
290 | }
291 | // 修正第一个可见view firstVisiPos 已经滑动了多少个完整的onceCompleteScrollLength就代表滑动了多少个item
292 |
293 | /**
294 | * 第一个子view的偏移量
295 | */
296 | float firstChildCompleteScrollLength = getWidth() / 2F + childWidth / 2F;
297 |
298 | if (horizontalOffset >= firstChildCompleteScrollLength) {
299 | layoutX = 0;
300 | onceCompleteScrollLength = childWidth;
301 | //计算 滚动到了 哪个view的区域
302 | firstPos = (int) Math.floor(Math.abs(horizontalOffset - firstChildCompleteScrollLength) / onceCompleteScrollLength) + 1;
303 | fraction = (Math.abs(horizontalOffset - firstChildCompleteScrollLength) % onceCompleteScrollLength) / (onceCompleteScrollLength * 1.0f);
304 | } else {
305 | firstPos = 0;
306 | layoutX = getMinOffset();
307 | //记录单个view 需要滚动的距离
308 | onceCompleteScrollLength = firstChildCompleteScrollLength;
309 | fraction = (Math.abs(horizontalOffset) % onceCompleteScrollLength) / (onceCompleteScrollLength * 1.0f);
310 | }
311 | // 临时将fastVisiPos赋值为getItemCount() - 1,放心,下面遍历时会判断view是否已溢出屏幕,并及时修正该值并结束布局
312 | //注意这里 是adapter 的 个数,而不是 state 保存的view 显示数量
313 | lastPos = getItemCount() - 1;
314 | //这里似乎多此一举了 ,可以把 分母直接替换为 normalViewOffset 在上面的逻辑中计算出来
315 | float normalViewOffset = onceCompleteScrollLength * fraction;
316 | boolean isNormalViewOffsetSetted = false;
317 |
318 | //-----------------------3 确认view位置---------------
319 | for (int itemIndex = firstPos; itemIndex <= lastPos; itemIndex++) {
320 |
321 | View itemView;
322 |
323 | // 如果初始化数据时已经取了一个临时view
324 | if (itemIndex == tempPosition && null != tempView) {
325 | itemView = tempView;
326 | } else {
327 | itemView = recycler.getViewForPosition(itemIndex);
328 | }
329 | //计算新view 位置
330 | int focusPosition = (int) (Math.abs(horizontalOffset) / (childWidth));
331 |
332 | if (itemIndex <= focusPosition) {
333 | addView(itemView);
334 | } else {
335 | addView(itemView, 0);
336 | }
337 | //测量新view
338 | measureChildWithMargins(itemView, 0, 0);
339 | if (!isNormalViewOffsetSetted) {
340 | layoutX -= normalViewOffset;
341 | isNormalViewOffsetSetted = true;
342 | }
343 | //计算view layout 坐标
344 | int left, top, right, bottom;
345 | left = (int) layoutX;
346 | top = 0;
347 | right = left + getDecoratedMeasurementHorizontal(itemView);
348 | bottom = top + getDecoratedMeasurementVertical(itemView);
349 | layoutDecoratedWithMargins(itemView, left, top, right, bottom);
350 | //更新下一个view X 轴坐标
351 | layoutX += childWidth;
352 | //修正溢出屏幕的view个数
353 | if (layoutX > getWidth() - getPaddingRight()) {
354 | lastPos = itemIndex;
355 | break;
356 | }
357 | }//end for
358 | return dx;
359 | }
360 |
361 | @Override
362 | public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
363 |
364 | if (dx == 0 || getChildCount() == 0) {
365 |
366 | return 0;
367 | }
368 | horizontalOffset += dx;
369 | dx = layout(recycler, dx);
370 |
371 | return dx;
372 | }
373 |
374 | /**
375 | * 回收需回收的item
376 | */
377 | private void recycleChildren(RecyclerView.Recycler recycler) {
378 | List scrapList = recycler.getScrapList();
379 | for (int i = 0; i < scrapList.size(); i++) {
380 | RecyclerView.ViewHolder holder = scrapList.get(i);
381 | removeAndRecycleView(holder.itemView, recycler);
382 | }
383 | }
384 |
385 | /**
386 | * 获取某个childView在竖直方向所占的空间,将margin考虑进去
387 | * 包含了指示器
388 | *
389 | * @param view
390 | * @return
391 | */
392 | public int getDecoratedMeasurementVertical(View view) {
393 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
394 | view.getLayoutParams();
395 | return getDecoratedMeasuredHeight(view) + params.topMargin
396 | + params.bottomMargin;
397 | }
398 |
399 | /**
400 | * 获取某个childView在水平方向所占的空间,将margin考虑进去
401 | *
402 | * @param view
403 | * @return
404 | */
405 | public int getDecoratedMeasurementHorizontal(View view) {
406 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
407 | view.getLayoutParams();
408 | return getDecoratedMeasuredWidth(view) + params.leftMargin
409 | + params.rightMargin;
410 | }
411 |
412 | /**
413 | * 获取最小的偏移量
414 | *
415 | * @return
416 | */
417 | private float getMinOffset() {
418 | if (childWidth == 0) return 0;
419 | return (getWidth() - childWidth) / 2;
420 | }
421 |
422 | protected void onRecyclerViewSizeChange() {
423 | onceCompleteScrollLength = -1;
424 | }
425 |
426 | @Retention(RetentionPolicy.SOURCE)
427 | @Target(ElementType.PARAMETER)
428 | @IntDef({
429 | BookFlipMode.MODE_NORMAL,
430 | BookFlipMode.MODE_COVER,
431 | BookFlipMode.MODE_CURL,
432 |
433 | })
434 | public @interface BookFlipMode {
435 | int MODE_NORMAL = 1;
436 | int MODE_COVER = 2;
437 | int MODE_CURL = 3;
438 |
439 | }
440 |
441 | public interface OnStopScroller {
442 | void onStop(boolean autoLeftScroll, int curPos);
443 | }
444 |
445 | public interface OnForceLayoutCompleted {
446 | void onLayoutCompleted(final int curPos);
447 | }
448 | }
449 |
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/business/read/view/BookRecyclerView.java:
--------------------------------------------------------------------------------
1 | package com.juziml.read.business.read.view;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.graphics.Bitmap;
6 | import android.util.AttributeSet;
7 | import android.view.MotionEvent;
8 | import android.view.View;
9 |
10 | import androidx.annotation.Nullable;
11 | import androidx.recyclerview.widget.RecyclerView;
12 |
13 | import java.lang.ref.WeakReference;
14 | import java.util.LinkedList;
15 | import java.util.List;
16 |
17 |
18 | /**
19 | * 关闭了抛投效果
20 | * create by zhusw on 2020-03-30 11:51
21 | */
22 | public class BookRecyclerView extends RecyclerView implements RVInnerItemFunction, RVOuterFunction {
23 |
24 | private final BookLayoutManager layoutManager;
25 |
26 | private boolean allowInterceptTouchEvent = true;
27 |
28 | private int currentPosition = 0;
29 | private WeakReference eventProxyWeakReference;
30 | private AnimParentView animParentView;
31 | private BookView.OnPositionChangedListener onPositionChangedListener;
32 |
33 | private Bitmap previousBitmap = null;
34 | private Bitmap currentBitmap = null;
35 | private Bitmap nextBitmap = null;
36 |
37 | public BookRecyclerView(Context context) {
38 | this(context, null);
39 | }
40 |
41 | public BookRecyclerView(Context context, @Nullable AttributeSet attrs) {
42 | this(context, attrs, 0);
43 | }
44 |
45 | public BookRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
46 | super(context, attrs, defStyle);
47 | layoutManager = new BookLayoutManager(context);
48 | setLayoutManager(layoutManager);
49 | layoutManager.setOnForceLayoutCompleted(new ItemOnForceLayoutCompleted());
50 | layoutManager.setonStopScroller(new ItemOnScrollStop());
51 | }
52 |
53 | @Override
54 | protected void onAttachedToWindow() {
55 | super.onAttachedToWindow();
56 | animParentView = (AnimParentView) getParent();
57 | }
58 |
59 | @Override
60 | protected void onDetachedFromWindow() {
61 | super.onDetachedFromWindow();
62 | eventProxyWeakReference.clear();
63 | clearBitmapCache();
64 | }
65 |
66 | protected void bindReadCurlAnimProxy(EventProxy ic) {
67 | if (null != eventProxyWeakReference) {
68 | eventProxyWeakReference.clear();
69 | }
70 | eventProxyWeakReference = new WeakReference<>(ic);
71 | }
72 |
73 |
74 | protected void setOnPositionChangedListener(BookView.OnPositionChangedListener onPositionChangedListener) {
75 | this.onPositionChangedListener = onPositionChangedListener;
76 | }
77 |
78 | @Override
79 | public boolean fling(int velocityX, int velocityY) {
80 | return false;
81 | }
82 |
83 | @Override
84 | public void scrollToPosition(int position) {
85 | layoutManager.forceScrollToPosition(position);
86 | }
87 |
88 | @Override
89 | public void smoothScrollToPosition(int position) {
90 | layoutManager.smoothScrollToPosition(position);
91 | }
92 |
93 | @Override
94 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
95 | super.onSizeChanged(w, h, oldw, oldh);
96 | layoutManager.onRecyclerViewSizeChange();
97 | }
98 |
99 |
100 | private final List moveSampling = new LinkedList<>();
101 | private final int MAX_COUNT = 5;
102 |
103 | @Override
104 | public boolean isScrollContainer() {
105 | if (allowInterceptTouchEvent) {
106 | return super.isScrollContainer();
107 | } else {
108 | return false;
109 | }
110 |
111 | }
112 |
113 | private float downX = 0F;
114 |
115 | @SuppressLint("ClickableViewAccessibility")
116 | @Override
117 | public boolean onTouchEvent(MotionEvent e) {
118 | if (!allowInterceptTouchEvent) return false;//[偶现 动画期间 产生了item滑动,这里最后杀手锏再屏蔽下]
119 |
120 | switch (e.getAction()) {
121 | case MotionEvent.ACTION_DOWN:
122 | moveSampling.clear();
123 | downX = e.getRawX();
124 | break;
125 | case MotionEvent.ACTION_MOVE:
126 | float mx = e.getRawX();
127 |
128 | if (moveSampling.size() == 0 || mx != moveSampling.get(moveSampling.size() - 1)) {
129 | moveSampling.add(mx);
130 | }
131 | if (moveSampling.size() > MAX_COUNT) {
132 | moveSampling.remove(0);
133 | }
134 |
135 | break;
136 | case MotionEvent.ACTION_UP:
137 | case MotionEvent.ACTION_CANCEL:
138 | if (moveSampling.size() > 0) {
139 | float lastMoveX = moveSampling.get(moveSampling.size() - 1);
140 | float firstMoveX = moveSampling.get(0);
141 | float finallyMoveX = lastMoveX - firstMoveX;
142 | if (lastMoveX - downX < 0) {//左滑
143 | layoutManager.setAutoLeftScroll(finallyMoveX < 10);
144 | } else {
145 | layoutManager.setAutoLeftScroll(finallyMoveX < 0);
146 | }
147 | moveSampling.clear();
148 | } else {
149 | layoutManager.setAutoLeftScroll(false);
150 | }
151 |
152 | break;
153 | default:
154 | break;
155 | }
156 | return super.onTouchEvent(e);
157 | }
158 |
159 | @Override
160 | public boolean onInterceptTouchEvent(MotionEvent e) {
161 | //交由父类处理滑动,flip = BookFlipMode.MODE_NORMAL,
162 | if (allowInterceptTouchEvent) {
163 | return super.onInterceptTouchEvent(e);
164 | }
165 | //交由子View自行处理,flip = BookFlipMode.MODE_COVER| BookFlipMode.MODE_CURL
166 | return false;
167 | }
168 |
169 | @Override
170 | public void onExpectNext(boolean smooth) {
171 | Adapter adapter = getAdapter();
172 | final int dataCount = adapter.getItemCount();
173 | final int nextPos = currentPosition + 1;
174 |
175 | if (nextPos < dataCount) {
176 | if (smooth) {
177 | smoothScrollToPosition(nextPos);
178 | } else {
179 | scrollToPosition(nextPos);
180 | }
181 | }
182 | }
183 |
184 | @Override
185 | public void onExpectPrevious(boolean smooth) {
186 | if (currentPosition - 1 >= 0) {
187 | if (smooth) {
188 | smoothScrollToPosition(currentPosition - 1);
189 | } else {
190 | scrollToPosition(currentPosition - 1);
191 | }
192 | }
193 |
194 | }
195 |
196 | protected void setFlipMode(int flipMode) {
197 | layoutManager.setBookFlipMode(flipMode);
198 | if (flipMode == BookLayoutManager.BookFlipMode.MODE_CURL || flipMode == BookLayoutManager.BookFlipMode.MODE_COVER) {
199 | allowInterceptTouchEvent = false;
200 | } else {
201 | allowInterceptTouchEvent = true;
202 | }
203 | layoutManager.requestLayout();
204 | }
205 |
206 | @Override
207 | public int getFlipMode() {
208 | return layoutManager.getBookFlipMode();
209 | }
210 |
211 | @Override
212 | public void onItemViewTouchEvent(MotionEvent event) {
213 | if (null != eventProxyWeakReference && null != eventProxyWeakReference.get()) {
214 | eventProxyWeakReference.get().onItemViewTouchEvent(event);
215 | }
216 | }
217 |
218 | @Override
219 | public boolean animRunning() {
220 | if (null != eventProxyWeakReference && null != eventProxyWeakReference.get()) {
221 | eventProxyWeakReference.get().animRunning();
222 | }
223 | return false;
224 | }
225 |
226 | @Override
227 | public void onClickMenu() {
228 | animParentView.onClickMenuArea();
229 | }
230 |
231 | private class ItemOnScrollStop implements BookLayoutManager.OnStopScroller {
232 | @Override
233 | public void onStop(boolean autoLeftScroll, int curPos) {
234 | boolean arriveNext = currentPosition < curPos;
235 | currentPosition = curPos;
236 | if (null != onPositionChangedListener) {
237 | onPositionChangedListener.onChanged(arriveNext, curPos);
238 | }
239 | }
240 |
241 | }
242 |
243 | private class ItemOnForceLayoutCompleted implements BookLayoutManager.OnForceLayoutCompleted {
244 |
245 | @Override
246 | public void onLayoutCompleted(final int curPos) {
247 | boolean arriveNext = currentPosition < curPos;
248 | currentPosition = curPos;
249 | if (null != onPositionChangedListener) {
250 | onPositionChangedListener.onChanged(arriveNext, curPos);
251 | }
252 | }
253 | }
254 |
255 | @Override
256 | public Bitmap getPreviousBitmap() {
257 | int prePos = currentPosition - 1;
258 | Bitmap pb = null;
259 | if (prePos >= 0) {
260 | pb = printViewToBitmap(prePos, 0);
261 | }
262 | return pb;
263 | }
264 |
265 | @Override
266 | public Bitmap getCurrentBitmap() {
267 | return printViewToBitmap(currentPosition, 1);
268 | }
269 |
270 | @Override
271 | public Bitmap getNextBitmap() {
272 | final int dataCount = getAdapter().getItemCount();
273 | int nextPos = currentPosition + 1;
274 | Bitmap nb = null;
275 | if (nextPos < dataCount) {
276 | nb = printViewToBitmap(nextPos, 2);
277 | }
278 | return nb;
279 | }
280 |
281 | /**
282 | * 将view渲染结果 打印到一个bitmap上
283 | *
284 | * @param type 0 前一页,1 当前页,2 后一页
285 | * @return
286 | */
287 | private Bitmap printViewToBitmap(int pos, int type) {
288 | View view = layoutManager.findViewByPosition(pos);
289 | if (null != view) {
290 | if (view instanceof PaperLayout) {
291 | PaperLayout pageView = (PaperLayout) view;
292 | Bitmap bitmapTarget = obtainBitmap(pageView.getWidth(), pageView.getHeight(), type);
293 | pageView.drawViewScreenShotToBitmap(bitmapTarget);
294 | return bitmapTarget;
295 | } else {
296 | throw new IllegalArgumentException("item 根View必须使用 PaperLayout");
297 | }
298 | }
299 | return null;
300 | }
301 |
302 | private Bitmap obtainBitmap(int w, int h, int type) {
303 | Bitmap cache = null;
304 | if (type == 0) {
305 | previousBitmap = previousBitmap != null ? previousBitmap : createNewBitmap(w, h);
306 | cache = previousBitmap;
307 | } else if (type == 1) {
308 | currentBitmap = currentBitmap != null ? currentBitmap : createNewBitmap(w, h);
309 | cache = currentBitmap;
310 | } else if (type == 2) {
311 | nextBitmap = nextBitmap != null ? nextBitmap : createNewBitmap(w, h);
312 | cache = nextBitmap;
313 | }
314 | return cache;
315 | }
316 |
317 | private Bitmap createNewBitmap(int w, int h) {
318 | return Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444);
319 | }
320 |
321 | private void clearBitmapCache() {
322 | if (null != previousBitmap) {
323 | previousBitmap.recycle();
324 | previousBitmap = null;
325 | }
326 | if (null != currentBitmap) {
327 | currentBitmap.recycle();
328 | currentBitmap = null;
329 | }
330 | if (null != nextBitmap) {
331 | nextBitmap.recycle();
332 | nextBitmap = null;
333 | }
334 | }
335 |
336 | }
337 |
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/business/read/view/BookView.java:
--------------------------------------------------------------------------------
1 | package com.juziml.read.business.read.view;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.Color;
6 | import android.util.AttributeSet;
7 | import android.view.Gravity;
8 | import android.view.View;
9 | import android.widget.FrameLayout;
10 |
11 | import androidx.annotation.NonNull;
12 | import androidx.annotation.Nullable;
13 | import androidx.recyclerview.widget.RecyclerView;
14 |
15 | import com.juziml.read.business.read.anim.AnimHelper;
16 |
17 | /**
18 | * create by zhusw on 2020-08-14 17:33
19 | */
20 | public class BookView extends FrameLayout implements AnimParentView {
21 |
22 | private BookRecyclerView bookRecyclerView;
23 | private PuppetView puppetView;
24 | private final AnimHelper animHelper;
25 |
26 | private OnClickMenuListener onClickMenuListener;
27 |
28 | private int itemViewBackgroundColor = Color.WHITE;
29 | private Runnable dataPendIntentTask;
30 |
31 | public BookView(@NonNull Context context) {
32 | this(context, null);
33 | }
34 |
35 | public BookView(@NonNull Context context, @Nullable AttributeSet attrs) {
36 | this(context, attrs, 0);
37 | }
38 |
39 | public BookView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
40 | super(context, attrs, defStyleAttr);
41 | animHelper = new AnimHelper();
42 | init();
43 | }
44 |
45 | private void init() {
46 | removeAllViews();
47 | bookRecyclerView = new BookRecyclerView(getContext());
48 | puppetView = new PuppetView(getContext());
49 | puppetView.setAnimMode(bookRecyclerView.getFlipMode());
50 | bookRecyclerView.bindReadCurlAnimProxy(puppetView);
51 | LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
52 | params.gravity = Gravity.CENTER;
53 | addView(bookRecyclerView, params);
54 |
55 | LayoutParams params2 = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
56 | params2.gravity = Gravity.CENTER;
57 | addView(puppetView, params2);
58 | }
59 |
60 | @Override
61 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
62 | super.onSizeChanged(w, h, oldw, oldh);
63 |
64 | bookRecyclerView.bindReadCurlAnimProxy(puppetView);
65 |
66 | }
67 |
68 | public void setOnPositionChangedListener(OnPositionChangedListener onPositionChangedListener) {
69 | bookRecyclerView.setOnPositionChangedListener(onPositionChangedListener);
70 |
71 | }
72 |
73 | public void setOnClickMenuListener(OnClickMenuListener onClickMenuListener) {
74 | this.onClickMenuListener = onClickMenuListener;
75 | }
76 |
77 | public void scrollToPosition(int position) {
78 | bookRecyclerView.scrollToPosition(position);
79 | }
80 |
81 | public void smoothScrollToPosition(int position) {
82 | bookRecyclerView.smoothScrollToPosition(position);
83 | }
84 |
85 | public void setAdapter(RecyclerView.Adapter adapter) {
86 | bookRecyclerView.setAdapter(adapter);
87 | }
88 |
89 | public void setItemViewBackgroundColor(int itemViewBackgroundColor) {
90 | this.itemViewBackgroundColor = itemViewBackgroundColor;
91 | }
92 |
93 | @Override
94 | protected void onDetachedFromWindow() {
95 | super.onDetachedFromWindow();
96 | if (null != dataPendIntentTask) {
97 | bookRecyclerView.removeCallbacks(dataPendIntentTask);
98 | }
99 | }
100 |
101 | @Override
102 | public void onExpectNext() {
103 | bookRecyclerView.onExpectNext(false);
104 | if (null != dataPendIntentTask) {
105 | bookRecyclerView.post(dataPendIntentTask);
106 | dataPendIntentTask = null;
107 | }
108 | }
109 |
110 | @Override
111 | public void onExpectPrevious() {
112 | bookRecyclerView.onExpectPrevious(false);
113 | if (null != dataPendIntentTask) {
114 | bookRecyclerView.post(dataPendIntentTask);
115 | dataPendIntentTask = null;
116 | }
117 | }
118 |
119 | @Override
120 | public Bitmap getPreviousBitmap() {
121 | return bookRecyclerView.getPreviousBitmap();
122 | }
123 |
124 | @Override
125 | public Bitmap getCurrentBitmap() {
126 | return bookRecyclerView.getCurrentBitmap();
127 | }
128 |
129 | @Override
130 | public Bitmap getNextBitmap() {
131 | return bookRecyclerView.getNextBitmap();
132 | }
133 |
134 | @Override
135 | public int getBackgroundColor() {
136 | return itemViewBackgroundColor;
137 | }
138 |
139 |
140 | public void setFlipMode(@BookLayoutManager.BookFlipMode int flipMode) {
141 | if (flipMode == BookLayoutManager.BookFlipMode.MODE_CURL
142 | || flipMode == BookLayoutManager.BookFlipMode.MODE_COVER) {
143 | puppetView.setVisibility(View.VISIBLE);
144 | } else {
145 | puppetView.setVisibility(View.INVISIBLE);//不可以设置为gone,避免animView 无法获取尺寸
146 | }
147 | bookRecyclerView.setFlipMode(flipMode);
148 | puppetView.setAnimMode(flipMode);
149 |
150 | }
151 |
152 | @Override
153 | public AnimHelper getAnimHelper() {
154 | return animHelper;
155 | }
156 |
157 | @Override
158 | public void onClickMenuArea() {
159 | if (null != onClickMenuListener) {
160 | onClickMenuListener.onClickMenu();
161 | }
162 | }
163 |
164 | /**
165 | * 只在非卷曲模式下调用
166 | */
167 | @Override
168 | public void onClickNextArea() {
169 | bookRecyclerView.onExpectNext(true);
170 | }
171 |
172 | /**
173 | * 只在非卷曲模式下调用
174 | */
175 | @Override
176 | public void onClickPreviousArea() {
177 | bookRecyclerView.onExpectPrevious(true);
178 | }
179 |
180 | public boolean checkAllowChangeData() {
181 | return !puppetView.animRunningOrTouching()
182 | || bookRecyclerView.getFlipMode() == BookLayoutManager.BookFlipMode.MODE_NORMAL;
183 | }
184 |
185 | /**
186 | * @param dataPendIntentTask
187 | */
188 | public void setDataPendIntentTask(Runnable dataPendIntentTask) {
189 | this.dataPendIntentTask = dataPendIntentTask;
190 | }
191 |
192 | public interface OnPositionChangedListener {
193 | void onChanged(boolean arriveNext, int curPosition);
194 | }
195 |
196 | public interface OnClickMenuListener {
197 | void onClickMenu();
198 | }
199 |
200 |
201 | }
202 |
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/business/read/view/EventProxy.java:
--------------------------------------------------------------------------------
1 | package com.juziml.read.business.read.view;
2 |
3 | import android.view.MotionEvent;
4 |
5 | /**
6 | * create by zhusw on 2020-08-14 18:37
7 | */
8 | public interface EventProxy {
9 | boolean onItemViewTouchEvent(MotionEvent event);
10 |
11 | boolean animRunning();
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/business/read/view/FPoint.java:
--------------------------------------------------------------------------------
1 | package com.juziml.read.business.read.view;
2 |
3 | /**
4 | * create by zhusw on 2020-07-30 17:05
5 | */
6 | public class FPoint {
7 | public float x, y;
8 |
9 | public FPoint() {
10 | }
11 |
12 | public FPoint(float x, float y) {
13 | this.x = x;
14 | this.y = y;
15 | }
16 |
17 | public void setXY(float x, float y) {
18 | this.x = x;
19 | this.y = y;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/business/read/view/PaperLayout.java:
--------------------------------------------------------------------------------
1 | package com.juziml.read.business.read.view;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.graphics.Bitmap;
6 | import android.graphics.Canvas;
7 | import android.graphics.RectF;
8 | import android.util.AttributeSet;
9 | import android.view.MotionEvent;
10 | import android.widget.LinearLayout;
11 |
12 | import androidx.annotation.NonNull;
13 | import androidx.annotation.Nullable;
14 |
15 | /**
16 | * 作为根布局使用,最好再填充一个ViewGroup并设置match_parent
17 | * 必须开启硬件加速,否则掉帧
18 | * create by zhusw on 2020-07-28 16:00
19 | */
20 | public class PaperLayout extends LinearLayout {
21 | private final Canvas viewScreenShotCanvas;
22 |
23 | private BookRecyclerView bookRecyclerView;
24 | private final RectF menuBounds;
25 |
26 | public PaperLayout(@NonNull Context context) {
27 | this(context, null);
28 | }
29 |
30 | public PaperLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
31 | this(context, attrs, 0);
32 | }
33 |
34 | public PaperLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
35 | super(context, attrs, defStyleAttr);
36 | viewScreenShotCanvas = new Canvas();
37 | menuBounds = new RectF();
38 | }
39 |
40 | @Override
41 | protected void onAttachedToWindow() {
42 | super.onAttachedToWindow();
43 | bookRecyclerView = (BookRecyclerView) getParent();
44 |
45 | }
46 |
47 | @Override
48 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
49 | super.onSizeChanged(w, h, oldw, oldh);
50 | if (bookRecyclerView.getFlipMode() == BookLayoutManager.BookFlipMode.MODE_CURL
51 | || bookRecyclerView.getFlipMode() == BookLayoutManager.BookFlipMode.MODE_COVER) {
52 | requestDisallowInterceptTouchEvent(true);
53 | } else {
54 | requestDisallowInterceptTouchEvent(false);
55 | }
56 | menuBounds.left = getWidth() / 3F;
57 | menuBounds.top = getHeight() / 3F;
58 | menuBounds.right = getWidth() * 2 / 3F;
59 | menuBounds.bottom = getHeight() * 2 / 3F;
60 | }
61 |
62 | private int offset = -2;
63 |
64 | /**
65 | * 这个会被调用多次,最终宽度为实际测量宽度-2px
66 | * 这样在 layoutManager 进行布局时 才可以同时保持3个item被显示
67 | *
68 | * @param widthMeasureSpec
69 | * @param heightMeasureSpec
70 | */
71 | @Override
72 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
73 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
74 | int height = measureSize(5, heightMeasureSpec);
75 | int width = measureSize(5, widthMeasureSpec);
76 | setMeasuredDimension(width + offset, height);
77 | }
78 |
79 |
80 | private int measureSize(int defaultSize, int measureSpec) {
81 | int result = defaultSize;
82 | int specMode = MeasureSpec.getMode(measureSpec);
83 | int specSize = MeasureSpec.getSize(measureSpec);
84 |
85 | if (specMode == MeasureSpec.EXACTLY) {
86 | result = specSize;
87 | } else if (specMode == MeasureSpec.AT_MOST) {
88 | result = Math.min(result, specSize);
89 | }
90 | return result;
91 | }
92 |
93 | public Bitmap drawViewScreenShotToBitmap(Bitmap bitmap) {
94 | viewScreenShotCanvas.setBitmap(bitmap);
95 | draw(viewScreenShotCanvas);
96 | return bitmap;
97 | }
98 |
99 | @Override
100 | public boolean isScrollContainer() {
101 | return false;
102 | }
103 |
104 |
105 | private float interceptDownX;
106 |
107 | @Override
108 | public boolean onInterceptTouchEvent(MotionEvent ev) {
109 | //动画执行期间 子view 也不可获取事件
110 | if (bookRecyclerView.animRunning()) return true;
111 | if (ev.getAction() == MotionEvent.ACTION_DOWN) {
112 | interceptDownX = ev.getRawX();
113 | } else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
114 | float currentX = ev.getRawX();
115 | float distance = Math.abs(currentX - interceptDownX);
116 | if (distance > 1F) {
117 | return true;
118 | }
119 | }
120 | return super.onInterceptTouchEvent(ev);
121 | }
122 |
123 | private float receiveDownX = -1;
124 | private float downX = -1;
125 | private float downY = -1;
126 |
127 | @SuppressLint("ClickableViewAccessibility")
128 | @Override
129 | public boolean onTouchEvent(MotionEvent ev) {
130 | if (ev.getAction() == MotionEvent.ACTION_DOWN) {
131 | receiveDownX = ev.getRawX();
132 | downX = ev.getRawX();
133 | downY = ev.getRawY();
134 | } else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
135 | if (receiveDownX == -1) {
136 | ev.setAction(MotionEvent.ACTION_DOWN);
137 | receiveDownX = ev.getRawX();
138 | }
139 | } else if (ev.getAction() == MotionEvent.ACTION_UP
140 | || ev.getAction() == MotionEvent.ACTION_CANCEL) {
141 | receiveDownX = -1;
142 | }
143 | if (bookRecyclerView.getFlipMode() == BookLayoutManager.BookFlipMode.MODE_NORMAL) {
144 | if (ev.getAction() == MotionEvent.ACTION_UP) {
145 | float upX = ev.getRawX();
146 | float upY = ev.getRawY();
147 | if (downX > menuBounds.left
148 | && downY > menuBounds.top
149 | && downX < menuBounds.right
150 | && downY < menuBounds.bottom
151 | && upX > menuBounds.left
152 | && upY > menuBounds.top
153 | && upX < menuBounds.right
154 | && upY < menuBounds.bottom) {
155 | bookRecyclerView.onClickMenu();
156 | } else if (upX >= getWidth() / 2F) {
157 | bookRecyclerView.onExpectNext(true);
158 | } else if (upX < getWidth() / 2F) {
159 | bookRecyclerView.onExpectPrevious(true);
160 | }
161 | }
162 | } else {
163 | bookRecyclerView.onItemViewTouchEvent(ev);
164 | }
165 | return true;
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/business/read/view/PuppetView.java:
--------------------------------------------------------------------------------
1 | package com.juziml.read.business.read.view;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.Canvas;
6 | import android.util.AttributeSet;
7 | import android.view.MotionEvent;
8 | import android.view.View;
9 | import android.view.ViewParent;
10 |
11 | import androidx.annotation.NonNull;
12 | import androidx.annotation.Nullable;
13 |
14 | import com.juziml.read.business.read.anim.AnimHelper;
15 | import com.juziml.read.business.read.anim.CoverAnimationEffecter;
16 | import com.juziml.read.business.read.anim.IAnimationEffecter;
17 | import com.juziml.read.business.read.anim.SimulationAnimationEffecter;
18 |
19 |
20 | /**
21 | * 此View的作用就像幕后一样,负责接受事件并传递到动画,Effecter
22 | * create by zhusw on 2020-08-14 17:37
23 | */
24 | public class PuppetView extends View implements EventProxy, AnimParentView {
25 |
26 | IAnimationEffecter animationEffecter;
27 | AnimParentView parentView;
28 | private Bitmap previousViewBitmap;
29 | private Bitmap currentViewBitmap;
30 | private Bitmap nextViewBitmap;
31 | boolean performDrawCurlTexture = false;
32 | private int vWidth, vHeight;
33 |
34 | public PuppetView(@NonNull Context context) {
35 | this(context, null);
36 | }
37 |
38 | public PuppetView(@NonNull Context context, @Nullable AttributeSet attrs) {
39 | this(context, attrs, 0);
40 | }
41 |
42 | public PuppetView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
43 | super(context, attrs, defStyleAttr);
44 | /*
45 | view 可以单独关,但是不能单独打开硬件加速
46 | 关闭硬件加速 卡到爆炸
47 | 开启硬件加速,诱发 OpenGLRenderer: Path too large to be rendered into a texture
48 | setLayerType(LAYER_TYPE_SOFTWARE,null);
49 | */
50 | }
51 |
52 | public boolean animRunningOrTouching() {
53 | boolean animRunningOrTouching = false;
54 | if (null != animationEffecter) {
55 | animRunningOrTouching = animRunning();
56 | }
57 | return animRunningOrTouching;
58 | }
59 |
60 | @Override
61 | protected void onAttachedToWindow() {
62 | super.onAttachedToWindow();
63 | ViewParent viewParent = getParent();
64 | parentView = (AnimParentView) viewParent;
65 | if (null != animationEffecter) {
66 | animationEffecter.onViewAttachedToWindow();
67 | }
68 | }
69 |
70 |
71 | public void setAnimMode(int animMode) {
72 | //重置某些属性 与变量
73 | animationEffecter = null;
74 | if (animMode == BookLayoutManager.BookFlipMode.MODE_COVER) {
75 | animationEffecter = new CoverAnimationEffecter(this);
76 | } else if (animMode == BookLayoutManager.BookFlipMode.MODE_CURL) {
77 | animationEffecter = new SimulationAnimationEffecter(this);
78 | }
79 | if (null != animationEffecter) {
80 | animationEffecter.onViewSizeChanged(vWidth, vHeight);
81 | }
82 | }
83 |
84 | @Override
85 | public void draw(Canvas canvas) {
86 | if (performDrawCurlTexture && null != animationEffecter) {
87 | animationEffecter.draw(canvas);
88 | } else {
89 | super.draw(canvas);
90 | }
91 | }
92 |
93 | @Override
94 | protected void onDetachedFromWindow() {
95 | super.onDetachedFromWindow();
96 | if (null != animationEffecter) {
97 | animationEffecter.onViewDetachedFromWindow();
98 | }
99 | }
100 |
101 | /**
102 | * 这个会被调用多次,最终宽度为实际测量宽度-2px
103 | * 这样在 layoutmanager 进行布局时 才可以同时保持3个item被显示
104 | *
105 | * @param widthMeasureSpec
106 | * @param heightMeasureSpec
107 | */
108 | @Override
109 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
110 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
111 | int height = measureSize(5, heightMeasureSpec);
112 | int width = measureSize(5, widthMeasureSpec) - 2;
113 | setMeasuredDimension(width - 2, height);
114 | vWidth = width;
115 | vHeight = height;
116 | if (null != animationEffecter) {
117 | animationEffecter.onViewSizeChanged(vWidth, vHeight);
118 | }
119 | }
120 |
121 |
122 | private int measureSize(int defaultSize, int measureSpec) {
123 | int result = defaultSize;
124 | int specMode = MeasureSpec.getMode(measureSpec);
125 | int specSize = MeasureSpec.getSize(measureSpec);
126 | if (specMode == MeasureSpec.EXACTLY) {
127 | result = specSize;
128 | } else if (specMode == MeasureSpec.AT_MOST) {
129 | result = Math.min(result, specSize);
130 | }
131 | return result;
132 | }
133 |
134 |
135 | public void buildBitmap(int slideDirection) {
136 | performDrawCurlTexture = false;
137 | if (slideDirection == AnimHelper.SLID_DIRECTION_LEFT) {
138 | currentViewBitmap = parentView.getCurrentBitmap();
139 | nextViewBitmap = parentView.getNextBitmap();
140 | } else if (slideDirection == AnimHelper.SLID_DIRECTION_RIGHT) {
141 | previousViewBitmap = parentView.getPreviousBitmap();
142 | }
143 | performDrawCurlTexture = true;
144 | }
145 |
146 |
147 | @Override
148 | public boolean onItemViewTouchEvent(MotionEvent event) {
149 | if (null != animationEffecter) {
150 | animationEffecter.handlerEvent(event);
151 | }
152 | return true;
153 | }
154 |
155 | @Override
156 | public boolean animRunning() {
157 | if (null != animationEffecter) {
158 | return animationEffecter.animInEffect();
159 | }
160 | return false;
161 | }
162 |
163 | @Override
164 | public void computeScroll() {
165 | if (null != animationEffecter) {
166 | animationEffecter.onScroll();
167 | }
168 | }
169 |
170 | @Override
171 | public void onExpectNext() {
172 | parentView.onExpectNext();
173 | }
174 |
175 | @Override
176 | public void onExpectPrevious() {
177 | parentView.onExpectPrevious();
178 | }
179 |
180 | @Override
181 | public Bitmap getPreviousBitmap() {
182 | return previousViewBitmap;
183 | }
184 |
185 | @Override
186 | public Bitmap getCurrentBitmap() {
187 | return currentViewBitmap;
188 | }
189 |
190 | @Override
191 | public Bitmap getNextBitmap() {
192 | return nextViewBitmap;
193 | }
194 |
195 | @Override
196 | public int getBackgroundColor() {
197 | return parentView.getBackgroundColor();
198 | }
199 |
200 | @Override
201 | public AnimHelper getAnimHelper() {
202 | return parentView.getAnimHelper();
203 | }
204 |
205 | @Override
206 | public void onClickMenuArea() {
207 | parentView.onClickMenuArea();
208 | }
209 |
210 | @Override
211 | public void onClickNextArea() {
212 | parentView.onClickNextArea();
213 | }
214 |
215 | @Override
216 | public void onClickPreviousArea() {
217 | parentView.onClickPreviousArea();
218 | }
219 |
220 | public void reset() {
221 | previousViewBitmap = null;
222 | nextViewBitmap = null;
223 | currentViewBitmap = null;
224 | performDrawCurlTexture = false;
225 | }
226 |
227 | }
228 |
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/business/read/view/RVInnerItemFunction.java:
--------------------------------------------------------------------------------
1 | package com.juziml.read.business.read.view;
2 |
3 | import android.view.MotionEvent;
4 |
5 | /**
6 | * -recyclerview 内部功能接口
7 | * create by zhusw on 2020-08-17 10:07
8 | */
9 | public interface RVInnerItemFunction {
10 | void onItemViewTouchEvent(MotionEvent event);
11 |
12 | boolean animRunning();
13 |
14 | void onClickMenu();
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/business/read/view/RVOuterFunction.java:
--------------------------------------------------------------------------------
1 | package com.juziml.read.business.read.view;
2 |
3 | import android.graphics.Bitmap;
4 |
5 | /**
6 | * create by zhusw on 2020-08-17 10:15
7 | */
8 | public interface RVOuterFunction {
9 | void onExpectNext(boolean smooth);
10 |
11 | void onExpectPrevious(boolean smooth);
12 |
13 | Bitmap getPreviousBitmap();
14 |
15 | Bitmap getCurrentBitmap();
16 |
17 | Bitmap getNextBitmap();
18 |
19 | int getFlipMode();
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/core/App.java:
--------------------------------------------------------------------------------
1 | package com.juziml.read.core;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.annotation.NonNull;
6 |
7 | import com.bumptech.glide.Glide;
8 |
9 | /**
10 | * Created by victor on 2020/9/10.
11 | * Email : victorhhl@163.com
12 | * Description :
13 | */
14 | public class App extends ArchApplication {
15 |
16 | public static App instance;
17 |
18 | @Override
19 | protected void attachBaseContext(Context base) {
20 | super.attachBaseContext(base);
21 | instance = this;
22 | }
23 |
24 | /**
25 | * 获取Application实例
26 | */
27 | @NonNull
28 | public static App get() {
29 | return instance;
30 | }
31 |
32 | @Override
33 | public void onTrimMemory(int level) {
34 | super.onTrimMemory(level);
35 | Glide.get(this).trimMemory(level);
36 | }
37 |
38 | @Override
39 | public void onLowMemory() {
40 | super.onLowMemory();
41 | Glide.get(this).onLowMemory();
42 | System.gc();
43 | }
44 | }
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/core/ArchApplication.kt:
--------------------------------------------------------------------------------
1 | package com.juziml.read.core
2 |
3 | import android.app.Application
4 | import android.content.Intent
5 | import android.os.Bundle
6 |
7 | /**
8 | * create by zhusw on 6/7/21 14:16
9 | */
10 | open class ArchApplication : Application() {
11 | init {
12 | @Suppress("LeakingThis")//里面什么都没做,所以抑制警告
13 | (ArchConfig.preInitApplication(this))
14 | }
15 |
16 |
17 | override fun onCreate() {
18 | super.onCreate()
19 | //ArchConfig 需要晚于Utils 初始化
20 | ArchConfig.initApplicationOnCreate()
21 |
22 | }
23 |
24 | override fun startActivity(intent: Intent) {
25 | startActivity(intent, null)
26 | }
27 |
28 | override fun startActivity(intent: Intent, options: Bundle?) {
29 | //默认增加new task标识
30 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
31 | super.startActivity(intent, options)
32 | }
33 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/core/ArchConfig.kt:
--------------------------------------------------------------------------------
1 | package com.juziml.read.core
2 |
3 | import android.app.Application
4 | import android.content.ContentProvider
5 | import android.content.pm.PackageInfo
6 | import android.content.pm.PackageManager
7 | import com.juziml.read.BuildConfig
8 |
9 | /**
10 | * 基础架构 配置类
11 | * create by zhusw on 5/24/21 17:20
12 | */
13 | object ArchConfig {
14 |
15 | private var _application: Application? = null
16 |
17 | @JvmStatic
18 | val app: Application
19 | get() = _application!!
20 |
21 | /**
22 | * 预初始化,Application构造调用,这样可以保证任何情况都不为null([ContentProvider])
23 | */
24 | @JvmStatic
25 | fun preInitApplication(app: Application) {
26 | _application = app
27 | //此处不允许有逻辑
28 | }
29 |
30 | @JvmStatic
31 | fun initApplicationOnCreate() {
32 | }
33 |
34 | /**
35 | * 记录:非const无法被编译优化掉(开启混淆后可被混淆规则优化掉)
36 | */
37 | @JvmField
38 | val debug: Boolean = BuildConfig.DEBUG
39 |
40 | private fun getHostVersionName(): String {
41 | return try {
42 | val pm: PackageManager = app.packageManager
43 | val pi: PackageInfo = pm.getPackageInfo(
44 | app.packageName, 0
45 | )
46 | pi.versionName ?: "unknown"
47 | } catch (e: PackageManager.NameNotFoundException) {
48 | e.printStackTrace()
49 | "unknown"
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/utils/DLog.java:
--------------------------------------------------------------------------------
1 | package com.juziml.read.utils;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * create by zhusw on 2020-03-27 15:46
7 | */
8 | public class DLog {
9 |
10 | public static void log(String rules, Object... args) {
11 | Log.i("DLog:", String.format(rules, args));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/juziml/read/utils/TraceUtils.java:
--------------------------------------------------------------------------------
1 | package com.juziml.read.utils;
2 |
3 | import android.util.Log;
4 |
5 | import com.juziml.read.BuildConfig;
6 |
7 | /**
8 | * create by zhusw on 2020-08-13 13:43
9 | */
10 | public class TraceUtils {
11 | private static String msg = null;
12 | private static long time = 0;
13 |
14 | public static void startTrace(String format, Object... o) {
15 | if (BuildConfig.DEBUG) {
16 | msg = String.format(format, o);
17 | time = System.currentTimeMillis();
18 | }
19 | }
20 |
21 | public static void endTrace() {
22 | if (BuildConfig.DEBUG) {
23 | String content = String.format("TAG:%s time:%s", msg, System.currentTimeMillis() - time);
24 | Log.i("TraceUtils", content);
25 | time = 0l;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_add_chat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/app/src/main/res/drawable-xxhdpi/ic_add_chat.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_chat_gpt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/app/src/main/res/drawable-xxhdpi/ic_chat_gpt.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_chat_sakura_flower.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/app/src/main/res/drawable-xxhdpi/ic_chat_sakura_flower.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_close_pink_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/app/src/main/res/drawable-xxhdpi/ic_close_pink_128.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_empty_new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/app/src/main/res/drawable-xxhdpi/ic_empty_new.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_head.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/app/src/main/res/drawable-xxhdpi/ic_head.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_launcher_20.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
15 |
18 |
21 |
24 |
27 |
30 |
33 |
36 |
39 |
42 |
45 |
48 |
51 |
54 |
55 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_loading_cycle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/app/src/main/res/drawable-xxhdpi/ic_loading_cycle.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_send.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/app/src/main/res/drawable-xxhdpi/ic_send.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_voice_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/app/src/main/res/drawable-xxhdpi/ic_voice_dark.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_voice_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/app/src/main/res/drawable-xxhdpi/ic_voice_light.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/icon_net_error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/app/src/main/res/drawable-xxhdpi/icon_net_error.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_app_window.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
15 |
18 |
21 |
24 |
27 |
30 |
33 |
36 |
39 |
42 |
45 |
48 |
51 |
54 |
55 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shadow.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/sp_chat_item_item_bg_bot.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/sp_chat_item_item_bg_user.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/sp_chat_session_list_item_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/sp_item_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/test_img.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/app/src/main/res/drawable/test_img.jpg
--------------------------------------------------------------------------------
/app/src/main/res/layout/act_simple.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
17 |
18 |
25 |
26 |
27 |
34 |
35 |
42 |
43 |
44 |
45 |
51 |
52 |
59 |
60 |
67 |
68 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_read.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
25 |
26 |
46 |
47 |
61 |
62 |
66 |
67 |
74 |
75 |
80 |
81 |
86 |
87 |
88 |
99 |
100 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #5aa5c5
4 | #4e9cbf
5 | #E5738E
6 | #79b9d2
7 |
8 | #1E445E
9 |
10 | @color/colorPrimary
11 | @color/colorPrimary
12 |
13 |
14 | #f0f0f0
15 | #C2C2C2
16 |
17 | #333333
18 | #666666
19 | #FFFFFF
20 | #999999
21 | #f0f0f0
22 | #aa305C79
23 |
24 | #FFF
25 | #00aaff
26 |
27 | #412903
28 | #ead0ae
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | 长文本长文本长长文长文本长文本长长文本长文本长文本长文本文本文本长文本长文本文本长文本本长文本长文本长文本文本长文本
3 | 长文
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
22 |
23 |
26 |
27 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/release/res/xml/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/test.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/app/test.jks
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | import com.buildsrc.kts.GlobalConfig
2 |
3 | //buildSrc的初始化init
4 | GlobalConfig.init(project)
5 |
6 | buildscript {
7 |
8 | repositories {
9 | com.buildsrc.kts.Repositories.defRepositories(it)
10 | }
11 |
12 | dependencies {
13 | //recofix 引起processManifest错误 只能使用老版本编译
14 | classpath 'com.android.tools.build:gradle:4.1.0'
15 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${com.buildsrc.kts.Dependencies.Kotlin.version}"
16 | }
17 | }
18 |
19 |
20 | allprojects {
21 | repositories {
22 | com.buildsrc.kts.Repositories.defRepositories(it)
23 | }
24 | }
25 |
26 | task clean(type: Delete) {
27 | delete rootProject.buildDir
28 | }
29 |
--------------------------------------------------------------------------------
/buildSrc/build.gradle.kts:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | //阿里镜像地址:https://developer.aliyun.com/mvn/guide
4 | maven { setUrl("https://maven.aliyun.com/nexus/content/groups/public/") }
5 | maven { setUrl("https://maven.aliyun.com/repository/google") }
6 | }
7 | }
8 |
9 | plugins {
10 | `kotlin-dsl`
11 | id("org.jetbrains.kotlin.jvm") version "1.6.21"
12 | }
13 |
14 | repositories {
15 | maven { setUrl("https://maven.aliyun.com/nexus/content/groups/public/") }
16 | maven { setUrl("https://maven.aliyun.com/repository/google") }
17 | }
18 | dependencies {
19 | implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
20 | implementation("com.android.tools.build:gradle:4.1.0")
21 | implementation(localGroovy())
22 | }
23 | java {
24 | sourceCompatibility = JavaVersion.VERSION_1_8
25 | targetCompatibility = JavaVersion.VERSION_1_8
26 | }
27 |
--------------------------------------------------------------------------------
/buildSrc/src/main/java/com/buildsrc/kts/AndroidConfig.kt:
--------------------------------------------------------------------------------
1 | package com.buildsrc.kts
2 |
3 | import org.gradle.api.JavaVersion
4 |
5 | object AndroidConfig {
6 | private val gradleProperties = PropertiesUtils.gradleProperties
7 |
8 | const val compileSdkVersion = 31
9 | const val minSdkVersion = 24
10 | const val targetSdkVersion = 31
11 |
12 |
13 | object AppInfo {
14 | @JvmStatic
15 | val versionName = gradleProperties["version.name"].toString()
16 |
17 | @JvmStatic
18 | val versionCode = gradleProperties["version.code"].toString().toInt()
19 |
20 | @JvmStatic
21 | val applicationId = gradleProperties["app.id"].toString()
22 |
23 | const val appName = "Sakura"
24 |
25 | @JvmStatic
26 | val releaseObfuscate = gradleProperties["switch.release.obfuscate"].toString().toBoolean()
27 |
28 | @JvmStatic
29 | val debugProguardObfuscate =
30 | gradleProperties["switch.debugProguard.obfuscate"].toString().toBoolean()
31 | }
32 |
33 | object Language {
34 | const val jvmTarget = "1.8"
35 | val sourceCompatibility = JavaVersion.VERSION_1_8
36 | val targetCompatibility = JavaVersion.VERSION_1_8
37 | }
38 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/java/com/buildsrc/kts/DebugThirdSdkConfig.kt:
--------------------------------------------------------------------------------
1 | package com.buildsrc.kts
2 |
3 | /**
4 | * create by zhusw on 8/5/21 19:09
5 | */
6 | object DebugThirdSdkConfig {
7 |
8 | object UMeng {
9 | const val appKey = "624e9ed96adb343c47f85910"
10 | const val appMessageSecret = "2d0da5a6d87a0d9bb4eed9fa4f740908"
11 | }
12 |
13 | object ALi {
14 | const val appKey = "333485384"
15 | const val appSecret = "0372e7363be74dc3bdd7d60810898af4"
16 | const val sophixStuRsaSecret =
17 | "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDzUz+VRAF15ltVBrHiDmNRo7mrRP8wubiLnD/X/+cwJwiq1dWzTx2+chem7PWboBdyiN5s+3fjXcwgJ8cxLqM9tVTk5gr7TUvk+93VInG7LbfQmaWv/Xy4kc57gkFDry5FpRLnR1qbwxTUSjXENQAVO7n+6rGR2Li7LJeDz99RIDhZfmvIG/yNVv89bf1RKDLmz+vPB7SQj2W4m5px0/S0TJuRnL3Kkz87AEnoGF39R2aF3aqIbesnLF1ETy4Cui2U++pM2+olvdQhBc4ygKAkdTnpXnNZJOCRBRNhtt/hYUR/81/osO6WnTAok/fnn4/r7lPhnpP/gA8WQMxR8W1vAgMBAAECggEAOuCf+AxWliLBYteV328/1Rda6/hsD2xBHXcXc7q2RE6NqFjJtvOj7NtKodegrqGUSuCXgaNvpRKjO/ykFGCGm33vJtwLODfB74yKZvJIttQfMFidYPJjZX3Sisw/4pLXDX8eDdyULjCML7otPGbeuESVgvtjMDlSqefmWbzI+qXqcgIHknR2t1EkkFpSO2aW506aiMt/VSZrfHMOybs9N9oceUx1UO/BT24/tOWEnR4EfgdL0vfAko/8sTebV2iTaRw0HYe6KpzVgKosFB77kYL+A+N8k1RBFN5Ohc9kvgTh7RzF3Fi9duWqzajYv7p3IbdjIkZGXHmzpttlR4TbwQKBgQD8Rmxl2ZJ/BkvYFsyMFa4kIiqof9qhnDHkBv0cupwXSMXYjPyC7Az3ZUgVC14wA/mBBRN+2xv3l3an1abNjYVBrgUzBne1P1IBGs7+NGDJ0B2WdxG4jj5U0x6dKox20wYizISslRPnVoXozDOfXS2JzkaYMo7r3aXLDosFbcddkQKBgQD26v7BQB/k+iW6B9Ciw64yDxs1Tn9e0UYWrqPUpo7UXLe8NxMguhaIa8tmDGUMQaXyvWUduZiJhvvAET94ArhQgxuRPudLfsPmb8OaIkzqDG5gNjCyi90k05MLDfgp4XvWu0yO0yKymdRJ/KcDtvsyDg1sCdQDZOkl1Us6p/Ka/wKBgQDTElUFjlmM4Aa7ILA5kLYn01JfssGJL/dqMxK5Y8TPknq+aY+oNrnOkDYULXO7tsed1FJwdADdUiPb3t115C+XOXNuMr4broP9GqIPfGFy2Sa4Sp1QDetG63EUzJHDpWHf4cAr6XtCtljJ43hZoQtF3omgM8TywXtC/sMAYs5IkQKBgQC0Eq9V2mEoSbIH2Lk0asb4ZQHvv16ulKqr0hNS+2FAyJmKnxPJE01pkkuVEccDyECxJaS/p+HxskqZSTrjWAB5XxRUue5OpZVS0gFxwEAdlJFOg1bCb7QIrnAXaMmqKXJ6KMBtjNdKL1ud1a8ry1+ooozxVL2U+Bv8ZnBlx6PrmwKBgQChoioA3D9qMON8BKXSuQ45vRNHTm6oO8zSX4PEilQmjOhUc0WgyjJmDmKuVUmpHwzGuZfkLDTtg2YtQFVH6IvkZf4o8WHKDdEIkYK5CkiMlveiGfgX2wGeCusKK7yKwjEAyenS0J/MtJUrokc8ihrepvdlvx+wFLZrrJKnvIcTNw=="
18 |
19 | //也叫:apm/tlog.rsaSecret
20 | const val apmRsaPublicKey =
21 | "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCmPOP3M0//QRCl8r3jua+mhSC2/pXio0+9X/CZPjh0MnQotMTCXHuE9gUb8/4qLhfJn/Sn4XMOkVfzsMEPs+7NiM5UpOQz4erd3mle/m2lnJo7ONaf+Jm5f0jwa1Zpw+ajZiDICjAAV4n7lnR9zVT6kM4vJjN09xECDhn52yPfYQIDAQAB"
22 | }
23 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/java/com/buildsrc/kts/Dependencies.kt:
--------------------------------------------------------------------------------
1 | package com.buildsrc.kts
2 |
3 | import org.gradle.api.artifacts.dsl.DependencyHandler
4 |
5 | /**
6 | *@Desc:
7 | *-
8 | *-依赖声明
9 | *create by zhusw on 5/6/21 15:45
10 | */
11 | object Dependencies {
12 |
13 | object Kotlin {
14 | const val version = "1.6.21"
15 |
16 | /**
17 | * kotlin 语言核心库,像 let,isEmpty 这种工具函数
18 | */
19 | const val kotlin_stdlib = "org.jetbrains.kotlin:kotlin-stdlib:$version"
20 |
21 | }
22 |
23 | object FlutterModule {
24 | const val recommendCustomerListPage =
25 | "com.mj.flutter.rcp:flutter_release:1.0.4"
26 | }
27 |
28 | object Foundation {
29 | const val activityFragment = "com.foundation.app:activity-fragment:1.1.1"
30 | const val rv_adapter = "com.foundation.widget:convenient-recyclerview-adapter:1.1.1"
31 | const val view_binding_helper = "com.foundation.widget:view-binding-helper:1.0.6"
32 |
33 | }
34 |
35 | object AndroidX {
36 | private const val roomVersion = "2.3.0"
37 |
38 | /**
39 | * kotlin 标准库,各种拓展方法,像 foreach什么的
40 | */
41 | const val core_ktx = "androidx.core:core-ktx:1.3.2"
42 | const val appcompat = "androidx.appcompat:appcompat:1.3.0"
43 | const val constraintLayout = "androidx.constraintlayout:constraintlayout:2.1.4"
44 | const val room_runtime = "androidx.room:room-runtime:$roomVersion"
45 | const val room_ktx = "androidx.room:room-ktx:$roomVersion"
46 | const val room_compiler = "androidx.room:room-compiler:$roomVersion"
47 | }
48 |
49 |
50 | object Material {
51 | /**
52 | * Material 样式库,包含对应的view widget 与 style
53 | */
54 | const val material = "com.google.android.material:material:1.3.0"
55 |
56 | }
57 |
58 | object OpenSourceLibrary {
59 |
60 | const val flex_box = "com.google.android:flexbox:2.0.1"
61 | const val materialEdittext = "com.rengwuxian.materialedittext:library:2.1.4"
62 | const val banner = "com.github.zhpanvip:BannerViewPager:3.5.0"
63 | private const val smartVersion = "2.0.5"
64 | const val smartRefreshLayout = "io.github.scwang90:refresh-layout-kernel:$smartVersion"
65 | const val smartRefreshLayoutHeader =
66 | "io.github.scwang90:refresh-header-material:$smartVersion"
67 | const val smartRefreshLayoutFooter =
68 | "io.github.scwang90:refresh-footer-classics:$smartVersion"
69 |
70 | const val glide = "com.github.bumptech.glide:glide:4.13.2"
71 | const val aliyun_file_upload = "com.aliyun.dpa:oss-android-sdk:2.9.5"
72 | const val live_event_bus_x = "io.github.jeremyliao:live-event-bus-x:1.8.0"
73 |
74 | const val chunkDebug = "com.github.chuckerteam.chucker:library:3.5.0"
75 | const val chunkRelease = "com.github.chuckerteam.chucker:library-no-op:3.5.0"
76 | const val leakCanary = "com.squareup.leakcanary:leakcanary-android:2.7"
77 |
78 | const val retorifit = "com.squareup.retrofit2:retrofit:2.9.0"//依赖okhttp 3.14.9
79 | const val retorifitAdapterRxJava =
80 | "com.squareup.retrofit2:adapter-rxjava:2.9.0"//retorifit支持RxJava
81 |
82 | const val junit = "junit:junit:4.13.2"
83 | const val eventBus = "org.greenrobot:eventbus:3.3.1"
84 | const val rxAndroid = "io.reactivex:rxandroid:1.2.1"
85 |
86 | //新版为2.3.0,api改动较大暂不升级
87 | const val photoView = "com.github.chrisbanes:PhotoView:1.3.1"
88 |
89 | //城市选择滑动控件,和module重复,待后期合并删除,新版4.1.9,改动较大并且于19年停止维护
90 | const val pickerView = "com.contrarywind:Android-PickerView:3.2.7"
91 | const val zxingLibrary = "cn.yipianfengye.android:zxing-library:2.2"
92 | const val XTabLayout = "com.androidkun:XTabLayout:1.1.5"
93 |
94 | //萤石直播依赖库,4.19.9改动较大暂不升级
95 | const val ezviz = "io.github.ezviz-open:ezviz-sdk:4.16.6.3"
96 |
97 | //视频压缩
98 | const val videoProcessor = "com.github.yellowcath:VideoProcessor:2.4.2"
99 |
100 | //数据库工具类,分类用到(无用代码,待删除)
101 | const val litepal = "org.litepal.android:core:1.6.1"
102 |
103 | //饺子视屏播放
104 | const val jiaoZiVideoPlayer = "cn.jzvd:jiaozivideoplayer:7.7.2.3300"
105 |
106 | //pdf预览
107 | const val pdfViewPager = "es.voghdev.pdfviewpager:library:1.1.3"
108 |
109 | //微信
110 | const val WeChat = "com.tencent.mm.opensdk:wechat-sdk-android-without-mta:6.8.0"
111 |
112 | //神策
113 | const val sensors = "com.sensorsdata.analytics.android:SensorsAnalyticsSDK:6.4.2"
114 |
115 | //侧滑删除库
116 | const val swipeLayout = "com.daimajia.swipelayout:library:1.2.0@aar"
117 |
118 | const val unpeekLiveData = "com.kunminx.arch:unpeek-livedata:6.1.0-beta1"
119 | const val chadAdapter = "com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.30"
120 | const val recyclerView = "androidx.recyclerview:recyclerview:1.2.0"
121 | }
122 |
123 | object Chucker {
124 | const val chuckerDebug = "com.github.chuckerteam.chucker:library:3.5.2"
125 | const val chuckerRelease = "com.github.chuckerteam.chucker:library-no-op:3.5.2"
126 | }
127 |
128 | object ButterKnife {
129 | const val butterKnife = "com.jakewharton:butterknife:10.2.3"
130 | const val butterKnifeCompiler = "com.jakewharton:butterknife-compiler:10.2.3"
131 | }
132 |
133 | object Google {
134 | //谷歌更强大的flowLayout>FlexboxLayout和对应的Manager
135 | const val flexBox = "com.google.android:flexbox:2.0.1"
136 | }
137 |
138 | object AMap {
139 | const val map = "com.amap.api:map2d:6.0.0" //2D地图
140 | const val location = "com.amap.api:location:6.1.0" //定位功能
141 | const val search = "com.amap.api:search:9.3.1" //搜索功能
142 | }
143 |
144 | object Umeng {
145 | const val umeng_common = "com.umeng.umsdk:common:9.5.0"
146 | const val umeng_asms = "com.umeng.umsdk:asms:1.6.3"
147 |
148 | //友盟Push依赖
149 | const val umeng_push = "com.umeng.umsdk:push:6.5.3"
150 |
151 | //性能、异常监控
152 | const val umeng_apm = "com.umeng.umsdk:apm:1.7.0"
153 |
154 | const val huaweiUmeng = "com.umeng.umsdk:huawei-umengaccs:1.3.5"
155 | const val huaweiPush = "com.huawei.hms:push:5.3.0.304"
156 |
157 | const val xiaomiUmeng = "com.umeng.umsdk:xiaomi-umengaccs:1.2.6"
158 | const val xiaomiPush = "com.umeng.umsdk:xiaomi-push:4.8.1"
159 |
160 | const val vivoUmeng = "com.umeng.umsdk:vivo-umengaccs:1.1.5"
161 | const val vivoPush = "com.umeng.umsdk:vivo-push:3.0.0.3"
162 |
163 | const val oppoUmeng = "com.umeng.umsdk:oppo-umengaccs:1.0.7-fix"
164 | const val oppoPush = "com.umeng.umsdk:oppo-push:2.1.0"
165 |
166 | const val meizuUmeng = "com.umeng.umsdk:meizu-umengaccs:1.1.5"
167 | const val meizuPush = "com.umeng.umsdk:meizu-push:4.1.4"
168 | }
169 |
170 | object Ali {
171 | //热修复
172 | const val hotfix = "com.aliyun.ams:alicloud-android-hotfix:3.3.7"
173 |
174 |
175 | const val haAdapter = "com.aliyun.ams:alicloud-android-ha-adapter:1.1.5.2-open"
176 | const val apm = "com.aliyun.ams:alicloud-android-apm:1.1.0.0-open"
177 | }
178 | }
179 |
180 | /**
181 | * @param dependencies 第三方库字符串或module
182 | */
183 | fun DependencyHandler.debugProguardImplementation(dependencies: Any) {
184 | add("debugProguardImplementation", dependencies)
185 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/java/com/buildsrc/kts/EnterpriseWeChatConfig.kt:
--------------------------------------------------------------------------------
1 | package com.buildsrc.kts
2 |
3 | object EnterpriseWeChatConfig {
4 | const val updateDesc =
5 | """因低版本Inflater崩溃,临时移除Inflater的hook
6 | """
7 |
8 | @JvmStatic
9 | val atUsers = listOf("WangNeng")
10 |
11 |
12 | const val webHookUrl =
13 | "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4c7a799c-f4bb-4aa9-93cd-a934d4655b0b"
14 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/java/com/buildsrc/kts/GlobalConfig.kt:
--------------------------------------------------------------------------------
1 | package com.buildsrc.kts
2 |
3 | import org.gradle.api.Project
4 |
5 | /**
6 | * 全局初始化project,方便buildSrc全局调用
7 | */
8 | object GlobalConfig {
9 | private var _project: Project? = null
10 | val project: Project get() = _project!!
11 |
12 | val rootDirFile get() = project.rootDir
13 |
14 | @JvmStatic
15 | fun init(project: Project) {
16 | _project = project
17 | }
18 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/java/com/buildsrc/kts/PropertiesUtils.kt:
--------------------------------------------------------------------------------
1 | package com.buildsrc.kts
2 |
3 | import com.android.SdkConstants
4 | import java.io.File
5 | import java.io.FileInputStream
6 | import java.io.InputStreamReader
7 | import java.util.*
8 |
9 | object PropertiesUtils {
10 |
11 | val gradleProperties =
12 | getFileProperties(File(GlobalConfig.rootDirFile, SdkConstants.FN_GRADLE_PROPERTIES))
13 |
14 | /**
15 | * local可能没有,如:远程构建
16 | */
17 | val localProperties by lazy(LazyThreadSafetyMode.PUBLICATION) {
18 | getFileProperties(File(GlobalConfig.rootDirFile, SdkConstants.FN_LOCAL_PROPERTIES))
19 | }
20 |
21 | /**
22 | * 读取properties文件
23 | */
24 | fun getFileProperties(file: File): Properties {
25 | val properties = Properties()
26 | InputStreamReader(FileInputStream(file), Charsets.UTF_8).use { reader ->
27 | properties.load(reader)
28 | }
29 | return properties
30 | }
31 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/java/com/buildsrc/kts/Repositories.kt:
--------------------------------------------------------------------------------
1 | package com.buildsrc.kts
2 |
3 | import org.gradle.api.artifacts.dsl.RepositoryHandler
4 | import org.gradle.kotlin.dsl.maven
5 |
6 | object Repositories {
7 | private const val aliyunNexusPublic = "https://maven.aliyun.com/nexus/content/groups/public/"
8 | private const val aliyunNexusRelease =
9 | "https://maven.aliyun.com/nexus/content/repositories/releases/"
10 | private const val aliyunPublic = "https://maven.aliyun.com/repository/public/"
11 | private const val aliyunGoogle = "https://maven.aliyun.com/repository/google/"
12 | private const val aliyunJcenter = "https://maven.aliyun.com/repository/jcenter/"
13 | private const val aliyunCentral = "https://maven.aliyun.com/repository/central/"
14 | private const val jitpackIo = "https://jitpack.io/"
15 |
16 | private const val huawei = "https://developer.huawei.com/repo/"
17 | private const val flutter = "https://storage.googleapis.com/download.flutter.io"
18 |
19 | internal const val aliyunReleaseAndArtifacts =
20 | "https://packages.aliyun.com/maven/repository/2196753-release-jjUEtd/"
21 |
22 | //公共账号密码,只可用于拉取
23 | private const val aliyunMjDefName = "642b9f209f62bf75b33fc1ae"
24 | private const val aliyunMjDefPassword = "EkNR7ao]bCHh"
25 |
26 | /**
27 | * 默认的需要拉的库
28 | */
29 | @JvmStatic
30 | fun defRepositories(resp: RepositoryHandler) {
31 | resp.apply {
32 | maven(aliyunNexusPublic)
33 | maven(aliyunNexusRelease)
34 | maven(aliyunPublic)
35 | maven(aliyunGoogle)
36 | maven(aliyunJcenter)
37 | maven(aliyunCentral)
38 | maven(jitpackIo)
39 | maven(flutter)
40 | mavenPassword(
41 | aliyunReleaseAndArtifacts,
42 | aliyunMjDefName,
43 | aliyunMjDefPassword
44 | )
45 | maven(huawei)
46 | mavenLocal()
47 | // 可能会影响下载速度,如果需要可以单独放开
48 | // mavenCentral()
49 | // google()
50 | // 过时的jcenter
51 | // jcenter()
52 | }
53 | }
54 |
55 | internal fun RepositoryHandler.mavenPassword(url: String, pwdName: String, pwd: String) {
56 | maven(url) {
57 | credentials {
58 | username = pwdName
59 | password = pwd
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/buildSrc/src/main/java/com/buildsrc/kts/ThirdSdkConfig.kt:
--------------------------------------------------------------------------------
1 | package com.buildsrc.kts
2 |
3 | /**
4 | * create by zhusw on 8/5/21 19:09
5 | */
6 | object ThirdSdkConfig {
7 |
8 | object UMeng {
9 | const val appKey = "56f5f77e67e58e741a000599"
10 | const val appMessageSecret = "67fbbecf12a01a57879ba798f22bfd2e"
11 | }
12 |
13 |
14 | object ALi {
15 | const val appKey = "333655480"
16 | const val appSecret = "dbc647947a6042b0bfbe4317386bc6d4"
17 | const val sophixStuRsaSecret =
18 | "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDUfGTjUCGMX8qdNTGQ5tRv/KXipiCTmZq2wP+dnQxzBrFx35tIsjA6ftMX74FlcuklEZmn0ZoBuBwoTJnFKid90hWkLgLKbF5c9xKYxAXHqspHBYQV3/1uYkiyS491nt0VuI2gmjBtbqtaNMBSu3rkpPyQXajjOv7AklcOHLHiO3v9z2xNFATmUEXmCyFwMhUkA8+nfWJUdzBaEk8KF+QKw8N8JN1huImE8BlJskfI34DkmBKYWPrpevRNudXQWW3MPmQ/V2q6E6MYVrulqLaEftrFfKpg5INAPx2Np+UCBskudPs8Vb3ZfBygRTsGhLcRku65iTPbxy2i7lwSbh3HAgMBAAECggEAPOsQ7Y6an6kS6LFIbZeQDi8ry+f+7PIbxgtc/W36FLw1gUUvOs9DPnH/yRv0SAaCywrZt6BC+/0xrWgQqdMlEVB6veAnEquw/gwju8JvOchAR85QCQl3mf75goxhK8KI9eI8UHAENEuHOsxm2xE+8XBPrH/9nG/ce485ACW/5fDME3rSTfaDXw5o3kuwBvrzl2yAEdgkDDl2aqyQruJQnk0cOfgC9ng/0R+ePVARcq2GMvPjW6MZ+edHqgmfkNu195SLw6JX09Cqjdcbe9bdXqmXV3Tkcr7qxetgIH5dpRF5jbFLrK0ycXWctYRK04QEV1Cu6sBBRsr7T7KQaDxzMQKBgQDxNJy0W1G7pLdvvocQ5+AYL0yHvwFhNkFDXNDfxTr0Joc+nqzMfSpDFbS5PMH8nW3kdbEngjAqvIcFv0sCYlpM2wCiZdVDofzBWeWqQ5tC6K1vNcZtZEH6zX1n858EomJu30lUjdyMUDz5RSW+O7YcMmrwRx1/qnXgFHA14G+5iQKBgQDhhNRM3lUJUew494HoTWtyB8arn/EqyrkuIOYZ+G1F/jyQ+gYUiakZTa5n9Df92fCAl0IQti3ALgaXIlQLaICAGGf2d4eVMg1I58xS3Y7w0K79YBpbIVlqCh1dNFu266MeOIOi5Ogx1y5LmjhnJQwqJcmxM3GTbIPBQDSLqdBYzwKBgHRk6A2cV4eOB7rTnQfKbPgesvZ6dGb9sGaaPm0NFtLEWNUd6tLOv3sZ2Vjd+do1+daZ+H/RMf8ddKaQVdjzrxGRSPAU5eG6JGcFQ29zKlQSRcb5kyQski49xPtJs32gApHwC8nSnw/SlGnRNVJkLA/VQIdsOc7mafVUx4UoJaHJAoGAY3RWxAeQ7OlKCd1tVus9pS40uV9x1fNNM0Ci+XULi8CRKUZ6A/ipsFUHwyR6xOKmI6+RHRmal5Q0p9YlxloxwUwCHTmde3h1T1+aRjkbXqxJX8hQFzxf2FnZlU8P212TtDIsK1xT4hcJfaHnmeVGthewGQuc3O0QNduA0M706RUCgYBTVSFnTopqkVmohTZrBYJ4JkJIVb9CDdRvFr59CH4b/T6Uv0k0C9G6Fc0OU16PM9RpOvm4H+e8uAgVdvSxkJBenOgQb+yIKoPuJ8ewOKG2JmdkmOW6Mk3Cm1jiSrlF5rOiXu+2SrylOGoarXNAL+oUoeizcBXxnjgVBcC6A6myGw=="
19 |
20 | //也叫:apm/tlog.rsaSecret
21 | const val apmRsaPublicKey =
22 | "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCT0fSoQXecW6AwL09P7X+FukM2EhGZFzxTIP7GrmRUVxIVZcV6djfS2edteJz7GERdAbFyElSrNw9vYS9waIkgDbFQm7OgSUMdYoC6cJjXzpbdE87n1sr+6tZtPRSg577fuQSv7Shl7fZftPuUxONTjQGvbUHVwmECUNy5HhF30wIDAQAB"
23 | }
24 |
25 | object Wework {
26 | const val appId = "ww559f418d868a6096"
27 | const val agentId = "1000013"
28 | }
29 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/java/com/buildsrc/kts/Utils.kt:
--------------------------------------------------------------------------------
1 | package com.buildsrc.kts
2 |
3 | /**
4 | * create by zhusw on 9/28/21 18:15
5 | */
6 | object Utils {
7 |
8 | @JvmStatic
9 | fun quote(str: String): String {
10 | return "\"$str\""
11 | }
12 | }
--------------------------------------------------------------------------------
/gpu_test/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/gpu_test/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 31
5 |
6 | defaultConfig {
7 | applicationId "com.juziml.content"
8 | minSdkVersion 24
9 | targetSdkVersion 31
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 |
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 |
24 | }
25 |
26 | dependencies {
27 | implementation fileTree(dir: 'libs', include: ['*.jar'])
28 | implementation 'androidx.appcompat:appcompat:1.3.0'
29 | }
30 |
--------------------------------------------------------------------------------
/gpu_test/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/gpu_test/src/androidTest/java/com/juziml/content/gpu_test/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.juziml.content.gpu_test;
2 |
3 | import static org.junit.Assert.*;
4 |
5 | import android.content.Context;
6 | import android.support.test.InstrumentationRegistry;
7 | import android.support.test.runner.AndroidJUnit4;
8 |
9 | import org.junit.Test;
10 | import org.junit.runner.RunWith;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.lzx.content.gpu_test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/gpu_test/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/gpu_test/src/main/java/com/juziml/content/gpu_test/FPoint.java:
--------------------------------------------------------------------------------
1 | package com.juziml.content.gpu_test;
2 |
3 | /**
4 | * create by zhusw on 2020-07-30 17:05
5 | */
6 | public class FPoint {
7 | public float x, y;
8 |
9 | public FPoint() {
10 | }
11 |
12 | public FPoint(float x, float y) {
13 | this.x = x;
14 | this.y = y;
15 | }
16 |
17 | public void setXY(float x, float y) {
18 | this.x = x;
19 | this.y = y;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/gpu_test/src/main/java/com/juziml/content/gpu_test/GpuTestAct.java:
--------------------------------------------------------------------------------
1 | package com.juziml.content.gpu_test;
2 |
3 | import android.os.Bundle;
4 | import android.view.MotionEvent;
5 | import android.view.View;
6 |
7 | import androidx.annotation.Nullable;
8 | import androidx.appcompat.app.AppCompatActivity;
9 |
10 | import com.juziml.content.R;
11 |
12 | /**
13 | * create by zhusw on 2020-07-29 10:55
14 | */
15 | public class GpuTestAct extends AppCompatActivity {
16 |
17 | GpuTestCurlAnimView gpuTestCurlAnimView;
18 |
19 | @Override
20 | protected void onCreate(@Nullable Bundle savedInstanceState) {
21 |
22 | super.onCreate(savedInstanceState);
23 | setContentView(R.layout.act_gputest);
24 | gpuTestCurlAnimView = findViewById(R.id.curlView);
25 |
26 | gpuTestCurlAnimView.setOnTouchListener(new View.OnTouchListener() {
27 | @Override
28 | public boolean onTouch(View v, MotionEvent event) {
29 | switch (event.getAction()) {
30 | case MotionEvent.ACTION_DOWN:
31 | gpuTestCurlAnimView.flipPrepare(event.getRawX(), event.getRawY());
32 | break;
33 | case MotionEvent.ACTION_MOVE:
34 | gpuTestCurlAnimView.flipCurl(event.getRawX(), event.getRawY());
35 | break;
36 | case MotionEvent.ACTION_UP:
37 | case MotionEvent.ACTION_CANCEL:
38 | gpuTestCurlAnimView.flipSetToDefault();
39 | break;
40 | default:
41 | break;
42 | }
43 | return true;
44 | }
45 | });
46 | }
47 |
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/gpu_test/src/main/java/com/juziml/content/gpu_test/GpuTestCurlAnimView.java:
--------------------------------------------------------------------------------
1 | package com.juziml.content.gpu_test;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.graphics.Paint;
8 | import android.graphics.Path;
9 | import android.graphics.PorterDuff;
10 | import android.graphics.PorterDuffXfermode;
11 | import android.graphics.Region;
12 | import android.util.AttributeSet;
13 | import android.view.View;
14 |
15 | import androidx.annotation.Nullable;
16 |
17 | /**
18 | * create by zhusw on 2020-07-29 10:54
19 | */
20 | public class GpuTestCurlAnimView extends View {
21 |
22 | Paint pointPaint;
23 | FPoint a, f, g, e, h, c, j, b, k, d, i;
24 |
25 | int width;
26 | int height;
27 |
28 | //图形
29 | Paint pathAPaint;
30 | Path pathA;
31 | Bitmap holderBitmap;
32 | Canvas bitmapCanvas = new Canvas();
33 |
34 | Paint pathCPaint;
35 | Path pathC;
36 | Paint pathBPaint;
37 | Path pathB;
38 | PorterDuffXfermode xfDST_ATOP = new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP);
39 |
40 | public GpuTestCurlAnimView(Context context) {
41 | this(context, null);
42 | }
43 |
44 |
45 | public GpuTestCurlAnimView(Context context, @Nullable AttributeSet attrs) {
46 | this(context, attrs, 0);
47 | }
48 |
49 | @Override
50 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
51 | super.onSizeChanged(w, h, oldw, oldh);
52 | width = w;
53 | height = h;
54 | a.setXY(-1, -1);
55 | holderBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
56 |
57 | }
58 |
59 | public GpuTestCurlAnimView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
60 | super(context, attrs, defStyleAttr);
61 | pointPaint = new Paint();
62 | pointPaint.setColor(Color.RED);
63 | pointPaint.setTextSize(25);
64 | a = new FPoint();
65 | f = new FPoint();
66 | g = new FPoint();
67 | e = new FPoint();
68 | h = new FPoint();
69 | c = new FPoint();
70 | j = new FPoint();
71 | b = new FPoint();
72 | k = new FPoint();
73 | d = new FPoint();
74 | i = new FPoint();
75 |
76 |
77 | pathAPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
78 | pathAPaint.setColor(Color.GREEN);
79 | pathA = new Path();
80 |
81 | pathCPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
82 | pathCPaint.setColor(Color.YELLOW);
83 | pathCPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));//消除c区 与a区重叠部分,原文注释:丢弃原图想覆盖目标图像的区域
84 | pathC = new Path();
85 |
86 | pathBPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
87 | pathBPaint.setColor(Color.BLUE);
88 | pathB = new Path();
89 | }
90 |
91 | public void flipPrepare(float x, float y) {
92 | if (y < height / 2) {
93 | f.setXY(width, 0);
94 | } else {
95 | f.setXY(width, height);
96 | }
97 | }
98 |
99 | public void flipSetToDefault() {
100 | a.setXY(-1, -1);
101 | f.setXY(0, 0);
102 | calculatePointXY(a, f);
103 | postInvalidate();
104 | }
105 |
106 | public void flipCurl(float x, float y) {
107 | a.x = x;
108 | a.y = y;
109 | calculatePointXY(a, f);
110 |
111 | //修正c点范围 不可小于0
112 | if (calculateCxRange(a.x, a.y, f) < 0) {
113 | calcPointAByTouchPoint();
114 | calculatePointXY(a, f);
115 | }
116 | postInvalidate();
117 | }
118 |
119 | private float calculateCxRange(float ax, float ay, FPoint f) {
120 | float gx = (ax + f.x) / 2;
121 | float gy = (ay + f.y) / 2;
122 | float ex = gx - (f.y - gy) * (f.y - gy) / (f.x - gx);
123 | return ex - (f.x - ex) / 2;
124 | }
125 |
126 | /**
127 | * 如果c点x坐标小于0,根据触摸点重新测量a点坐标
128 | */
129 | private void calcPointAByTouchPoint() {
130 | float w0 = width - c.x;
131 | float w1 = Math.abs(f.x - a.x);
132 | float w2 = width * w1 / w0;
133 | a.x = Math.abs(f.x - w2);
134 | float h1 = Math.abs(f.y - a.y);
135 | float h2 = w2 * h1 / w1;
136 | a.y = Math.abs(f.y - h2);
137 | }
138 |
139 | @Override
140 | public void onDrawForeground(Canvas canvas) {
141 | super.onDrawForeground(canvas);
142 | //把点绘制在前景上 方便观察
143 | drawPoint(canvas);
144 | }
145 |
146 | @Override
147 | protected void onDraw(Canvas canvas) {
148 | super.onDraw(canvas);
149 |
150 | if (a.x == -1 && a.y == -1) {
151 | canvas.drawPath(getDefaultPath(), pathAPaint);
152 | } else {
153 | Path pathA = getPathAFromRightTop();
154 | if (f.x == width && f.y == height) {
155 | pathA = getPathAFromRightBottom();
156 | }
157 | Path pathC = getPathC();
158 | canvas.drawPath(pathC, pathCPaint);//先给C区画上底色
159 | canvas.drawPath(pathA, pathAPaint);//先给A区画上底色
160 |
161 | canvas.save();//扣出B区
162 | canvas.clipPath(pathC, Region.Op.DIFFERENCE);
163 | canvas.clipPath(pathA, Region.Op.DIFFERENCE);
164 | canvas.drawPath(getPathB(), pathBPaint);
165 | canvas.restore();
166 | }
167 | }
168 |
169 | public Path getDefaultPath() {
170 | pathA.reset();
171 | pathA.lineTo(0, height);
172 | pathA.lineTo(width, height);
173 | pathA.lineTo(width, 0);
174 | pathA.close();
175 | return pathA;
176 | }
177 |
178 | /**
179 | * 以右下角为触发起点
180 | *
181 | * @return
182 | */
183 | private Path getPathAFromRightBottom() {
184 | pathA.reset();
185 | //划线从 0,0开始
186 | pathA.lineTo(0, height);//划线到左下角
187 | pathA.lineTo(c.x, c.y);//划线到c点
188 | pathA.quadTo(e.x, e.y, b.x, b.y);//以c为起点,b为终点,e为动点 画曲线
189 | pathA.lineTo(a.x, a.y);
190 | pathA.lineTo(k.x, k.y);
191 | pathA.quadTo(h.x, h.y, j.x, j.y);//以k为起点,j为终点 h为动点 画曲线
192 | pathA.lineTo(width, 0);
193 | pathA.close();
194 | return pathA;
195 | }
196 |
197 | private Path getPathAFromRightTop() {
198 | pathA.reset();
199 | pathA.lineTo(c.x, c.y);//划线到c
200 | pathA.quadTo(e.x, e.y, b.x, b.y);//c-b的曲线
201 | pathA.lineTo(a.x, a.y);//划线到a
202 | pathA.lineTo(k.x, k.y);//划线到k
203 | pathA.quadTo(h.x, h.y, j.x, j.y);//k-j的曲线
204 | pathA.lineTo(width, height);
205 | pathA.lineTo(0, height);
206 | pathA.close();
207 | return pathA;
208 | }
209 |
210 |
211 | private Path getPathC() {
212 | pathC.reset();
213 | pathC.moveTo(d.x, d.y);
214 | pathC.lineTo(b.x, b.y);
215 | pathC.lineTo(a.x, a.y);
216 | pathC.lineTo(k.x, k.y);
217 | pathC.lineTo(i.x, i.y);
218 | pathC.close();
219 | return pathC;
220 | }
221 |
222 | private Path getPathB() {
223 | pathB.reset();
224 | pathB.lineTo(0, height);
225 | pathB.lineTo(width, height);
226 | pathB.lineTo(width, 0);
227 | pathB.close();
228 | return pathB;
229 | }
230 |
231 |
232 | private void drawPoint(Canvas canvas) {
233 | canvas.drawText("a", a.x, a.y, pointPaint);
234 |
235 | canvas.drawText("f", f.x, f.y, pointPaint);
236 |
237 | canvas.drawText("g", g.x, g.y, pointPaint);
238 |
239 | canvas.drawText("e", e.x, e.y, pointPaint);
240 | canvas.drawText("h", h.x, h.y, pointPaint);
241 |
242 | canvas.drawText("c", c.x, c.y, pointPaint);
243 | canvas.drawText("j", j.x, j.y, pointPaint);
244 |
245 | canvas.drawText("b", b.x, b.y, pointPaint);
246 | canvas.drawText("k", k.x, k.y, pointPaint);
247 |
248 | canvas.drawText("d", d.x, d.y, pointPaint);
249 | canvas.drawText("i", i.x, i.y, pointPaint);
250 |
251 |
252 | }
253 |
254 | /**
255 | * 计算切点坐标,这里就没有推导公式了,直接拿来用了
256 | *
257 | * @param lineOne_My_pointOne
258 | * @param lineOne_My_pointTwo
259 | * @param lineTwo_My_pointOne
260 | * @param lineTwo_My_pointTwo
261 | * @return
262 | */
263 | private FPoint getIntersectionPoint(FPoint lineOne_My_pointOne,
264 | FPoint lineOne_My_pointTwo,
265 | FPoint lineTwo_My_pointOne,
266 | FPoint lineTwo_My_pointTwo) {
267 | float x1, y1, x2, y2, x3, y3, x4, y4;
268 | x1 = lineOne_My_pointOne.x;
269 | y1 = lineOne_My_pointOne.y;
270 | x2 = lineOne_My_pointTwo.x;
271 | y2 = lineOne_My_pointTwo.y;
272 | x3 = lineTwo_My_pointOne.x;
273 | y3 = lineTwo_My_pointOne.y;
274 | x4 = lineTwo_My_pointTwo.x;
275 | y4 = lineTwo_My_pointTwo.y;
276 |
277 | float pointX = ((x1 - x2) * (x3 * y4 - x4 * y3)
278 | - (x3 - x4) * (x1 * y2 - x2 * y1)) / ((x3 - x4)
279 | * (y1 - y2) - (x1 - x2) * (y3 - y4));
280 | float pointY = ((y1 - y2) * (x3 * y4 - x4 * y3)
281 | - (x1 * y2 - x2 * y1) * (y3 - y4)) / ((y1 - y2)
282 | * (x3 - x4) - (x1 - x2) * (y3 - y4));
283 |
284 | return new FPoint(pointX, pointY);
285 | }
286 |
287 | /**
288 | * 计算各点坐标
289 | */
290 | private void calculatePointXY(FPoint a, FPoint f) {
291 | g.x = (a.x + f.x) / 2;
292 | g.y = (a.y + f.y) / 2;
293 |
294 | e.x = g.x - (f.y - g.y) * (f.y - g.y) / (f.x - g.x);
295 | e.y = f.y;
296 |
297 | h.x = f.x;
298 | h.y = g.y - (f.x - g.x) * (f.x - g.x) / (f.y - g.y);
299 |
300 | c.x = e.x - (f.x - e.x) / 2;
301 | c.y = f.y;
302 |
303 | j.x = f.x;
304 | j.y = h.y - (f.y - h.y) / 2;
305 |
306 | b = getIntersectionPoint(a, e, c, j);
307 | k = getIntersectionPoint(a, h, c, j);
308 |
309 | d.x = (c.x + 2 * e.x + b.x) / 4;
310 | d.y = (2 * e.y + c.y + b.y) / 4;
311 |
312 | i.x = (j.x + 2 * h.x + k.x) / 4;
313 | i.y = (2 * h.y + j.y + k.y) / 4;
314 | }
315 |
316 | }
317 |
--------------------------------------------------------------------------------
/gpu_test/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/gpu_test/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/gpu_test/src/main/res/layout/act_gputest.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
--------------------------------------------------------------------------------
/gpu_test/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/gpu_test/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/gpu_test/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/gpu_test/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/gpu_test/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/gpu_test/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/gpu_test/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/gpu_test/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/gpu_test/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/gpu_test/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/gpu_test/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/gpu_test/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/gpu_test/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/gpu_test/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/gpu_test/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/gpu_test/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/gpu_test/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/gpu_test/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/gpu_test/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/gpu_test/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/gpu_test/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/gpu_test/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/gpu_test/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/gpu_test/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | gpu_test
3 |
4 |
--------------------------------------------------------------------------------
/gpu_test/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/gpu_test/src/test/java/com/juziml/content/gpu_test/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.juziml.content.gpu_test;
2 |
3 | import static org.junit.Assert.*;
4 |
5 | import org.junit.Test;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | ## Project-wide Gradle settings.
2 | #
3 | # For more details on how to configure your build environment visit
4 | # http://www.gradle.org/docs/current/userguide/build_environment.html
5 | #
6 | # Specifies the JVM arguments used for the daemon process.
7 | # The setting is particularly useful for tweaking memory settings.
8 | # Default value: -Xmx1024m -XX:MaxPermSize=256m
9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
10 | #
11 | # When configured, Gradle will run in incubating parallel mode.
12 | # This option should only be used with decoupled projects. More details, visit
13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
14 | # org.gradle.parallel=true
15 | #Tue Aug 07 11:00:08 CST 2018
16 | # systemProp.http.proxyHost=172.19.2.146
17 | # systemProp.https.proxyPort=1080
18 | # systemProp.https.proxyHost=127.0.0.1
19 | # systemProp.http.proxyPort=1080
20 | android.useDeprecatedNdk=true
21 | android.enableD8=true
22 | org.gradle.jvmargs=-Xmx5120m -XX:+UseParallelGC
23 | android.enableAapt2=true
24 | android.useAndroidX=true
25 | # Automatically convert third-party libraries to use AndroidX
26 | android.enableJetifier=true
27 | version.name=1.0.0
28 | version.code=100
29 | #包名
30 | app.id=com.juziml.read
31 | switch.release.obfuscate=false
32 | switch.debugProguard.obfuscate=false
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Jun 15 10:38:36 CST 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://emas-devops-cdn.aliyuncs.com/distributions/gradle-6.5-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/images/Book_view_desc.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/images/Book_view_desc.jpg
--------------------------------------------------------------------------------
/images/demo_pic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Western-parotia/BookViewProject/59f24a609314e25bae435238d39e88cef82fb40c/images/demo_pic.png
--------------------------------------------------------------------------------
/screenMatch.properties:
--------------------------------------------------------------------------------
1 | # 屏幕dp百分比适配的配置文件
2 | ############################################################################
3 | # Start with '#' is annotate. #
4 | # In front of '=' is key, cannot be modified. #
5 | # More information to visit: #
6 | # http://blog.csdn.net/fesdgasdgasdg/article/details/52325590 #
7 | # http://download.csdn.net/detail/fesdgasdgasdg/9913744 #
8 | # https://github.com/mengzhinan/PhoneScreenMatch #
9 | ############################################################################
10 | #
11 | # You need to refresh or reopen the project every time you modify the configuration,
12 | # or you can't get the latest configuration parameters.
13 | #
14 | #############################################################################
15 | #
16 | # Base dp value for screen match. Cut the screen into [base_dp] parts.
17 | # Data type is double. System default value is 360.
18 | # I advise you not to modify the value, be careful !!!!!!!!! _^_ *_*
19 | base_dp=360
20 | # Also need to match the phone screen of [match_dp].
21 | # If you have another dp values.
22 | # System default values is 240,320,384,392,400,410,411,480,533,592,600,640,662,720,768,800,811,820,960,961,1024,1280,1365
23 | match_dp=
24 | # If you not wanna to match dp values above. Write some above values here, append value with "," .
25 | # For example: 811,961,1365
26 | ignore_dp=
27 | # They're not android module name. If has more��split with , Symbol.
28 | # If you set, it will not show in SelectDialog.
29 | # If you have, write here and append value with "," .
30 | # For example: testLibrary,commonModule
31 | # System default values is .gradle, gradle, .idea, build, .git
32 | ignore_module_name=
33 | # Use which module under the values/dimen.xml file to do the base file,
34 | # and generated dimen.xml file store in this module?
35 | # Default value is 'app'.
36 | match_module=app
37 | # Don't show select dialog again when use this plugin.
38 | # System screen match will use the last selected module name or default module name.
39 | # You can give value true or false. Default value is false.
40 | not_show_dialog=false
41 | # Do you want to generate the default example dimens.xml file?
42 | # In path of .../projectName/screenMatch_example_dimens.xml, It does not affect your project code.
43 | # You can give value true or false. Default value is false.
44 | not_create_default_dimens=false
45 | # Does the font scale the same size as the DP? May not be accuracy.
46 | # You can give value true or false. Default value is true. Also need scaled.
47 | is_match_font_sp=true
48 | # Do you want to create values-wXXXdp folder or values-swXXXdp folder ?
49 | # I suggest you create values-swXXXdp folder,
50 | # because I had a problem when I was working on the horizontal screen adapter.
51 | # values-swXXXdp folder can solve my problem.
52 | # If you want create values-swXXXdp folder, set "create_values_sw_folder=true",
53 | # otherwise set "create_values_sw_folder=true".
54 | # Default values is true.
55 | create_values_sw_folder=true
56 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | include ':gpu_test'
3 |
--------------------------------------------------------------------------------
/signing.properties:
--------------------------------------------------------------------------------
1 | storeFile=test.jks
2 | keyAlias=android
3 | keyPassword=android
4 | storePassword=android
--------------------------------------------------------------------------------