|
2 years ago | |
---|---|---|
android | 2 years ago | |
assets | 2 years ago | |
ios | 2 years ago | |
lib | 2 years ago | |
linux | 2 years ago | |
macos | 2 years ago | |
test | 2 years ago | |
web | 2 years ago | |
windows | 2 years ago | |
.gitignore | 2 years ago | |
.metadata | 2 years ago | |
README.md | 2 years ago | |
analysis_options.yaml | 2 years ago | |
pubspec.lock | 2 years ago | |
pubspec.yaml | 2 years ago |
ComPDFKit SDK在Flutter中采用混合兼容方式进行对接。Android和iOS端分别采用PlatformViewLink和UiKitView,并通过MethodChannel和EventChannel进行交互。我们只封装了CPDFReaderView,而其他注释工具和书签列表等功能则使用Flutter开发。 关于MethodChannel、EventChannel使用方法请参考以下文章 Flutter Native交互
graph TD
A[Flutter] --> B(init Configuration)
B --> C[ComPDFKitWidget.dart]
C -->|Platform.isAndroid| D[PlatformViewlink]
C -->|Platform.isIOS| E[UiKitView]
D --> F[Android]
E --> G[iOS]
F --> H[FlutterCPDFReaderView]
G --> H[FlutterCPDFReaderView]
H --> |return| I[PDFReaderView]
I --> |add| ComPDFKitSDK-CPDFReaderView
Flutter与Native端通信使用MethodChannel、EventChannel进行交互,以下将以设置CPDFReaderView翻页模式举例展示交互流程
sequenceDiagram
setIsContinueMode->>MethodChannel: true
MethodChannel->> Native(Android、iOS): invokeMethod(functionName, true)
NOTE right of Native(Android、iOS): CPDFReaderView set isContinueMode = true
Native(Android、iOS)--> MethodChannel: return continue mode
MethodChannel--> setIsContinueMode:get continue mode
在PlatformView(Android)/UIKitView(iOS) 创建时,flutter会携带部分参数在PlatformViewFactory(Android)/FlutterPlatformViewFactory(iOS)中,从而设置默认的CPDFReaderView显示效果
参数Key | value | 说明 |
---|---|---|
scrollDirection | "vertical","horizontal" | 滑动方向 |
isDoublePage | true,false | 是否双页显示 |
isContinueMode | true,false | 是否为连续翻页模式 |
isCoverPageMode | true,false | 封面模式 |
isCropPageMode | true,false | 裁剪模式 |
readBackgroundColor | -1 | 阅读背景颜色,int类型 |
annotAttribute | map | 注释属性设置,此处将传递map类型 |
highlight:{'color' : -1 , 'alpha' : 255 } | 设置高亮注释默认属性值,此处为map类型 | |
strikeout:{'color' : -1 , 'alpha' : 255 } | 设置删除线注释默认属性值,此处为map类型 | |
underline:{'color' : -1 , 'alpha' : 255 } | 设置下划线注释默认属性值,此处为map类型 | |
squiggly:{'color' : -1 , 'alpha' : 255 } | 设置波浪线注释默认属性值,此处为map类型 |
{
'scrollDirection' : ScrollDirection.vertical,
'isDoublePage' : false,
'isContinueMode' : true,
'isCoverPageMode' : false,
'isCropPageMode' : false,
'readBackgroundColor' : 0xFFFFFFFF,
'annotAttribute' : {
'highlight' : {
'color' : Colors.red.value,
'alpha' : 255
},
'strikeout' : {
'color' : Colors.green.value,
'alpha' : 255
},
'underline' : {
'color' : Colors.blue.value,
'alpha' : 255
},
'squiggly' : {
'color' : Colors.purple.value,
'alpha' : 255
}
}
}
请求
方法名 | 通讯方式 | 参数 | 说明 |
---|---|---|---|
event_reader_view_call_back | EventChannel | "readerViewCallBack" | 监听CPDFReaderView滑动位置、点击文档区域、滑动状态监听 |
响应
Key | Value | 类型 | 说明 |
---|---|---|---|
eventType | onTapMainDocArea | String | 点击主文档区域监听 |
eventType | onScrollEnd | String | 滑动结束 |
eventType | onScrolling | String | 滑动中 |
eventType | onMoveToChild | String | 滑动到指定页码 |
eventType | onRecordLastJumpPageNum | String | 记录上次跳转页码 |
eventType | pageIndex | String | 页码,仅onMoveToChild、onRecordLastJumpPageNum |
示例: 1. Flutter中创建EventChannel对象
const _readerViewCallBackEventChannel =
EventChannel('event_reader_view_call_back');
2. 定义一个方法例如setReaderViewCallbackListener
CancelListener setReaderViewCallbackListener() {
var subscription = _readerViewCallBackEventChannel
.receiveBroadcastStream(eventSinkId.readerViewCallBack.index)//任何参数都可
.listen((data) {
Map<dynamic, dynamic> result = data;
String eventType = result[EventParameters.eventType];
switch (eventType) {
case EventParameters.onTapMainDocArea:
...
break;
case EventParameters.onMoveToChild:
int pageIndex = result[EventParameters.pageIndex];
...
break;
case EventParameters.onRecordLastJumpPageNum:
int pageIndex = result[EventParameters.pageIndex];
...
break;
case EventParameters.onScrollEnd:
...
break;
case EventParameters.onScrolling:
...
break;
default:
break;
}
}, cancelOnError: true);
return () {
subscription.cancel();
};
}
3. 在混合集成模式Widget创建完成的回调中调用setReaderViewCallbackListener
..addOnPlatformViewCreatedListener((id) {
setReaderViewCallbackListener();
})
onPlatformViewCreated: (id){
setReaderViewCallbackListener();
}
4. 事件回调 在IReaderViewCallback中通过EventChannel.EventSink.success返回结果
Android
val readerViewCallbackEventChannel = EventChannel(messenger, EVENT_CHANNEL_READER_VIEW_CALL_BACK)
readerViewCallbackEventChannel.setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
readerView.setReaderViewCallbackEventEmitter1(events)
}
override fun onCancel(arguments: Any?) {
}
})
override fun onTapMainDocArea() {
readerViewCallbackEventEmitter?.success(mapOf("eventType" to “onTapMainDocArea”))
}
override fun onMoveToChild(pageIndex: Int) {
readerViewCallbackEventEmitter?.success(mapOf("eventType" to “onMoveToChild”, "pageIndex" to pageIndex))
}
override fun onEndScroll() {
readerViewCallbackEventEmitter?.success(mapOf("eventType" to “onScrollEnd”))
}
override fun onScrolling() {
readerViewCallbackEventEmitter?.success(mapOf("eventType" to “onScrolling”))
}
override fun onRecordLastJumpPageNum(pageIndex: Int) {
readerViewCallbackEventEmitter?.success(mapOf("eventType" to “onRecordLastJumpPageNum”, "pageIndex" to pageIndex))
}
iOS
请求
方法名 | 通讯方式 | 参数 | 说明 |
---|---|---|---|
getScrollDirection | MethodChannel | 无 | 获取当前CPDFReaderView滚动方向 |
响应
参数名称 | 类型 | 说明 |
---|---|---|
vertical | String | 垂直滑动 |
horizontal | String | 水平滑动 |
示例
Flutter获取滚动方向
///这里可以定义一个共用的MethodChannel
const _methodChannel = MethodChannel('com.compdfkit.pdf.flutter');
//获取滚动方向
Future<bool> scrollDirectionIsVerticalMode() async {
String scrollDirection =
await _methodChannel.invokeMethod(Functions.getScrollDirection);
return scrollDirection == ScrollDirection.vertical;
}
Android 返回当前滚动方向
//定义MethodChannel对象
val methodChannel = MethodChannel(messenger, "com.compdfkit.pdf.flutter")
methodChannel.setMethodCallHandler(this)
//在onMethodCall方法中返回结果
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
“getScrollDirection” -> {
result.success(if (cpdfReaderView.isVerticalMode) “vertical” else “horizontal”)
}
}
iOS 返回当前滚动方向
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
FlutterMethodChannel* methodChannel = [FlutterMethodChannel
methodChannelWithName:@"com.compdfkit.pdf.flutter"
binaryMessenger:controller.binaryMessenger];
[methodChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
if([@"getScrollDirection" isEqualToString:call.method]) {
result(@"horizontal");//此处仅示例作为参考,请从CPDFReaderView中获取滚动方向
}
}];
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
class ComPDFKitPlugin {
init(messenger : FlutterBinaryMessenger){
let channel = FlutterMethodChannel(name: "com.compdfkit.pdf.flutter", binaryMessenger: messenger)
channel.setMethodCallHandler{(call : FlutterMethodCall, result : @escaping FlutterResult) in
if (call.method == "getScrollDirection"){
result("vertical")//此处仅示例作为参考,请从CPDFReaderView中获取滚动方向
}
}
}
}
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
ComPDFKitPlugin(messenger: controller.binaryMessenger)//register FlutterMethodChannel
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
请求
方法名 | 通讯方式 | 数据格式 | key | value | 说明 |
---|---|---|---|---|---|
setScrollDirection | MethodChannel | Map | scrollDirection | "vertical","horizontal" | 设置CPDFReaderView滚动方向 |
响应
参数名称 | 类型 | 说明 |
---|---|---|
vertical | String | 垂直滑动 |
horizontal | String | 水平滑动 |
示例
Flutter 设置CPDFReaderView滚动方向
///direction : vertical or horizontal
Future<bool> setScrollDirection(String direction) async {
String scrollDirection = await _methodChannel.invokeMethod('setScrollDirection',
{'scrollDirection': direction});
//原生端返回当前的滚动状态
return scrollDirection == ScrollDirection.vertical;
}
Android端
在MethodChannel的onMethodCall方法中获取到Flutter传输的数据
//在onMethodCall方法中返回结果
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
val configuration = call.arguments as? HashMap<String, Any>
when (call.method) {
“setScrollDirection” -> {
(configuration?.get("scrollDirection") as? String)?.let { direction->
pdfReaderView.pdfReaderView.isVerticalMode = direction == "vertical"
result.success(if (pdfReaderView.pdfReaderView.isVerticalMode) "vertical" else "horizontal")
}
else ->{
...
}
}
}
iOS
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
FlutterMethodChannel* methodChannel = [FlutterMethodChannel
methodChannelWithName:@"com.compdfkit.pdf.flutter"
binaryMessenger:controller.binaryMessenger];
[methodChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
if([@"setScrollDirection" isEqualToString:call.method]) {
NSDictionary* arguments = call.arguments;
NSString* scrollDirection = arguments[@"scrollDirection"];
//请从CPDFReaderView获取当前滑动方向并返回
result(scrollDirection);
}
}];
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
channel.setMethodCallHandler{(call : FlutterMethodCall, result : @escaping FlutterResult) in
if (call.method == "setScrollDirection"){
var convertData = arguments as! [String: String];
var scrollDirection = convertData["scrollDirection"]
result(scrollDirection)//请从CPDFReaderView获取当前滑动方向并返回
}
...
}
#### 设置获取滚动方式(isContinueMode)
##### **1. 获取滚动模式**
**请求**
| 方法名 | 通讯方式 | 参数 | 说明 |
| ----------------- | --------------- | ------ | -------------------------------------- |
| getPageContinue | MethodChannel | 无 | 获取当前滚动方式:连续滚动、翻页滚动 |
| **响应** | | | |
* 响应数据类型:**Boolean**
* 响应数据:
| 参数名称 | 类型 | 说明 |
| ---------- | --------- | ---------- |
| true | boolean | 连续滚动 |
| false | boolean | 翻页滚动 |
> 示例:请参考[isVerticalMode](#2.1)
##### **2. 设置滚动方式**
**请求**
| 方法名 | 通讯方式 | 数据格式 | key | value | 说明 |
| ----------------- | --------------- | ---------- | ---------------- | ------------ | ------------------------------------------------ |
| setPageContinue | MethodChannel | Map | isContinueMode | true,false | 设置CPDFReaderView滚动方式:连续滚动、翻页滚动 |
**响应**
* 响应数据传递类型:**Boolean**
* 响应事件:
| 参数名称 | 类型 | 说明 |
| ---------- | --------- | ---------- |
| true | boolean | 连续滚动 |
| false | boolean | 翻页滚动 |
> 示例:请参考[isVerticalMode](#2.1)
---
#### 显示模式(isDoublePage)
##### **1. 获取显示模式**
**请求**
| 方法名 | 通讯方式 | 参数 | 说明 |
| ------------- | --------------- | ------ | -------------------- |
| getPageMode | MethodChannel | 无 | 单页显示、双页显示 |
**响应**
* 响应数据类型:**Boolean**
* 响应数据:
| 参数名称 | 类型 | 说明 |
| ---------- | --------- | ---------- |
| true | boolean | 双页显示 |
| false | boolean | 单页显示 |
> 示例:请参考[isVerticalMode](#2.1)
##### **2. 设置显示模式**
**请求**
| 方法名 | 通讯方式 | 数据格式 | key | value | 说明 |
| ------------- | --------------- | ---------- | -------------- | ------------ | -------------------- |
| setPageMode | MethodChannel | Map | isDoublePage | true,false | 单页显示、双页显示 |
**响应**
* 响应数据类型:**Boolean**
* 响应事件:
| 参数名称 | 类型 | 说明 |
| ---------- | --------- | ---------- |
| true | boolean | 双页显示 |
| false | boolean | 单页显示 |
> 示例:请参考[isVerticalMode](#2.1)
---
#### 封面模式(isCoverPageMode)
##### **1. 获取是否为封面显示模式**
**请求**
| 方法名 | 通讯方式 | 参数 | 说明 |
| ----------------- | --------------- | ------ | ------------------------------ |
| isCoverPageMode | MethodChannel | 无 | 显示模式:默认模式、封面模式 |
**响应**
* 响应数据类型:**Boolean**
* 响应数据:
| 参数名称 | 类型 | 说明 |
| ---------- | --------- | ---------- |
| true | boolean | 封面模式 |
| false | boolean | 默认模式 |
> 示例:请参考[isVerticalMode](#2.1)
##### **2. 设置为封面显示模式**
**请求**
| 方法名 | 通讯方式 | 数据格式 | key | value | 说明 |
| ------------------ | --------------- | ---------- | ----------------- | ------------ | ------------------------------- |
| setCoverPageMode | MethodChannel | Map | isCoverPageMode | true,false | true:双页显示、false:单页显示 |
**响应**
* 响应数据类型:**Boolean**
* 响应事件:
| 参数名称 | 类型 | 说明 |
| ---------- | --------- | ---------- |
| true | boolean | 封面模式 |
| false | boolean | 默认模式 |
> 示例:请参考[isVerticalMode](#2.1)
---
#### 裁剪显示(isCropPageMode)
##### **1. 获取当前是否为裁剪模式**
| 方法名 | 通讯方式 | 参数 | 说明 |
| ---------------- | --------------- | ------ | ---------------------------------- |
| isCropPageMode | MethodChannel | 无 | 页面显示模式:默认模式、裁剪模式 |
**响应**
* 响应数据类型:**Boolean**
* 响应数据:
| 参数名称 | 类型 | 说明 |
| ---------- | --------- | ---------- |
| true | boolean | 裁剪模式 |
| false | boolean | 默认模式 |
> 示例:请参考[isVerticalMode](#2.1)
##### **2. 设置为裁剪显示模式**
**请求**
| 方法名 | 通讯方式 | 数据格式 | key | value | 说明 |
| ------------------- | --------------- | ---------- | ---------------- | ------------ | ------------------------------- |
| setIsCropPageMode | MethodChannel | Map | isCropPageMode | true,false | true:裁剪模式、false:默认模式 |
**响应**
* 响应数据传递类型:**Boolean**
* 响应事件:
| 参数名称 | 类型 | 说明 |
| ---------- | --------- | ---------- |
| true | boolean | 裁剪模式 |
| false | boolean | 默认模式 |
> 示例:请参考[isVerticalMode](#2.1)
---
方法名 | 通讯方式 | 参数 | 说明 |
---|---|---|---|
getReaderViewBackgroundColor | MethodChannel | 无 | 背景颜色 |
响应
参数名称 | 类型 | 说明 |
---|---|---|
-1 | int | int类型颜色值 |
示例:请参考isVerticalMode
请求
方法名 | 通讯方式 | 数据格式 | key | value | 说明 |
---|---|---|---|---|---|
setReaderViewBackgroundColor | MethodChannel | Map | backgroundColor | -1 | 传入int类型颜色值 |
响应
参数名称 | 类型 | 说明 |
---|---|---|
-1 | int | 当前设置的背景颜色值 |
示例:请参考isVerticalMode
请求
方法名 | 通讯方式 | 数据格式 | key | value | 说明 |
---|---|---|---|---|---|
getAnnotAttribute | MethodChannel | Map | annotType | "highlight","strikeout","underline","squiggly"... | 注释类型 |
响应
Key | Value | 类型 | 说明 |
---|---|---|---|
annotAttrColor | -1 | int | 注释颜色 |
annotAttrAlpha | -1 | int | 注释颜色透明度 |
请求
方法名 | 通讯方式 | 数据格式 | key | value | 说明 |
---|---|---|---|---|---|
setAnnotAttribute | MethodChannel | Map | annotType | "highlight","strikeout","underline","squiggly"... | 注释类型 |
annotAttribute | {'color': color, 'alpha':alpha} | 此处为map格式数据,alpha范围:0~255 |
响应
Key | Value | 类型 | 说明 |
---|---|---|---|
annotAttrColor | -1 | int | 注释颜色 |
annotAttrAlpha | -1 | int | 注释颜色透明度 |
请求
方法名 | 通讯方式 | 数据格式 | key | value | 说明 |
---|---|---|---|---|---|
setCurrentFocusedType | MethodChannel | Map | touchMode | "browse","add_annot" | 设置触摸模式,browse:浏览模式, add_annot:添加注释模式 |
Map | focusedType | "highlight","strikeout","underline","squiggly"... | 注释类型 |
响应 无
在CPDFReaderView视图中选中注释(例如:高亮),会弹出选择菜单,菜单中可以进行注释的属性调整、复制、删除等操作,其中属性调整需要与Flutter进行交互,通知Flutter弹出注释属性调整界面
val readerViewContextMenuHelperEventChannel =
EventChannel(messenger, “event_reader_view_context_menu_helper”)
readerViewContextMenuHelperEventChannel.setStreamHandler {
onListen { arguments, events ->
//此处保存events对象
readerView.setContextMenuHelperEventEmitter(events)
}
}
2.在自行实现的CPDFContextMenuShowHelper中例如getMarkupContentView方法,点击attribute按钮需要调整注释属性时,通知Flutter展示属性调整界面
override fun getMarkupContentView(pageView: CPDFPageView, annotImpl: CPDFBaseAnnotImpl<*>, layoutInflater: LayoutInflater): View {
val contentView = layoutInflater.inflate(R.layout.t_markup_annot_menu_layout, null)
val markupAnnotImpl = annotImpl as CPDFMarkupAnnotImpl
val markupAnnotation = markupAnnotImpl.onGetAnnotation()
invokeOnClickListener(contentView, { v: View ->
val id = v.id
if (id == R.id.attr) {
//此处需要将当前注释、CPDFPageView对象保存到全局变量,以便于后续调整注释属性
selectMarkupAnnotImpl = markupAnnotImpl
selectPageView = pageView
//Notify the flutter side to open the annotation attribute modification interface
contextMenuHelperEventEmitter?.success(mapOf(
"annotationType" to markupAnnotation.type.name.lowercase(),
"annotAttrColor" to markupAnnotation.color,
"annotAttrAlpha" to markupAnnotation.alpha
))
} else if (id == R.id.copy) {
...
} else if (id == R.id.delete) {
...
}
popupWindow.dismiss()
}, R.id.attr, R.id.copy, R.id.delete)
return contentView
}
key | value | 说明 |
---|---|---|
annotationType | "highlight","strikeout","underline","squiggly"... | 注释类型,用于展示不同注释类型的属性调整界面 |
annotAttrColor | -1 | 注释颜色 |
annotAttrAlpha | 0~255 | 注释颜色透明度 |
3.在实现的CPDFContextMenuShowHelper中定义MethodChannel获取Flutter端传入的需要修改的注释属性
modifyAnnotationAttrChannel = MethodChannel(messenger,“method_modify_annotation_attribute”)
modifyAnnotationAttrChannel.setMethodCallHandler { call, result ->
//这里获取flutter传入的颜色、透明度等参数
val configuration = call.arguments as? HashMap<String, Any>
when(call.method){
“modifyAnnotationAttribute” ->{
//颜色、透明度可能为空,注意判断,可能用户只调节一个属性
val annotAttrColor = configuration.getLong("color")
val annotAttrAlpha = configuration.getInt("alpha")
val annotationType = configuration.getString(PluginUtils.KEY_ANNOT_TYPE)
selectMarkupAnnotImpl?.let {
if (it.onGetAnnotation().type.name.equals(annotationType, true)) {
it.onGetAnnotation()?.let {annotation->
if (annotAttrColor != null) {
annotation.color = annotAttrColor.toInt()
}
if (annotAttrAlpha != null) {
annotation.alpha = annotAttrAlpha
}
annotation.updateAp()
selectMarkupAnnotImpl?.onAnnotAttrChange()
selectPageView?.invalidate()
}
}
}
}
...
KEY_DISMISS_MODIFY_ANNOTATION_ATTRIBUTE -> {
selectMarkupAnnotImpl = null
selectPageView = null
}
}
}
方法名 | 通讯方式 | 数据格式 | key | value | 说明 |
---|---|---|---|---|---|
modifyAnnotationAttribute | MethodChannel | Map | annotType | "highlight","strikeout","underline","squiggly"... | 注释类型 |
color | -1 | 注释颜色 | |||
alpha | 0~255 | 注释颜色透明度 |
4.在用户关闭调节选项弹窗后会发出dismissModifyAnnotationAttr通知,请在此通知中释放之前赋值的全局变量
modifyAnnotationAttrChannel = MethodChannel(messenger,“method_modify_annotation_attribute”)
modifyAnnotationAttrChannel.setMethodCallHandler { call, result ->
//这里获取flutter传入的颜色、透明度等参数
val configuration = call.arguments as? HashMap<String, Any>
when(call.method){
“modifyAnnotationAttribute” ->{
...
}
"dismissModifyAnnotationAttr" -> {
selectMarkupAnnotImpl = null
selectPageView = null
}
}
}
方法名 | 通讯方式 | 说明 |
---|---|---|
dismissModifyAnnotationAttr | MethodChannel | flutter界面关闭属性调整弹窗后发送到原生端 |