一、位于laya.display包下的兩個(gè)類(lèi)
1.class Text extends Sprite
內(nèi)部使用了graphics.fillBorderText繪制文本
2.class Input extends Text
內(nèi)部封裝了原生的文本輸入框蟆融,并且是全局唯一的
private static function _createInputElement():void {
_initInput(area = Browser.createElement("textarea"));
_initInput(input = Browser.createElement("input"));
inputContainer = Browser.createElement("div");
inputContainer.style.position = "absolute";
inputContainer.style.zIndex = 1E5;
Browser.container.appendChild(inputContainer);
//[IF-SCRIPT] inputContainer.setPos = function(x:int, y:int):void
{ inputContainer.style.left = x + 'px'; inputContainer.style.top = y + 'px'; };
}
在focusin時(shí)把原生的textarea或input添加到div中顯示出來(lái)草巡,在focusout時(shí)再移除。
//Input.as
public function set focus(value:Boolean):void {
var input:* = nativeInput;
if (_focus !== value) {
if (value) {
if (input.target) {
input.target._focusOut();
} else {
_setInputMethod();
}
input.target = this;
_focusIn();
} else {
input.target = null;
_focusOut();
Browser.document.body.scrollTop = 0;
input.blur();
if (Render.isConchApp) {
input.setPos(-10000, -10000);
} else if (inputContainer.contains(input))
inputContainer.removeChild(input);
}
}
}
private function _setInputMethod():void {
input.parentElement && (inputContainer.removeChild(input));
area.parentElement && (inputContainer.removeChild(area));
inputElement = (_multiline ? area : input);
inputContainer.appendChild(inputElement);
if (Text.RightToLeft)
{
inputElement.style.direction = "rtl";
}
}
3.自動(dòng)獲取焦點(diǎn)型酥,并彈出鍵盤(pán)
參考彈出一個(gè)面板 想讓焦點(diǎn)自動(dòng)在輸入文本上且彈出手機(jī)鍵盤(pán)山憨,官方答復(fù)是:“軟鍵盤(pán)彈出必須有觸發(fā)行為,不支持直接默認(rèn)彈出虛擬鍵盤(pán)弥喉!”
其實(shí)在安卓上可以做到的郁竟,設(shè)置完input.focus=true之后,再調(diào)用_popupInputMethod即可由境。但I(xiàn)OS上無(wú)效棚亩。
public focusInInput() {
this.chatInput.inputChat.focus = true;
//移動(dòng)平臺(tái)在單擊事件觸發(fā)后彈出輸入法
var layaInput:any = Laya.Input;
layaInput._popupInputMethod();
}
// 移動(dòng)平臺(tái)在單擊事件觸發(fā)后彈出輸入法
private static function _popupInputMethod(e:*):void {
//e.preventDefault();
if (!Input.isInputting) return;
var input:* = Input.inputElement;
// 彈出輸入法。
input.focus();
}
二虏杰、位于laya.ui包下的兩個(gè)類(lèi)
1.class Component extends Sprite
2.class Label extends Component
//內(nèi)部封裝了一個(gè)Text讥蟆,全是調(diào)用Text的方法,基本沒(méi)有新代碼纺阔。
protected var _tf:Text;
3.class TextInput extends Label
/**@inheritDoc */
override protected function createChildren():void {
addChild(_tf = new Input());
_tf.padding = Styles.inputLabelPadding;
_tf.on(Event.INPUT, this, _onInput);
_tf.on(Event.ENTER, this, _onEnter);
_tf.on(Event.BLUR, this, _onBlur);
_tf.on(Event.FOCUS, this, _onFocus);
}
在這段代碼中瘸彤,把_tf實(shí)例化為一個(gè)Input(Input是Text的一個(gè)子類(lèi),沒(méi)有毛病)笛钝。所以可以看作TextInput是對(duì)Input的一個(gè)封裝质况,比如很多方法是這樣的:
public function get maxChars():int {
return Input(_tf).maxChars;
}
public function set maxChars(value:int):void {
Input(_tf).maxChars = value;
}
4.class TextArea extends TextInput
內(nèi)部封裝了兩個(gè)滾動(dòng)條愕宋,還有一些控制邏輯。
/**@private */
protected var _vScrollBar:VScrollBar;
/**@private */
protected var _hScrollBar:HScrollBar;
三结榄、在處理高度自適應(yīng)時(shí)中贝,追蹤了height屬性
在class TextInput extends Label看到這段:
//TextInput.as
override protected function initialize():void {
width = 128;
height = 22;
}
override public function set height(value:Number):void {
super.height = value;
_bg && (_bg.height = value);
}
調(diào)用了super.height,所以去Label.as中看一看:
//Label.as
override public function set height(value:Number):void {
super.height = value;
_tf.height = value;
}
調(diào)用了_tf.height潭陪,所以去了Text.as中:
//Text.as
override public function set height(value:Number):void {
if (value != _height) {
super.height = value;
isChanged = true;
}
}
Text.as的super.height就是指向Sprite.as類(lèi)了
//Sprite.as
public function set height(value:Number):void {
if (this._height !== value) {
this._height = value;
conchModel && conchModel.size(this._width, value);
repaint();
}
}
再往回看雄妥,因?yàn)閏lass Input extends Text,所以Input類(lèi)繼承了Text類(lèi)的 set height依溯,但是Input類(lèi)的構(gòu)造方法中老厌,又指定了寬高值:
Input.as
/**創(chuàng)建一個(gè)新的 <code>Input</code> 類(lèi)實(shí)例。*/
public function Input() {
_width = 100;
_height = 20;
multiline = false;
overflow = Text.SCROLL;
on(Event.MOUSE_DOWN, this, _onMouseDown);
on(Event.UNDISPLAY, this, _onUnDisplay);
}
四黎炉、進(jìn)入正題枝秤,textarea高度自適應(yīng)
在上述引擎源碼分析中,可以看到class Input extends Text類(lèi)中慷嗜,是有一個(gè)原生的textarea的淀弹。作為靜態(tài)屬性,是全局唯一的庆械,在Laya.init()中就會(huì)將其初始化薇溃。在HTML頁(yè)面中也能看到基本屬性:
<textarea style="position: absolute; overflow: hidden; resize: none;
transform-origin: 0px 0px 0px; background-color: transparent; border: none;
outline: none; z-index: 1; white-space: pre-wrap; color: rgb(51, 51, 51);
font-size: 32px; font-family: Arial; line-height: 32px; font-style: normal;
font-weight: normal; text-align: left; padding: 0px; width: 532px;
height: 62px;" maxlength="100000" placeholder="">
</textarea>
所以要找到原生JS是如何處理textarea高度自適應(yīng),這里參考SegmentFault 如何創(chuàng)建一個(gè)高度自適應(yīng)的textarea
第一種方式是把textarea替換成一個(gè)div缭乘,再把div的contentEditable設(shè)置為true沐序。
第二種就是利用scrollHeight,設(shè)置到height上堕绩。這個(gè)可以參考http://www.jacklmoore.com/autosize/策幼,我最終使用的就是這個(gè)庫(kù)。當(dāng)然也有手寫(xiě)的奴紧,這個(gè)網(wǎng)上帖子也比較多特姐,可以參考textarea如何實(shí)現(xiàn)高度自適應(yīng)(不出現(xiàn)滾動(dòng)條)?
第三種是1樓高贊回復(fù)黍氮,在Textarea同級(jí)放一個(gè)pre 和span標(biāo)簽唐含,然后把輸入內(nèi)容實(shí)時(shí)同步到span里。pre會(huì)隨內(nèi)容的高度變化而變化沫浆,expandingArea的高度又隨pre變化觉壶,因?yàn)閠extarea的高度100% textarea的高度會(huì)隨expandingArea變化,只要同步textarea的內(nèi)容到pre中件缸,就達(dá)到一個(gè)textarea隨內(nèi)容高度變化的目的了。
<div class="expandingArea">
<pre><span></span><br></pre>
<textarea placeholder="輸入文字"></textarea>
</div>
五叔遂、使用jackmoore autosize庫(kù)遇到的一些問(wèn)題
1.autosize庫(kù)源碼中的resize方法有這樣兩行:
ta.style.height = '';
ta.style.height = ta.scrollHeight + heightOffset + 'px';
將height置為空串這個(gè)操作他炊,經(jīng)過(guò)我的測(cè)試争剿,如果注釋掉的話,在刪除一行時(shí)痊末,textarea不會(huì)自動(dòng)縮回去蚕苇。但是這行代碼是有副作用的,那就是在輸入第一行時(shí)凿叠,會(huì)多出來(lái)一個(gè)空行涩笤。后來(lái)參考論壇回復(fù),把rows設(shè)置為1解決了盒件。在Laya中的代碼可以在Laya.init執(zhí)行后去操作Textarea的屬性蹬碧。
//Input.as
/**@private */
protected static var input:*;
/**@private */
protected static var area:*;
/**@private */
protected static var inputElement:*;
/**@private */
protected static var inputContainer:*;
private static function _createInputElement():void {
_initInput(area = Browser.createElement("textarea"));
_initInput(input = Browser.createElement("input"));
……
area無(wú)法直接訪問(wèn),所以只能這樣寫(xiě)了:
Laya.init(750, 1334, Laya.WebGL);
Laya.Input["area"].rows = 1;
2._syncInputTransform在手機(jī)上不執(zhí)行
在Input.as的_focusIn方法中炒刁,最后有這么一段:
// 輸入框重定位恩沽。
_syncInputTransform();
if (!Render.isConchApp && Browser.onPC)
Laya.timer.frameLoop(1, this, _syncInputTransform);
為什么只有在Browser.onPC時(shí),才執(zhí)行這個(gè)重定位呢翔始,原因不明罗心。那么只能偵聽(tīng)Laya.Event.INPUT事件,自己再手動(dòng)執(zhí)行_syncInputTransform方法了城瞎。
/**
* 在輸入期間渤闷,如果 Input 實(shí)例的位置改變,調(diào)用_syncInputTransform同步輸入框的位置脖镀。
*/
private function _syncInputTransform():void {
var inputElement:Object = nativeInput;
var transform:Object =
Utils.getTransformRelativeToWindow(this, padding[3], padding[0]);
var inputWid:int = _width - padding[1] - padding[3];
var inputHei:int = _height - padding[0] - padding[2];
if (Render.isConchApp) {
inputElement.setScale(transform.scaleX, transform.scaleY);
inputElement.setSize(inputWid, inputHei);
inputElement.setPos(transform.x, transform.y);
} else {
//[IF-SCRIPT]inputContainer.style.transform =
inputContainer.style.webkitTransform =
"scale(" + transform.scaleX + "," +
transform.scaleY + ") rotate(" + (Laya.stage.canvasDegree) + "deg)";
//[IF-SCRIPT]inputElement.style.width = inputWid + 'px';
//[IF-SCRIPT]inputElement.style.height = inputHei + 'px';
//[IF-SCRIPT]inputContainer.style.left = transform.x + 'px';
//[IF-SCRIPT]inputContainer.style.top = transform.y + 'px';
}
}
這里原始的_syncInputTransform會(huì)考慮padding后飒箭,去設(shè)置style.width和height。因?yàn)槲覀円呀?jīng)在autosize中計(jì)算了寬高认然,所以_syncInputTransform方法中就不用再計(jì)算了补憾,只要設(shè)置inputContainer.style.left和top即可。
六卷员、彈出鍵盤(pán)不遮擋輸入框
參考H5移動(dòng)端彈出鍵盤(pán)時(shí)遮擋輸入框盈匾,使用了文中第一種方式
private initLayaInput():void{
var ta:any = Laya.Input["area"];
//避免textarea出現(xiàn)空行
ta.rows = 1;
ta.onfocus = this.taFocusIn;
ta.onblur = this.taFocusOut;
}
private taFocusIn: any = this.delayScrollBody.bind(this);
private delayScrollBody(): void {
Laya.timer.loop(500,this,this.scrollDocBody);
}
private scrollDocBody():void{
Laya.Browser.document.body.scrollTop =
Laya.Browser.document.body.scrollHeight;
}
private taFocusOut: any = this.removeDelayScrollBody.bind(this);
private removeDelayScrollBody(): void {
Laya.timer.clear(this,this.scrollDocBody);
}
另外,也可以參考移動(dòng)端iOS第三方輸入法遮擋底部input及android鍵盤(pán)回落后留白問(wèn)題
七毕骡、焦點(diǎn)控制
//Input.as
/**創(chuàng)建一個(gè)新的 <code>Input</code> 類(lèi)實(shí)例削饵。*/
public function Input() {
_width = 100;
_height = 20;
multiline = false;
overflow = Text.SCROLL;
on(Event.MOUSE_DOWN, this, _onMouseDown);
on(Event.UNDISPLAY, this, _onUnDisplay);
}
private function _onUnDisplay(e:Event = null):void {
focus = false;
}
private function _onMouseDown(e:Event):void {
focus = true;
}
// 移動(dòng)平臺(tái)最后單擊畫(huà)布才會(huì)調(diào)用focus
// 因此 調(diào)用focus接口是無(wú)法都在移動(dòng)平臺(tái)立刻彈出鍵盤(pán)的
public function set focus(value:Boolean):void {
var input:* = nativeInput;
if (_focus !== value) {
if (value) {
if (input.target) {
input.target._focusOut();
} else {
_setInputMethod();
}
input.target = this;
_focusIn();
} else {
input.target = null;
_focusOut();
Browser.document.body.scrollTop = 0;
input.blur();
if (Render.isConchApp) {
input.setPos(-10000, -10000);
} else if (inputContainer.contains(input))
inputContainer.removeChild(input);
}
}
}
這里看到,偵聽(tīng)Event.MOUSE_DOWN獲得了焦點(diǎn)未巫。那么點(diǎn)擊INPUT之外的區(qū)域窿撬,是怎么失去焦點(diǎn)的呢。經(jīng)過(guò)查找叙凡,在MouseManager.as中找到了:
private function onMouseDown(ele:*):void {
if (Input.isInputting && Laya.stage.focus &&
Laya.stage.focus["focus"] && !Laya.stage.focus.contains(_target)) {
// 從UI Input組件中取得Input引用
// _tf 是TextInput的屬性
var pre_input:* = Laya.stage.focus['_tf'] || Laya.stage.focus;
var new_input:Input = ele['_tf'] || ele;
// 新的焦點(diǎn)是Input的情況下劈伴,不需要blur;
// 不過(guò)如果是Input和TextArea之間的切換握爷,還是需要重新彈出輸入法跛璧;
if (new_input is Input && new_input.multiline == pre_input.multiline)
pre_input['_focusOut']();
else
pre_input.focus = false;
}
TouchManager.I.onMouseDown(ele, _tTouchID, _isLeftMouse);
}
參考一下Input.focus代碼严里,可以看出執(zhí)行focus=false與執(zhí)行_focusOut方法的區(qū)別。最重要一點(diǎn)是追城,focus=false會(huì)額外執(zhí)行input.blur()刹碾,這將導(dǎo)致收起軟鍵盤(pán)
// 移動(dòng)平臺(tái)最后單擊畫(huà)布才會(huì)調(diào)用focus
// 因此 調(diào)用focus接口是無(wú)法都在移動(dòng)平臺(tái)立刻彈出鍵盤(pán)的
public function set focus(value:Boolean):void {
var input:* = nativeInput;
if (_focus !== value) {
if (value) {
if (input.target) {
input.target._focusOut();
} else {
_setInputMethod();
}
input.target = this;
_focusIn();
} else {
input.target = null;
_focusOut();
Browser.document.body.scrollTop = 0;
input.blur();
if (Render.isConchApp) {
input.setPos(-10000, -10000);
} else if (inputContainer.contains(input))
inputContainer.removeChild(input);
}
}
}
默認(rèn)情況下,在點(diǎn)擊聊天發(fā)送按鈕后座柱,輸入框input會(huì)失去焦點(diǎn)迷帜,導(dǎo)致軟鍵盤(pán)收起。這時(shí)繼續(xù)輸入色洞,就要重新點(diǎn)擊輸入框input戏锹,是不是很煩。但是想更改這個(gè)默認(rèn)設(shè)定锋玲,也只能改源碼了……
if ((new_input instanceof laya.display.Input )&& new_input.multiline==pre_input.multiline)
pre_input['_focusOut']();
else if((new_input instanceof laya.ui.Button )&& new_input.name == "sendBtn"){
//聊天點(diǎn)擊發(fā)送按鈕(name==sendBtn)景用,輸入框不會(huì)失去焦點(diǎn)
}else{
pre_input.focus=false;
}
八、沒(méi)有MOUSE_UP事件
在輸入框輸入文本時(shí)惭蹂,直接點(diǎn)擊發(fā)送按鈕伞插,在部分瀏覽器上會(huì)出現(xiàn)只是縮回鍵盤(pán),文本并未發(fā)出的情況盾碗。經(jīng)過(guò)檢查媚污,是沒(méi)有拋出MOUSE_UP事件,進(jìn)而導(dǎo)致沒(méi)有CLICK事件廷雅。將發(fā)送按鈕改為偵聽(tīng)MOUSE_DOWN事件即可耗美。