一、自己对Blockly模块的理解总概:
在Android中集成Blockly模块可以通过拖拽类似于搭建积木的方式来实现基本的逻辑和操作的编程,这种图形化的编程之后最终的结果能以代码的形式显示出来或能用这样的代码去操控机器人。
1.实现的过程:
* 快速实现的Blockly的方式,是在新建的一个工程当中先到两个module分别是blocklylib-core和blocklylib-vertical;
* 定义自己的activity继承抽象类AbstractBlocklyActivity,实现四个抽象方法:
getToolboxContentsXmlPath():获取toolbox的xml文件路径,返回的是toolbox设置的xml文件在asset中的路径;
getBlockDefinitionsJsonPaths():获取blockly定义json数据的文件路径,返回值是定义blockly的json数据在asset中的路径;
getGeneratorsJsPaths():获取定义js的文件路径,返回值是json block definitions在asset中的路径;
getCodeGenerationCallback():获取代码定义的回调,返回值是代码定义的回调值。
2.AbstractBlocklyActivity的理解:
*快速创建Blockly实现的基础类,首先它会去创建一个xml名为blockly_unified_workspace的可操作空间区域。
*整个blockly大概有六大部分组成:
blockly_workspace:块可放置拖拽的workspace部分;
blockly_trash_ui:块删除的UI部分;
blockly_toolbox_ui:toolbox工具块的选择部分;
blockly_categories:类型种类选择的部分;
及可编辑的workspace缩小、放大、定位的编辑选项;
及对workspace的run、clear上午操作。
3.blocklylib-core核心的module的理解:
这个module采用的是MVC的架构,model包里定义了一些数据的输入输出的类型类;ui包里有Toolbox、Tranh、workspace、zoom的界面视图;control包里BlocklyController是对Blockly中如workspace、toolbar、trash等各种组件的控制,ProcedureManager是管理整个过程中定义值参数名字。
4.blocklylib-core的整体架构是MVC,控制层包含工作空间的控件的控制;UI层是对工作空间中的空间的绘制;模型层对控件中的模型的定义:
二、Blockly模块之AbstractBlocklyActivity:
首先这是一个抽象的类,在自己的activity中可以继承它重写一些方法进行对Blockly 模块的自定义设置。
从AbstractBlocklyActivity模块中的一段英文介绍可以一步知道到底是怎么实现的:
1.整个布局中分成工作空间(workspace)、工具盒( toolbox)、垃圾回收(trask)、设置飞布局(fly-out views)几个部分。
下面是布局的对比图:
类中英文代码注释为:
* The default layout is filled with a workspace and the toolbox and trash each configured as
* fly-out views. Everything below the {@link ActionBar} can be replaced by overriding
* {@link #onCreateContentView}. After {@link #onCreateContentView}, a {@link BlocklyActivityHelper}
* is constructed to help initialize the Blockly fragments, controller, and supporting UI. If
* overriding {@link #onCreateContentView} without {@code unified_blockly_workspace.xml} or
* otherwise using standard blockly fragment and view ids ({@link R.id#blockly_workspace},
* {@link R.id#blockly_toolbox_ui}, {@link R.id#blockly_trash_ui}, etc.), override
* {@link #onCreateActivityHelper()} and {@link BlocklyActivityHelper#onCreateFragments()}
* appropriately.
大概意思为:整个默认布局填充着workspace、toolbox、trask、fly-out views。在actionbar之下的都可以在onCreateContentView中被重写,在BlocklyActivityHelper中构建初始化Blockly的fragment、controller、UI,如在
onCreateContentView中没有重新定义新的XML文件则会使用默认的XML(在这里可自定义自己需要的布局形式包括fragment中的)。
Configure the workspace by providing definitions for {@link #getBlockDefinitionsJsonPaths()},
* {@link #getToolboxContentsXmlPath()}. Alternate {@link BlockViewFactory}s are supported via
* {@link BlocklyActivityHelper#onCreateBlockViewFactory}. An initial workspace can be loaded during
* {@link #onLoadInitialWorkspace()}.
*
* The block definitions can be updated at any time by calling {@link #resetBlockFactory()},
* which triggers another call to {@link #getBlockDefinitionsJsonPaths()}. Similarly, The toolbox
* can be reloaded by calling {@link #reloadToolbox()}, which triggers another call to
* {@link #getToolboxContentsXmlPath()}.
这一段的大概意思是:继承AbstractBlocklyActivity的抽象类要重写的四个抽象方法:
*getToolboxContentsXmlPath():获取toolbox的xml文件路径,返回的是toolbox设置的xml文件在asset中的路径,相当于设置图1中的fly-out views的飞布局也可以自定义自己的json数据来实现自定义设置;
*getBlockDefinitionsJsonPaths():获取blockly定义json数据的文件路径,返回值是定义blockly的json数据在asset中的路径,相当于设置图1中的fly-out views的飞布局也可以自定义自己的json数据来实现自定义设置;
* getGeneratorsJsPaths():获取定义js的文件路径,返回值是json block definitions在asset中的路径;
*getCodeGenerationCallback():获取代码定义的回调,返回值是代码定义的回调值。
三、Blockly模块之BlocklyActivityHelper:
1.在这个类中对工作空间中的几大fragment的定义:WorkspaceFragment(放置控件的地方)、BlockListUI(如图中flay-outview)。
BlocklyActivityHelper中构造函数的注解具体实现了,整个工作空间的设置:
public BlocklyActivityHelper(AppCompatActivity activity) {
mActivity = activity;
onCreateFragments(); //创建工作空间的fragment
if (mWorkspaceFragment == null) {
throw new IllegalStateException("mWorkspaceFragment is null");
}
mWorkspaceHelper = new WorkspaceHelper(activity); // 初始化工作空间
mBlockViewFactory = onCreateBlockViewFactory(mWorkspaceHelper); //加载view的工厂
mClipDataHelper = onCreateClipDataHelper(); //加载内部默认的xml文件
mCodeGeneratorManager = new CodeGeneratorManager(activity); //运行后代码的管理器
BlocklyController.Builder builder = new BlocklyController.Builder(activity) //对整个工作空间控制的设置
.setClipDataHelper(mClipDataHelper)
.setWorkspaceHelper(mWorkspaceHelper)
.setBlockViewFactory(mBlockViewFactory)
.setWorkspaceFragment(mWorkspaceFragment)
.setTrashUi(mTrashBlockList)
.setToolboxUi(mToolboxBlockList, mCategoryFragment);
mController = builder.build();
onCreateVariableCallback(); //参数的回调
onCreateMutatorListener(); //设置变量的监听
onConfigureTrashIcon(); //设置回收图标
onConfigureZoomInButton(); //设置放大按钮
onConfigureZoomOutButton(); //设置缩小按钮
onConfigureCenterViewButton(); //设置定位按钮
}
2.保存工作空间至本地:
public void saveWorkspaceToAppDir(String filename)
throws FileNotFoundException, BlocklySerializerException{
Workspace workspace = mWorkspaceFragment.getWorkspace();
workspace.serializeToXml(mActivity.openFileOutput(filename, Context.MODE_PRIVATE));
}
public boolean saveWorkspaceToAppDirSafely(String filename) {
try {
saveWorkspaceToAppDir(filename);
Toast.makeText(mActivity, R.string.toast_workspace_saved,
Toast.LENGTH_LONG).show();
return true;
} catch (FileNotFoundException | BlocklySerializerException e) {
Toast.makeText(mActivity, R.string.toast_workspace_not_saved,
Toast.LENGTH_LONG).show();
Log.e(TAG, "Failed to save workspace to " + filename, e);
return false;
}
}
3.加载本地的工作空间文件:
public void loadWorkspaceFromAppDir(String filename) throws IOException, BlockLoadingException {
mController.loadWorkspaceContents(mActivity.openFileInput(filename));
}
4.请求代码的生成:
public void requestCodeGeneration(
LanguageDefinition codeGeneratorLanguage, //生成的代码类型
List blockDefinitionsJsonPaths, //json数据的路径
List generatorsJsPaths, //生成js的代码路径
CodeGenerationRequest.CodeGeneratorCallback codeGenerationCallback) {
final StringOutputStream serialized = new StringOutputStream();
try {
mController.getWorkspace().serializeToXml(serialized);
} catch (BlocklySerializerException e) {
// Not using a string resource because no non-developer should see this.
String msg = "Failed to serialize workspace during code generation.";
Log.wtf(TAG, msg, e);
Toast.makeText(mActivity, msg, Toast.LENGTH_LONG).show();
throw new IllegalStateException(msg, e);
}
mCodeGeneratorManager.requestCodeGeneration(
new CodeGenerationRequest(
serialized.toString(),
codeGenerationCallback,
codeGeneratorLanguage,
blockDefinitionsJsonPaths,
generatorsJsPaths));
try {
serialized.close();
} catch (IOException e) {
// Ignore error on close().
}
}
1. 这个类是对工作空间中所有控件动作的管理设置:
public BlocklyController build() {
if (mViewFactory == null && (mWorkspaceFragment != null || mTrashUi != null
|| mToolbox != null || mCategoryUi != null)) {
throw new IllegalStateException(
"BlockViewFactory cannot be null when using UIs.");
}
if (mWorkspaceHelper == null) {
mWorkspaceHelper = new WorkspaceHelper(mContext);
}
BlockClipDataHelper blockClipDataHelper = mClipHelper;
if (blockClipDataHelper == null) {
blockClipDataHelper = SingleMimeTypeClipDataHelper.getDefault(mContext);
}
BlockFactory factory = new BlockFactory();
loadBlockDefinitionsFromResources(factory, mBlockDefResources);
loadBlockDefinitionsFromAssets(factory, mBlockDefAssets);
BlocklyController controller = new BlocklyController(
mContext, factory, mWorkspaceHelper, blockClipDataHelper, mViewFactory);
loadToolbox(controller);
// Any of the following may be null and result in a no-op.
controller.setWorkspaceFragment(mWorkspaceFragment);
controller.setTrashUi(mTrashUi); //删除按钮的动作设置
controller.setToolboxUi(mToolbox, mCategoryUi); //工具栏动作的设置
controller.setTrashIcon(mTrashIcon);
controller.setVariableCallback(mVariableCallback); //设置参数回调
return controller;
}
2.点击fly-outview消失的监听:
private View.OnClickListener mDismissClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
mFlyoutController.closeFlyouts(); //关闭飞出来显示的控件
}
};
3.拖拽view的设置:
private final Dragger.DragHandler mWorkspaceDragHandler = new Dragger.DragHandler() {
@Override
public Runnable maybeGetDragGroupCreator(final PendingDrag pendingDrag) {
BlockView touchedView = pendingDrag.getTouchedBlockView();
// If a shadow or other undraggable block is touched, and it is attached to a draggable
// parent block, drag that block instead.
final BlockView activeTouchedView = mHelper.getNearestActiveView(touchedView);
if (activeTouchedView == null) {
Log.i(TAG, "User touched a stack of blocks that may not be dragged");
return null;
}
return new Runnable() {
@Override
public void run() {
// extractBlockAsRoot() fires MoveEvent immediately.
extractBlockAsRoot(activeTouchedView.getBlock());
// Since this block was already on the workspace, the block's position should
// have been assigned correctly during the most recent layout pass.
BlockGroup bg = mHelper.getRootBlockGroup(activeTouchedView);
bg.bringToFront();
// Measure and layout the block group to get the correct touch offset.
bg.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
bg.layout(0, 0, bg.getMeasuredWidth(), bg.getMeasuredHeight());
ViewPoint touchOffset = new ViewPoint( //拖拽位置的获取
(int) (activeTouchedView.getX()
+ pendingDrag.getTouchDownViewOffsetX()),
(int) (activeTouchedView.getY()
+ pendingDrag.getTouchDownViewOffsetY()));
pendingDrag.startDrag(mWorkspaceView, bg, touchOffset);
}
};
}