版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/xiaoyang0611/article/details/49128077
egret的WebView实现(基于egret2.5)
标签(空格分隔): egret webview

客户端开发中有一种很常见的场景就是展现web页面,也就是 WebView 。例如 IOS 、 Android ,都为开发者提供了 WebView 组件,而在 egret 中暂时并没有提供 WebView 组件,所以我们只能自己动手实现一个能在 egret 引擎中可用的 WebView 组件。

由于egret运行在浏览器中,所以想要实现一个能展现web页面的组件,第一想法应该是使用iframe。
那么这些问题需要解决:

与 egret 集成,并提供与 egret 组件一致的调用方式
融入 egret 的展现,即 WebView 的坐标、大小需要与 egret 一致
查看 egret 源码,我们不难发现 egret 的 HtmlInput 组件也是使用上面我们所考虑的方式实现在游戏中进行文本输入的。所以,我们可以采用 egret HtmlInput 类似的方式实现 WebView 。因此,我们首先从 egret HtmlInuput 源码入手:

  1. Egret中HtmlInput的实现
    1.1 HtmlInput实现方式:
    在 egret.web.js 中可以很找到 egret.web.HTMLInput 的初始化相关的源码:

HtmlInput.prototype._initStageDelegateDiv = function (container, canvas) {this.canvas = canvas;var self = this;var stageDelegateDiv;if (!stageDelegateDiv) {stageDelegateDiv = document.createElement("div");this.StageDelegateDiv = stageDelegateDiv;stageDelegateDiv.id = "StageDelegateDiv";container.appendChild(stageDelegateDiv);self.initValue(stageDelegateDiv);self._inputDIV = document.createElement("div");self.initValue(self._inputDIV);self._inputDIV.style.width = "0px";self._inputDIV.style.height = "0px";self._inputDIV.style.left = 0 + "px";self._inputDIV.style.top = "-100px";self._inputDIV.style[egret.web.getPrefixStyleName("transformOrigin")] = "0% 0% 0px";stageDelegateDiv.appendChild(self._inputDIV);this.canvas.addEventListener("click", function (e) {if (self._needShow) {self._needShow = false;self._stageText._onClickHandler(e);self.show();}else {if (self._inputElement) {self.clearInputElement();self._inputElement.blur();self._inputElement = null;}}});self.initInputElement(true);self.initInputElement(false);}};HtmlInput.prototype.initInputElement = function (multiline) {var self = this;//增加1个空的textareavar inputElement;if (multiline) {inputElement = document.createElement("textarea");inputElement.style["resize"] = "none";self._multiElement = inputElement;inputElement.id = "egretTextarea";}else {inputElement = document.createElement("input");self._simpleElement = inputElement;inputElement.id = "egretInput";}inputElement.type = "text";self._inputDIV.appendChild(inputElement);inputElement.setAttribute("tabindex", "-1");inputElement.style.width = "1px";inputElement.style.height = "12px";self.initValue(inputElement);inputElement.style.outline = "thin";inputElement.style.background = "none";inputElement.style.overflow = "hidden";inputElement.style.wordBreak = "break-all";//隐藏输入框inputElement.style.opacity = 0;inputElement.oninput = function () {if (self._stageText) {self._stageText._onInput();}};};

很明显,是在id为 StageDelegateDiv 的 dom 中嵌入 和 ,并在这两个 html 元素上做文章。

1.2 HtmlInput 渲染展现:
那么 text 输入框和 textarea 文本输入域是如何正确展现到 egret 的 canvas 上的呢?
在 egret.web.js 的 egret.web.HTML5StageText 中我们看到如下代码:

         p.$setTextField = function (textfield) {this.$textfield = textfield;return true;};/*** @private**/p.$addToStage = function () {this.htmlInput = egret.web.$getTextAdapter(this.$textfield);};/*** @private**/p._initElement = function () {var point = this.$textfield.localToGlobal(0, 0);var x = point.x;var y = point.y;var cX = this.$textfield.$renderMatrix.a;var cY = this.$textfield.$renderMatrix.d;var scaleX = this.htmlInput.$scaleX;var scaleY = this.htmlInput.$scaleY;this.inputDiv.style.left = x * scaleX + "px";this.inputDiv.style.top = y * scaleY + "px";if (this.$textfield.multiline) {this.inputDiv.style.top = (y) * scaleY + "px";this.inputElement.style.top = (-this.$textfield.lineSpacing / 2) + "px";}else {this.inputDiv.style.top = y * scaleY + "px";this.inputElement.style.top = 0 + "px";}this._gscaleX = scaleX * cX;this._gscaleY = scaleY * cY;};

从这几段关键代码中我们可以看到, HtmlInput 的文本使用 egret.TextFiled 组件进行渲染的,并且 HtmlInput 对应的 Dom 位置、宽高直接使用 TextFiled 的位置、宽高,这样 HtmlInputDom 就能与 egret 的坐标宽高一致了。

1.3 HtmlInput文本输入:
那么是怎么实现输入的呢?
在 egret.web.js 的 egret.web.HTML5StageText 的源码中能找到如下代码:

HTML5StageText.prototype.executeShow = function () {
var self = this;
//打开
this.inputElement.value = this.getText();if(this.inputElement.onblur==null)this.inputElement.onblur=this.onBlurHandler.bind(this);this.getText(); if (this.inputElement.onblur == null) { this.inputElement.onblur = this.onBlurHandler.bind(this); } this.getText();if(this.inputElement.onblur==null)this.inputElement.onblur=this.onBlurHandler.bind(this);this.resetStageText();
if (this.KaTeX parse error: Expected '}', got 'EOF' at end of input: …xlength", this.textfield.maxChars);
}
else {
this.inputElement.removeAttribute(“maxlength”);
}
this.inputElement.selectionStart = this.inputElement.value.length;
this.inputElement.selectionEnd = this.inputElement.value.length;
this.inputElement.focus();
};


从最后一行代码可以看到,直接调用 HtmlInput 对应的 dom 的 onfocus() ,从而弹出输入框,即可进行文本输入操作。

  1. WebView的实现
    看完 HtmlInput 的实现,可见 WebView 的实现有所不同:
    HtmlInput 依赖 egret.TextField 来进行渲染展现,egret.TextField 是 egret 中内建的组件,所以 HtmlInput 坐标宽高直接使用 textfield 的坐标、宽高就可以了。但是 WebView 依赖的是 iframe ,是 html 的标准组件,并不是 egret 的内建组件,而且在 egret 的 canvas 之外,所以必然会涉及到 webview 组件的坐标宽高的换算。结合上文提到的 WebView 应该达到的效果,我们可以考虑如下实 现WebView :

WebView 继承degret.displayObjectContailner,override相关方法(getX,getY,setX,setY,getWidht,getHeight,setWidth,setHeight等)以提供与egret引擎中内建组件一致的调用方式:

class WebView extends egret.DisplayObjectContainer {

private _x:number=0;
private _y:number=0;
private _width:number=0;
private _height:number=0;
private _src:string="";
//getter  setter ...

}


与 HtmlInput 一样,在 StageDelegatDiv 中 加入 iframe, WebView 对象持有此 iframe dom 的引用,但是必须根据其 x,y,width,height 进行相应的坐标、大小的转化并进行显示:
要实现以上想法,以下需要处理:

(1) 了解egret的scalemode( 缩放模式)
(2) 根据各种scalemode的实现方式换算webview的x,y,width,height
(3) 控制WebView中iframe的样式及显示时机

2.1 egret 的 scalemode (缩放模式)
egret的六种 scalemode :

StageScaleMode.NO_SCALE = "noScale";
StageScaleMode.SHOW_ALL = "showAll";
StageScaleMode.NO_BORDER = "noBorder";
StageScaleMode.EXACT_FIT = "exactFit";
StageScaleMode.FIXED_WIDTH = "fixedWidth";
StageScaleMode.FIXED_HEIGHT = "fixedHeight";

StageScaleMode.NO_SCALE:
不缩放应用程序内容。即使在更改播放器视口大小时,它仍然保持不变。如果播放器视口比内容小,则可能进行一些裁切。 在此模式下,舞台尺寸(Stage.stageWidth,Stage.stageHeight)始终跟播放器视口大小保持一致。

StageScaleMode.SHOW_ALL:
保持原始宽高比缩放应用程序内容,缩放后应用程序内容的较宽方向填满播放器视口,另一个方向的两侧可能会不够宽而留有黑边。在此模式下,舞台尺寸(Stage.stageWidth,Stage.stageHeight)始终等于初始化时外部传入的应用程序内容尺寸。

StageScaleMode.NO_BORDER:
保持原始宽高比缩放应用程序内容,缩放后应用程序内容的较窄方向填满播放器视口,另一个方向的两侧可能会超出播放器视口而被裁切在此模式下,舞台尺寸(Stage.stageWidth,Stage.stageHeight)始终等于初始化时外部传入的应用程序内容尺寸。

StageScaleMode.EXACT_FIT:
不保持原始宽高比缩放应用程序内容,缩放后应用程序内容正好填满播放器视口。在此模式下,舞台尺寸(Stage.stageWidth,Stage.stageHeight)始终等于初始化时外部传入的应用程序内容尺寸。

StageScaleMode.FIXED_WIDTH :
保持原始宽高比缩放应用程序内容,缩放后应用程序内容在水平和垂直方向都填满播放器视口,但只保持应用程序内容的原始高度不变,宽度可能会改变。在此模式下,舞台高度(Stage.stageHeight)始终等于初始化时外部传入的应用程序内容高度。舞台宽度(Stage.stageWidth)由当前的缩放比例与播放器视口宽度决定。

StageScaleMode.FIXED_HEIGHT :
保持原始宽高比缩放应用程序内容,缩放后应用程序内容在水平和垂直方向都填满播放器视口,但只保持应用程序内容的原始高度不变,宽度可能会改变。在此模式下,舞台高度(Stage.stageHeight)始终等于初始化时外部传入的应用程序内容高度。舞台宽度(Stage.stageWidth)由当前的缩放比例与播放器视口宽度决定。

引用官方的一张图:

egret官方文章的介绍: egret scalemode ,有很详细的讲解。

可见,不同的缩放模式对于舞台大小、显示宽高的大小有决定性的影响。我们仅考虑SHOW_ALL,NO_BORDER,FIXED_WIDTH,FIXED_HEIGHT这几种在实际生产中用得比较多的缩放模式。
各种缩放模式下是如何计算舞台宽高、显示宽高的呢?
在egret.js的源码中,我们可以找到如下代码片段:

/*** @private* 计算舞台显示尺寸* @param scaleMode 当前的缩放模式* @param screenWidth 播放器视口宽度* @param screenHeight 播放器视口高度* @param contentWidth 初始化内容宽度* @param contentHeight 初始化内容高度*/ScreenAdapter.prototype.calculateStageSize = function (scaleMode, screenWidth, screenHeight, contentWidth, contentHeight) {var displayWidth = screenWidth;var displayHeight = screenHeight;var stageWidth = contentWidth;var stageHeight = contentHeight;var scaleX = (screenWidth / stageWidth) || 0;var scaleY = (screenHeight / stageHeight) || 0;switch (scaleMode) {case egret.StageScaleMode.EXACT_FIT:break;case egret.StageScaleMode.FIXED_HEIGHT:stageWidth = Math.round(screenWidth / scaleY);break;case egret.StageScaleMode.FIXED_WIDTH:stageHeight = Math.round(screenHeight / scaleX);break;case egret.StageScaleMode.NO_BORDER:if (scaleX > scaleY) {displayHeight = Math.round(stageHeight * scaleX);}else {displayWidth = Math.round(stageWidth * scaleY);}break;case egret.StageScaleMode.SHOW_ALL:if (scaleX > scaleY) {displayWidth = Math.round(stageWidth * scaleY);}else {displayHeight = Math.round(stageHeight * scaleX);}break;default:stageWidth = screenWidth;stageHeight = screenHeight;break;}return {stageWidth: stageWidth,stageHeight: stageHeight,displayWidth: displayWidth,displayHeight: displayHeight};

2.2 根据各种 scalemode 的实现方式换算 webview 的 x,y,width,height
我们可以清楚看到各种缩放模式下,舞台宽高、显示宽高的计算,因此我们可以根据相应的缩放模式来换算WebView的x,y,widht,height对应在web窗口中的具体数值,这里以WebView.setwidth(w:number)为例:

public set width(value:number) {this._width = value;if(this._scaleMode==egret.StageScaleMode.FIXED_WIDTH || this._scaleMode==egret.StageScaleMode.FIXED_HEIGHT ){this._iframe.width=this._width/this._stageW*this._windowW+"px";this._iframeWrapper.style.width=this._width/this._stageW*this._windowW+"px";}if(this._scaleMode==egret.StageScaleMode.SHOW_ALL || this._scaleMode==egret.StageScaleMode.NO_BORDER ) {if(this._windowW==this._displayW){this._iframe.style.width = this._width / this._stageW * this._windowW + "px";this._iframeWrapper.style.width = this._width / this._stageW * this._windowW + "px";}else{this._iframe.style.width = this._width / this._stageW * this._displayW + "px";this._iframeWrapper.style.width = this._width / this._stageW * this._displayW + "px";}}}

setX,setY,setHeight等方法坐标换算与上述方式相同。
通过坐标换算,我们就能以egret组件一样的方式调用WebView并且展现在正确的位置。

2.3 控制 WebView 中 iframe 的样式及显示时机
坐标宽高都确定了,就可以设置 iframe 的样式了,这个比较简单,通过js即可设置样式。另外,iframe在网页完全加载完再显示会比较友好。

  1. IOS 系统中 iframe 宽高撑大、无法滑动的解决方案
    测试如上方法开发的 WebView 时,发现在 ios 中, iframe 宽高会被里面的内容撑大、并且无法滑动:

通过google,找到了解决方案:
http://stackoverflow.com/questions/23083462/how-to-get-an-iframe-to-be-responsive-in-ios-safari
或者
http://davidwalsh.name/scroll-iframes-ios

即:
给iframe一个父级div,设置如下css属性:

#iframe-wrapper {-webkit-overflow-scrolling: touch;overflow-y: scroll;/* important:  dimensions or positioning here! */
}#iframe-wrapper iframe {/* nada! */
}
private _iframeWrapper:HTMLDivElement=null;private _iframe:HTMLIFrameElement=null;/*** @param src*/public constructor(src:string){super();var stageDelegateDom:HTMLElement=document.getElementById("StageDelegateDiv"),playerContainer:HTMLElement=stageDelegateDom.parentElement;var iframeWrapperDom=document.getElementById("iframe-wrapper");if(!iframeWrapperDom){iframeWrapperDom=document.createElement("div");iframeWrapperDom.style.display="none";iframeWrapperDom.attributes['style'].value+='position:absolute;-webkit-overflow-scrolling: touch;overflow-y: scroll;';//解决iframe在ios下的显示问题iframeWrapperDom.id="iframe-wrapper";stageDelegateDom.appendChild(iframeWrapperDom);}this._iframeWrapper=<HTMLDivElement>iframeWrapperDom;this._iframeWrapper.style.display="none";this._iframeWrapper.style.opacity="0";var iframe = document.createElement("iframe"),t=new Date().getTime();iframe.src=src;iframe.id="webview-iframe-"+t;iframe.name="webview-iframe-"+t;iframe.style.position="absolute";iframe.style.top="0";iframe.style.left="0";iframe.style.opacity="0";iframe.style.display='none';iframe.frameBorder='0';iframe.border="0";this._iframeWrapper.appendChild(iframe);this._iframe=<HTMLIFrameElement>document.getElementById("webview-iframe-"+t);var self=this;this._iframe.οnlοad=function(){self._iframeWrapper.style.opacity="1";self._iframe.style.opacity="1";}}
  1. WebView.ts完整代码及使用方式:
    WebView.ts:
/*** WebView* 适配FIXED_WIDTH、FIXED_HEIGHT、NO_BORDER、SHOW_ALL四种缩放模式* 暂未考虑屏幕大小改变、屏幕旋转以及单页面多Webplay实例的情形* Created by yxiao on 2015/9/30.*/
class WebView extends egret.DisplayObjectContainer {private _x:number=0;private _y:number=0;private _width:number=0;private _height:number=0;private _src:string="";private _scaleMode:string=egret.MainContext.instance.stage.scaleMode;private _stageW:number;private _stageH:number;private _windowW:number;private _windowH:number;private _displayH:number;private _displayW:number;private _designH:number;private _designW:number;private _iframeWrapper:HTMLDivElement=null;private _iframe:HTMLIFrameElement=null;/*** @param src*/public constructor(src:string){super();var stageDelegateDom:HTMLElement=document.getElementById("StageDelegateDiv"),playerContainer:HTMLElement=stageDelegateDom.parentElement;var iframeWrapperDom=document.getElementById("iframe-wrapper");if(!iframeWrapperDom){iframeWrapperDom=document.createElement("div");iframeWrapperDom.style.display="none";iframeWrapperDom.attributes['style'].value+='position:absolute;-webkit-overflow-scrolling: touch;overflow-y: scroll;';//解决iframe在ios下的显示问题iframeWrapperDom.id="iframe-wrapper";stageDelegateDom.appendChild(iframeWrapperDom);}this._iframeWrapper=<HTMLDivElement>iframeWrapperDom;this._iframeWrapper.style.display="none";this._iframeWrapper.style.opacity="0";var iframe = document.createElement("iframe"),t=new Date().getTime();iframe.src=src;iframe.id="webview-iframe-"+t;iframe.name="webview-iframe-"+t;iframe.style.position="absolute";iframe.style.top="0";iframe.style.left="0";iframe.style.opacity="0";iframe.style.display='none';iframe.frameBorder='0';iframe.border="0";this._iframeWrapper.appendChild(iframe);this._iframe=<HTMLIFrameElement>document.getElementById("webview-iframe-"+t);var self=this;this._iframe.οnlοad=function(){self._iframeWrapper.style.opacity="1";self._iframe.style.opacity="1";}this._stageW=egret.MainContext.instance.stage.stageWidth;this._stageH=egret.MainContext.instance.stage.stageHeight;this._windowW=window.innerWidth;this._windowH=window.innerHeight;this._designH=parseInt(playerContainer.attributes['data-content-height'].value);this._designW=parseInt(playerContainer.attributes['data-content-width'].value);var stageSize = egret.sys.screenAdapter.calculateStageSize(egret.MainContext.instance.stage.scaleMode, this._windowW, this._windowH, this._designW, this._designH);this._displayH=stageSize.displayHeight;this._displayW=stageSize.displayWidth;console.log("windowW:"+this._windowW);console.log("stageW:"+this._stageW);console.log("disPlayW:"+this._displayW);console.log("windowH:"+this._windowH);console.log("stageH:"+this._stageH);console.log("displayH:"+this._displayH);}public show():void {this._iframe.style.display='block';this._iframeWrapper.style.display='block';}public destroy():void {if(this._iframe){this._iframeWrapper.style.display="none";this._iframeWrapper.removeChild(this._iframe);}}public get width():number {return this._width;}public set width(value:number) {this._width = value;if(this._scaleMode==egret.StageScaleMode.FIXED_WIDTH || this._scaleMode==egret.StageScaleMode.FIXED_HEIGHT ){this._iframe.width=this._width/this._stageW*this._windowW+"px";this._iframeWrapper.style.width=this._width/this._stageW*this._windowW+"px";}if(this._scaleMode==egret.StageScaleMode.SHOW_ALL || this._scaleMode==egret.StageScaleMode.NO_BORDER ) {if(this._windowW==this._displayW){this._iframe.style.width = this._width / this._stageW * this._windowW + "px";this._iframeWrapper.style.width = this._width / this._stageW * this._windowW + "px";}else{this._iframe.style.width = this._width / this._stageW * this._displayW + "px";this._iframeWrapper.style.width = this._width / this._stageW * this._displayW + "px";}}}public get height():number {return this._height;}public set height(value:number) {this._height = value;if(this._scaleMode==egret.StageScaleMode.FIXED_WIDTH || this._scaleMode==egret.StageScaleMode.FIXED_HEIGHT ) {this._iframe.height=this._height/this._stageH*this._windowH+"px";this._iframeWrapper.style.height=this._height/this._stageH*this._windowH+"px";}if(this._scaleMode==egret.StageScaleMode.SHOW_ALL || this._scaleMode==egret.StageScaleMode.NO_BORDER ) {if(this._windowH==this._displayH){this._iframe.style.height = this._height / this._stageH * this._windowH + "px";this._iframeWrapper.style.height = this._height / this._stageH * this._windowH + "px";}else{this._iframe.style.height = this._height / this._stageH * this._displayH + "px";this._iframeWrapper.style.height = this._height / this._stageH * this._displayH + "px";}}}public set x(value:number) {this._x = value;if(this._scaleMode==egret.StageScaleMode.FIXED_WIDTH   || this._scaleMode==egret.StageScaleMode.FIXED_HEIGHT) {this._iframeWrapper.style.left = this._x / this._stageW * this._windowW + "px";}if(this._scaleMode==egret.StageScaleMode.SHOW_ALL || this._scaleMode==egret.StageScaleMode.NO_BORDER ) {if(this._windowW==this._displayW){this._iframeWrapper.style.left = this._x / this._stageW * this._windowW + "px";}else{this._iframeWrapper.style.left = this._x / this._stageW * this._displayW + "px";}}}public set y(value:number) {this._y = value;if(this._scaleMode==egret.StageScaleMode.FIXED_WIDTH  || this._scaleMode==egret.StageScaleMode.FIXED_HEIGHT ) {this._iframeWrapper.style.top = this._y / this._stageH * this._windowH + "px";}if(this._scaleMode==egret.StageScaleMode.SHOW_ALL || this._scaleMode==egret.StageScaleMode.NO_BORDER){if(this._windowH==this._displayH){this._iframeWrapper.style.top = this._y / this._stageH * this._windowH + "px";}else{this._iframeWrapper.style.top =this._y / this._stageH * this._displayH + "px";}}}public get x():number {return this._x;}public get y():number {return this._y;}public get src():string {return this._src;}public set src(value:string) {this._src = value;}
}

使用方式:

var webview=new WebView("http://www.sina.com");
webview.x=100;
webview.y=100;
webview.width = 500;
webview.height = 800;
webview.show();

效果:

  1. 后续
    考虑屏幕大小改变、屏幕旋转以及单页面多Webplay实例的情形
    ————————————————
    版权声明:本文为CSDN博主「小样儿0611」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/xiaoyang0611/article/details/49128077

egret 使用frame转载相关推荐

  1. 白鹭 修改底层 egret.js 库后再次编译 成 新的库

    1.egret create_lib egret 2.修改 tsconfig 文件 { "compilerOptions": { "target": " ...

  2. I,P,B帧和PTS,DTS的关系 转载

    基本概念: I frame :帧内编码帧 又称intra picture,I 帧通常是每个 GOP(MPEG 所使用的一种视频压缩技术)的第一个帧,经过适度地压缩,做为随机访问的参考点,可以当成图象. ...

  3. 计算 webView 显示内容后实际高度

    两种方法,方法1可以得到内容的实际高度,方法2得到了将内容显示完整后的 webView 的尺寸(包含 UIEdgeInsets) - (void)webViewDidFinishLoad:(UIWeb ...

  4. 人脸、人眼检测与跟踪

    #include <opencv2/opencv.hpp> #include <iostream> #include <vector> using namespac ...

  5. Runtime实战之定制TabBarItem大小

    方案一:UIEdgeInsets 适用场景: 适合APP的TabBarItemImage的图片资源放在本地 图片超出tabbar的高度,需移动其位置,来进行适应 弊端: 若在本地配置好后,tabbar ...

  6. 0218 图片的添加

    //银行卡支付 UIView *line123=[[UIView alloc]initWithFrame:CGRectMake(0, 79, SCREEN_WIDTH, 2)]; line123.ba ...

  7. pandas apply()函数参数 args

    #!/usr/bin/pythonimport pandas as pddata = {'year':[2000,2001,2002,2001,2002],'value':[1.5,1.7,3.6,2 ...

  8. po 时不生效时, 不要用点方法

    Dot notation for message sending is not supported in lldb. Use bracket notation and cast the result ...

  9. spring security4 问题

    <headers>             <frame-options policy="SAMEORIGIN" /> </headers> s ...

  10. setjmp 与 longjmp

    setjmp和longjmp是C语言独有的,只有将它们结合起来使用,才能达到程序控制流有效转移的目的,按照程序员的预先设计的意图,去实现对程序中可能出现的异常进行集中处理. 先来看一下这两个函数的定义 ...

最新文章

  1. OpenCV3.3中 K-最近邻法(KNN)接口简介及使用
  2. Spring Cloud整合Redis
  3. SAP EWM - 物料主数据 - EWM系统存储视图属性
  4. iOS 动态更换icon
  5. android第五天晚:surfaceView
  6. 建筑师 第一类斯特林数
  7. 新冠疫情,或加速银行数字化服务转型
  8. kaggle房价预测特征意思_Kaggle竞赛丨房价预测(House Prices)
  9. 2018年10微型计算机接口技术,微机原理及接口技术
  10. 仿照扫描全能王python程序实现
  11. Java中类的修饰符有哪些?
  12. Sick编码器CanOpen通信
  13. 经验分享:半桥电路的工作原理及注意问题
  14. 内网远程控制软件哪个好用
  15. 7.26 5 优化浪漫 恋爱中的经济学
  16. html snippets怎么配置,用Snippets创建自己喜欢的注释格式
  17. vue内使用 cytoscape(数据可视化)
  18. 【实验】多周期CPU微程序设计
  19. Docker的常用命令
  20. 从键盘上输入一个4×4整数矩阵,以主对角线为对称轴,将左下角的每一个数组元素与对应的右上角的数据元素进行比较(例如a[2][1]与a[1][2]进行比较),将其中的较大者送入右上角对应位置,最后输出该

热门文章

  1. (转)解决office软件无法卸载也无法安装的顽固问题
  2. java继承和接口的区别_java中的接口与继承的区别
  3. 2022.08.24【R语言】|pheatmap外接函数调用时不能有NA/NaN/Inf(arg10)报错问题及解决方案
  4. HTML网页设计制作大作业 html+css+js萌宠之家 网页设计与实现
  5. thinkpad卡在logo界面_win7系统开机卡在Thinkpad LOGO画面无法进入桌面的解决方法
  6. ICP算法步骤——matlab
  7. 音频噪声抑制(1):经典滤波器篇
  8. DevOps学习笔记--Jerrit介绍
  9. Linux家目录被误删除恢复
  10. 小米手机连接MAC电脑