cocos2dx骨骼动画Armature源码分析(三)
代码目录结构
cocos2dx里骨骼动画代码在cocos -> editor-support -> cocostudio文件夹中,win下通过筛选器,文件结构如下。(mac下没有分,是整个一坨)
armature(目录):
animation(目录):动画控制相关。
CCProcessBase(文件):
ProcessBase(类):CCTween和ArmatureAnimation的基类。
CCTWeen(文件):
Tween(类):控制flash里一个layer的动画。
CCArmatureAnimation(文件):
ArmatureAnimation(类):控制整个动画,内有多个Tween。
datas(目录):xml或json转成c++中直接用的数据结构。
CCDatas(文件):
BaseData(类):BoneData、FrameData的基类,包含大小位置颜色等信息。
DisplayData(类): SpriteDisplayData、ArmatureDisplayData、ParticleDisplayData的基类。
SpriteDisplayData(类):骨骼中的显示数据。
ArmatureDisplayData(类):
ParticleDisplayData(类):
BoneData(类):单个骨骼数据,flash中一个layer是一个骨骼。
ArmatureData(类):骨骼数据,整个骨骼结构数据。
FrameData(类):关键帧数据。
MovementBoneData(类):带有关键帧的骨骼数据。
MovementData(类):一个完整动画数据。
AnimationData(类):组动画数据,包含多个MovementData。
ContourData(类):
TextureData(类):显示图片数据。
utils(目录):
CCArmatureDataManager(文件):
RelativeData(类):
ArmatureDataManager(类):管理ArmatureData、AnimationData、TextureData。
CCArmatureDefine(文件):
CCDataReaderHelper(文件):
_AsyncStruct(类):
_DataInfo(类):
DataReaderHelper(类):这正解析xml或json的类。
CCSpriteFrameCacheHelper(文件):
SpriteFrameCacheHelper(类):
CCTransformHelp(文件):
TransformHelp(类):矩阵运算。
CCUtilMath(文件): CCArmature(文件):
Armature(类):控制整个骨骼动画,内有ArmatureAnimation和ArmatureData。 CCBone(文件):
Bone(类):骨骼控制类 display(目录):显示的图片管理。
CCBatchNode(文件):
BatchNode(类):
CCDecorativeDisplay(文件):
DecorativeDisplay(类):
CCDisplayFactory(文件):
DisplayFactory(类):
CCDisplayManager(文件):
DisplayManager(类):
CCSkin(文件):
Skin(类): physics(目录):物理引擎相关,不分析。
ColliderFilter(文件):
ColliderFilter(类):
ColliderBody(类):
ColliderDetecotor(类)
数据相关源码
从底层到高层分析一个类一个类分析
再来看下数据相关的UML,总体来说,就是ArmatureDataManager依赖DataReaderHelper把flash导出的xml文件解析成程序直接用的XXData,XXData对应于xml的某个节点,比如FrameData就对应于<f>节点(<animaton><mov><b><f>)。
BaseData
BaseData:用来表示骨骼或帧的位置、旋转、颜色、缩放。
BaseData.h
class BaseData : public cocos2d::Ref
{
public:
//Calculate two BaseData's between value(to - from) and set to self
virtual void subtract(BaseData *from, BaseData *to, bool limit);
public:
//位置,xml的x,y
float x;
float y;
//xml中z
int zOrder;
//旋转,xml的kX,kY
float skewX;
float skewY;
//缩放,xml的cX,cY
float scaleX;
float scaleY;
//啥??
float tweenRotate;
//颜色的变化属性
bool isUseColorInfo;
int a, r, g, b;
};
作为FrameData和BoneData的基类,提供骨骼的状态信息。从下文可知BoneData对应xml中的<armature<b>>中的b节点,FrameData对应xml中的<f>节点,BoneData和FrameData都有
<x,y,kX,kY,cX,cY,pX,pY,z>
等属性,BaseDa代表了这些属性。
BoneData
BoneData对应xml中的<armature<b>>中的b节点
class BoneData : public BaseData
{
public:
void addDisplayData(DisplayData *displayData);
DisplayData *getDisplayData(int index);
public:
std::string name; //! the bone's name
std::string parentName; //! the bone parent's name
//! save DisplayData informations for the Bone
cocos2d::Vector<DisplayData*> displayDataList;
//仿射变换,程序里好像没用这个属性
cocos2d::AffineTransform boneDataTransform;
};
BoneData里有displayDataList,用来放这个骨头上的皮肤(就是DisplayData), DisplayData对应xml节点中的<b<d>>节点,一个BoneData里可以有多个皮肤,换装等功能需要多个皮肤。
FrameData
FrameData对应xml中的<f>节点,就是flash里的关键帧信息。
class FrameData : public BaseData
{
public:
int frameID;
//xml中dr,这一帧长度
int duration;
//不知要他干啥
bool isTween;
//xml中dI,显示哪个图
int displayIndex;
};
DisplayData
DisplayData是SpriteDisplayData、ArmatureDisplayData、ParticleDisplayData的父类,用来表示展示节点信息。
ArmatureData
ArmatureData是对应<armature>节点,里面有这个骨骼的所有骨头,可以看成骨骼动画的骨骼。
class ArmatureData : public cocos2d::Ref
{
public:
//添加骨骼信息
void addBoneData(BoneData *boneData);
BoneData *getBoneData(const std::string& boneName);
public:
std::string name;
//多个骨头信息
cocos2d::Map<std::string, BoneData*> boneDataDic;
float dataVersion;
};
AnimationData
AnimationData对应<animation>节点,里面有多个MovementData,MovementData(下面介绍)对应xml中的mov,为flash中的一个带帧标签的动画。
class AnimationData : public cocos2d::Ref
{
public:
void addMovement(MovementData *movData);
MovementData *getMovement(const std::string& movementName);
ssize_t getMovementCount();
public:
//<animation name="Dragon">中的name
std::string name;
//所有带帧标签的动画map
cocos2d::Map<std::string, MovementData*> movementDataDic;
//所有带帧标签的动画名
std::vector<std::string> movementNames;
};
MovementData
MovementData对应xml中<animation<mov>>, 其中有所有的带帧信息的骨骼MovementBoneData(mov中的b)。
class MovementData : public cocos2d::Ref
{
public:
void addMovementBoneData(MovementBoneData *movBoneData);
MovementBoneData *getMovementBoneData(const std::string& boneName);
public:
std::string name;
//xml 中 dr
int duration;
//这怎么有个scale??
float scale;
//xml中to
int durationTo;
//xml中drTW
int durationTween;
//xml中lp
bool loop;
//带帧信息的骨骼
cocos2d::Map<std::string, MovementBoneData*> movBoneDataDic;
};
MovementBoneData
MovementBoneData对应xml中<mov<b>>的b,里面有frameList,即为关键帧信息。
class MovementBoneData : public cocos2d::Ref
{
void addFrameData(FrameData *frameData);
FrameData *getFrameData(int index);
public:
//xml中的dl
float delay;
//xml中的sc
float scale;
//这个和MovementData中的duration是不是一个??
float duration;
std::string name;
//关键帧信息
cocos2d::Vector<FrameData*> frameList;
};
小总结
xml中的各个节点和XXData的对应关系如下表,xml各个字段的意义可以参考上篇文章
再来看产生动画相关的代码
ArmatureDataManager
ArmatureDataManager利用DataReaderHelper解析出armarureDatas、animationDatas和_textureDatas。ArmatureDataManager是个单例,用到动画时会到ArmatureDataManager取得要生成动画的数据。
class ArmatureDataManager : public cocos2d::Ref
{
public:
//单例
static ArmatureDataManager *getInstance();
static void destroyInstance();
public:
void addArmatureData(const std::string& id, ArmatureData *armatureData, const std::string& configFilePath = "");
ArmatureData *getArmatureData(const std::string& id);
void removeArmatureData(const std::string& id);
void addAnimationData(const std::string& id, AnimationData *animationData, const std::string& configFilePath = "");
AnimationData *getAnimationData(const std::string& id);
void removeAnimationData(const std::string& id);
void addTextureData(const std::string& id, TextureData *textureData, const std::string& configFilePath = "");
TextureData *getTextureData(const std::string& id);
void removeTextureData(const std::string& id);
void addArmatureFileInfo(const std::string& configFilePath);
const cocos2d::Map<std::string, ArmatureData*>& getArmatureDatas() const;
const cocos2d::Map<std::string, AnimationData*>& getAnimationDatas() const;
const cocos2d::Map<std::string, TextureData*>& getTextureDatas() const;
protected:
void addRelativeData(const std::string& configFilePath);
RelativeData *getRelativeData(const std::string& configFilePath);
private:
cocos2d::Map<std::string, ArmatureData*> _armarureDatas;
cocos2d::Map<std::string, AnimationData*> _animationDatas;
cocos2d::Map<std::string, TextureData*> _textureDatas;
std::unordered_map<std::string, RelativeData> _relativeDatas;
};
主要就是armarureDatas、animationDatas、_textureDatas三个map,那这三个map是怎么产生的呢?当执行
ArmatureDataManager::getInstance()->addArmatureFileInfo(“dragon.xml”);
后,那三个map变生成了。addArmatureFileInfo代码如下
void ArmatureDataManager::addArmatureFileInfo(const std::string& configFilePath)
{
addRelativeData(configFilePath);
_autoLoadSpriteFile = true;
DataReaderHelper::getInstance()->addDataFromFile(configFilePath);
}
又调用了DataReaderHelper::getInstance()->addDataFromFile(),可知是DataReaderHelper真正完成了数据的解析。DataReaderHelper类里有一堆decodeXXX()(比如decodeArmature、decodeBone)解析xml的某个节点。看下addDataFromFile这个代码:
void DataReaderHelper::addDataFromFile(const std::string& filePath)
{
//省略一些代码 DataInfo dataInfo;
dataInfo.filename = filePathStr;
dataInfo.asyncStruct = nullptr;
dataInfo.baseFilePath = basefilePath;
if (str == ".xml")
{
DataReaderHelper::addDataFromCache(contentStr, &dataInfo);
}
else if(str == ".json" || str == ".ExportJson")
{
DataReaderHelper::addDataFromJsonCache(contentStr, &dataInfo);
}
else if(isbinaryfilesrc)
{
DataReaderHelper::addDataFromBinaryCache(contentStr.c_str(),&dataInfo);
} CC_SAFE_DELETE_ARRAY(pBytes);
}
对应不同的文件(xml、json、二进制)解析方式,xml用到是addDataFromCache
void DataReaderHelper::addDataFromCache(const std::string& pFileContent, DataInfo *dataInfo)
{
tinyxml2::XMLDocument document;
document.Parse(pFileContent.c_str()); tinyxml2::XMLElement *root = document.RootElement();
CCASSERT(root, "XML error or XML is empty."); root->QueryFloatAttribute(VERSION, &dataInfo->flashToolVersion); /*
* Begin decode armature data from xml
*/
tinyxml2::XMLElement *armaturesXML = root->FirstChildElement(ARMATURES);
tinyxml2::XMLElement *armatureXML = armaturesXML->FirstChildElement(ARMATURE);
while(armatureXML)
{
ArmatureData *armatureData = DataReaderHelper::decodeArmature(armatureXML, dataInfo); if (dataInfo->asyncStruct)
{
_dataReaderHelper->_addDataMutex.lock();
}
ArmatureDataManager::getInstance()->addArmatureData(armatureData->name.c_str(), armatureData, dataInfo->filename.c_str());
armatureData->release();
if (dataInfo->asyncStruct)
{
_dataReaderHelper->_addDataMutex.unlock();
} armatureXML = armatureXML->NextSiblingElement(ARMATURE);
} /*
* Begin decode animation data from xml
*/
tinyxml2::XMLElement *animationsXML = root->FirstChildElement(ANIMATIONS);
tinyxml2::XMLElement *animationXML = animationsXML->FirstChildElement(ANIMATION);
while(animationXML)
{
AnimationData *animationData = DataReaderHelper::decodeAnimation(animationXML, dataInfo);
if (dataInfo->asyncStruct)
{
_dataReaderHelper->_addDataMutex.lock();
}
ArmatureDataManager::getInstance()->addAnimationData(animationData->name.c_str(), animationData, dataInfo->filename.c_str());
animationData->release();
if (dataInfo->asyncStruct)
{
_dataReaderHelper->_addDataMutex.unlock();
}
animationXML = animationXML->NextSiblingElement(ANIMATION);
} /*
* Begin decode texture data from xml
*/
tinyxml2::XMLElement *texturesXML = root->FirstChildElement(TEXTURE_ATLAS);
tinyxml2::XMLElement *textureXML = texturesXML->FirstChildElement(SUB_TEXTURE);
while(textureXML)
{
TextureData *textureData = DataReaderHelper::decodeTexture(textureXML, dataInfo); if (dataInfo->asyncStruct)
{
_dataReaderHelper->_addDataMutex.lock();
}
ArmatureDataManager::getInstance()->addTextureData(textureData->name.c_str(), textureData, dataInfo->filename.c_str());
textureData->release();
if (dataInfo->asyncStruct)
{
_dataReaderHelper->_addDataMutex.unlock();
}
textureXML = textureXML->NextSiblingElement(SUB_TEXTURE);
}
}
里面有三个while,分别decodeArmature、decodeAnimation、decodeTexture,生成ArmatureData、AnimationData、TextureData之后又ArmatureDataManager::getInstance()->addArmatureData、addAnimationData、addTextureData,加到ArmatureDataManager对应map里。decodeXXX里又会调用各种decodeXX来生成相应的XXXData。
Armature
在载入了xml数据后,调用
armature = Armature::create("Dragon");
armature->getAnimation()->play("walk");
armature->getAnimation()->setSpeedScale();
armature->setPosition(VisibleRect::center().x, VisibleRect::center().y * 0.3f);
armature->setScale(0.6f);
addChild(armature);
便展示了动画,那么这是如何做到的呢?
Armature部分代码如下,ArmatureAnimation控制xml的mov节点,Bone中有Tween,这个Tween对应xml中b(MovementBoneData)
class Armature: public cocos2d::Node, public cocos2d::BlendProtocol {
protected:
//要展示动画的ArmatureData
ArmatureData *_armatureData;
BatchNode *_batchNode;
Bone *_parentBone;
float _version;
mutable bool _armatureTransformDirty;
//所有Bone
cocos2d::Map<std::string, Bone*> _boneDic; cocos2d::Vector<Bone*> _topBoneList; cocos2d::BlendFunc _blendFunc;
cocos2d::Vec2 _offsetPoint;
cocos2d::Vec2 _realAnchorPointInPoints;
//动画控制器
ArmatureAnimation *_animation;
};
Bone
部分代码如下,tweenData为当前Bone的状态,每帧都会更新这个值,并用tweenData确定worldInfo,提供Skin显示信息。tween为骨头的整个动画过程。
class Bone: public cocos2d::Node {
protected:
BoneData *_boneData; //! A weak reference to the Armature
Armature *_armature; //! A weak reference to the child Armature
Armature *_childArmature; DisplayManager *_displayManager; /*
* When Armature play an animation, if there is not a MovementBoneData of this bone in this MovementData, this bone will be hidden.
* Set IgnoreMovementBoneData to true, then this bone will also be shown.
*/
bool _ignoreMovementBoneData; cocos2d::BlendFunc _blendFunc;
bool _blendDirty; Tween *_tween; //! Calculate tween effect //! Used for making tween effect in every frame
FrameData *_tweenData; Bone *_parentBone; //! A weak reference to its parent
bool _boneTransformDirty; //! Whether or not transform dirty //! self Transform, use this to change display's state
cocos2d::Mat4 _worldTransform; BaseData *_worldInfo; //! Armature's parent bone
Bone *_armatureParentBone; };
Tween
这个是每个骨头的动画过程,见下面的movementBoneData。tweenData是Bone中tweenData的引用,在这每帧会计算这个tweenData值。
class Tween : public ProcessBase{
protected:
//! A weak reference to the current MovementBoneData. The data is in the data pool
MovementBoneData *_movementBoneData; FrameData *_tweenData; //! The computational tween frame data, //! A weak reference to the Bone's tweenData
FrameData *_from; //! From frame data, used for calculate between value
FrameData *_to; //! To frame data, used for calculate between value // total diff guan
FrameData *_between; //! Between frame data, used for calculate current FrameData(m_pNode) value Bone *_bone; //! A weak reference to the Bone TweenType _frameTweenEasing; //! Dedermine which tween effect current frame use int _betweenDuration; //! Current key frame will last _betweenDuration frames // 总共运行了多少帧 guan
int _totalDuration; int _fromIndex; //! The current frame index in FrameList of MovementBoneData, it's different from m_iFrameIndex
int _toIndex; //! The next frame index in FrameList of MovementBoneData, it's different from m_iFrameIndex ArmatureAnimation *_animation; bool _passLastFrame; //! If current frame index is more than the last frame's index
};
ArmatureAnimation
控制动画的播放,看到_tweenList,所有骨头的集合就是动画了。
class ArmatureAnimation : public ProcessBase {
protected:
//! AnimationData save all MovementDatas this animation used.
AnimationData *_animationData; MovementData *_movementData; //! MovementData save all MovementFrameDatas this animation used. Armature *_armature; //! A weak reference of armature std::string _movementID; //! Current movment's name int _toIndex; //! The frame index in MovementData->m_pMovFrameDataArr, it's different from m_iFrameIndex. cocos2d::Vector<Tween*> _tweenList;
}
如何做到每帧更新骨头的信息?
addChild(armature)后,Armaure中的onEnter(node进入舞台就会调用,比如addchild),onEnter调scheduleUpdate调scheduleUpdateWithPriority调_scheduler->scheduleUpdate。这样就每帧调用armature的update。
void Armature::update(float dt)
{
_animation->update(dt);
for(const auto &bone : _topBoneList) {
bone->update(dt);
}
_armatureTransformDirty = false;
}
又调用了animation->update(dt);及遍历调用bone->update(dt);animation->update(dt)如下:
void ArmatureAnimation::update(float dt)
{
ProcessBase::update(dt); for (const auto &tween : _tweenList)
{
tween->update(dt);
}
//省略一堆代码
}
又调用了tween->update(dt); 每一个update都会调用updateHandler(ProcessBase中update调用了update里调用updateHandler)
void Tween::updateHandler()
{
//省略一堆代码
if (_loopType > ANIMATION_TO_LOOP_BACK)
{
percent = updateFrameData(percent);
} if(_frameTweenEasing != ::cocos2d::tweenfunc::TWEEN_EASING_MAX)
{
tweenNodeTo(percent);
}
}
tweenNodeTo调用了tweenNodeTo,其中的tweenData其实就是Bone的tweenData。根据percent计算了_tweenData的变化量。
FrameData *Tween::tweenNodeTo(float percent, FrameData *node)
{
node = node == nullptr ? _tweenData : node; if (!_from->isTween)
{
percent = ;
} node->x = _from->x + percent * _between->x;
node->y = _from->y + percent * _between->y;
node->scaleX = _from->scaleX + percent * _between->scaleX;
node->scaleY = _from->scaleY + percent * _between->scaleY;
node->skewX = _from->skewX + percent * _between->skewX;
node->skewY = _from->skewY + percent * _between->skewY; _bone->setTransformDirty(true); if (node && _between->isUseColorInfo)
{
tweenColorTo(percent, node);
} return node;
}
转了一大圈终于在每帧更新了Bone中的tweenData,最后看Bone的update,其根据tweenData计算了worldInfo、worldTransform。而且updateDisplay更新skin的信息,staticcast<skin*>(display)->updateArmatureTransform();再transform = TransformConcat(_bone->getNodeToArmatureTransform(), _skinTransform);
void Bone::update(float delta)
{
if (_parentBone)
_boneTransformDirty = _boneTransformDirty || _parentBone->isTransformDirty(); if (_armatureParentBone && !_boneTransformDirty)
{
_boneTransformDirty = _armatureParentBone->isTransformDirty();
} if (_boneTransformDirty)
{
if (_dataVersion >= VERSION_COMBINED)
{
TransformHelp::nodeConcat(*_tweenData, *_boneData);
_tweenData->scaleX -= ;
_tweenData->scaleY -= ;
} _worldInfo->copy(_tweenData); _worldInfo->x = _tweenData->x + _position.x;
_worldInfo->y = _tweenData->y + _position.y;
_worldInfo->scaleX = _tweenData->scaleX * _scaleX;
_worldInfo->scaleY = _tweenData->scaleY * _scaleY;
_worldInfo->skewX = _tweenData->skewX + _skewX + _rotationZ_X;
_worldInfo->skewY = _tweenData->skewY + _skewY - _rotationZ_Y; if(_parentBone)
{
applyParentTransform(_parentBone);
}
else
{
if (_armatureParentBone)
{
applyParentTransform(_armatureParentBone);
}
} TransformHelp::nodeToMatrix(*_worldInfo, _worldTransform); if (_armatureParentBone)
{
_worldTransform = TransformConcat(_worldTransform, _armature->getNodeToParentTransform());
}
} DisplayFactory::updateDisplay(this, delta, _boneTransformDirty || _armature->getArmatureTransformDirty()); for(const auto &obj: _children) {
Bone *childBone = static_cast<Bone*>(obj);
childBone->update(delta);
} _boneTransformDirty = false;
如何展示(draw)出图片(skin)
Armature诗歌node,加入父节点后会调用其draw函数,遍历draw了bone的显示元素。
void Armature::draw(cocos2d::Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
if (_parentBone == nullptr && _batchNode == nullptr)
{
// CC_NODE_DRAW_SETUP();
} for (auto& object : _children)
{
if (Bone *bone = dynamic_cast<Bone *>(object))
{
Node *node = bone->getDisplayRenderNode(); if (nullptr == node)
continue; switch (bone->getDisplayRenderNodeType())
{
case CS_DISPLAY_SPRITE:
{
Skin *skin = static_cast<Skin *>(node);
skin->updateTransform(); BlendFunc func = bone->getBlendFunc(); if (func.src != _blendFunc.src || func.dst != _blendFunc.dst)
{
skin->setBlendFunc(bone->getBlendFunc());
}
else
{
skin->setBlendFunc(_blendFunc);
}
skin->draw(renderer, transform, flags);
}
break;
case CS_DISPLAY_ARMATURE:
{
node->draw(renderer, transform, flags);
}
break;
default:
{
node->visit(renderer, transform, flags);
// CC_NODE_DRAW_SETUP();
}
break;
}
}
else if(Node *node = dynamic_cast<Node *>(object))
{
node->visit(renderer, transform, flags);
// CC_NODE_DRAW_SETUP();
}
}
}
再skin->draw(renderer, transform, flags);会用到刚刚更新的_quad,显示出最新的图片信息。
{
Mat4 mv = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); //TODO implement z order
_quadCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(), _blendFunc, &_quad, , mv);
renderer->addCommand(&_quadCommand);
}
至此,大家对cocos2dx里的骨骼动画应该有了全面的认识,三篇文章介绍的比较粗糙,其实有些细节内容我也没看懂,不过不要在意这些细节,没有实际的改动需求的话,懂80%就可以了,细节可以需要的时候在仔细理解。
cocos2dx骨骼动画Armature源码分析(三)的更多相关文章
- cocos2dx骨骼动画Armature源码分析(一)
源码分析一body { font-family: Helvetica, arial, sans-serif; font-size: 14px; line-height: 1.6; padding-to ...
- cocos2dx骨骼动画Armature源码分析(二)
flash中数据与xml中数据关系 上篇博文从总体上介绍了cocos2dx自带的骨骼动画,这篇介绍一下导出的配置数据各个字段的含义(也解释了DragonBone导出的xml数据每个字段的含义). sk ...
- 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入
使用react全家桶制作博客后台管理系统 前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...
- tomcat源码分析(三)一次http请求的旅行-从Socket说起
p { margin-bottom: 0.25cm; line-height: 120% } tomcat源码分析(三)一次http请求的旅行 在http请求旅行之前,我们先来准备下我们所需要的工具. ...
- ABP源码分析三:ABP Module
Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...
- ABP源码分析三十一:ABP.AutoMapper
这个模块封装了Automapper,使其更易于使用. 下图描述了改模块涉及的所有类之间的关系. AutoMapAttribute,AutoMapFromAttribute和AutoMapToAttri ...
- ABP源码分析三十三:ABP.Web
ABP.Web模块并不复杂,主要完成ABP系统的初始化和一些基础功能的实现. AbpWebApplication : 继承自ASP.Net的HttpApplication类,主要完成下面三件事一,在A ...
- ABP源码分析三十四:ABP.Web.Mvc
ABP.Web.Mvc模块主要完成两个任务: 第一,通过自定义的AbpController抽象基类封装ABP核心模块中的功能,以便利的方式提供给我们创建controller使用. 第二,一些常见的基础 ...
- ABP源码分析三十五:ABP中动态WebAPI原理解析
动态WebAPI应该算是ABP中最Magic的功能之一了吧.开发人员无须定义继承自ApiController的类,只须重用Application Service中的类就可以对外提供WebAPI的功能, ...
随机推荐
- Android 在Service中弹出对话框
1.在Androidmanifest.xml中插入 <uses-permission android:name="android.permission.SYSTEM_ALERT_WIN ...
- Codeforces Round #327 (Div. 2) B. Rebranding C. Median Smoothing
B. Rebranding The name of one small but proud corporation consists of n lowercase English letters. T ...
- Web APi之HttpClient注意事项以及建议(四)
前言 之前对于用SelfHost来手动实现Web API的宿主模式,似乎不是太深入,所以本篇文章我们一起来讨论关于利用HttpClient来访问Web API上的资源来进行探讨以及注意相关事项,希望此 ...
- 窥探Swift之协议(Protocol)和委托代理(Delegate)回调的使用
协议与委托代理回调在之前的博客中也是经常提到和用到的在<Objective-C中的委托(代理)模式>和<iOS开发之窥探UICollectionViewController(四) - ...
- IOS开发之记录用户登陆状态
上一篇博客中提到了用CoreData来进行数据的持久化,CoreData的配置和使用步骤还是挺复杂的.但熟悉CoreData的使用流程后,CoreData还是蛮好用的.今天要说的是如何记录我们用户的登 ...
- [C#]浅析ref、out参数
转载:http://www.cnblogs.com/vd630/p/4601919.html#top 按引用传递的参数算是C#与很多其他语言相比的一大特色,想要深入理解这一概念应该说不是一件容易的事, ...
- 【转】 个人认为,这是最详细的 android------HttpURLConnection 类用法详解。一些教材没讲到的,它讲到了
站在巨人的肩膀上,渐渐进步. 原文链接:http://www.blogjava.net/supercrsky/articles/247449.html 针对JDK中的URLConnection连接Se ...
- jquery自定义滚动条 鼠标移入或滚轮时显示 鼠标离开或悬停超时时隐藏
一.需求: 我需要做一个多媒体播放页面,左侧为播放列表,右侧为播放器.为了避免系统滚动条把列表和播放器隔断开,左侧列表的滚动条需要自定义,并且滚动停止和鼠标离开时要隐藏掉. 二.他山之石: 案例来自h ...
- java.util.ConcurrentModificationException异常处理
ConcurrentModificationException异常处理 ConcurrentModificationException异常是Iterator遍历ArrayList或者HashMap数组 ...
- 【原创】新手入门一篇就够:从零开发移动端IM
一.前言 IM发展至今,已是非常重要的互联网应用形态之一,尤其移动互联网时代,它正以无与论比的优势降低了沟通成本和沟通代价,对各种应用形态产生了深远影响. 做为IM开发者或即将成为IM开发者的技术人员 ...