Вставка большого объема текста в TextArea приводит к тайм-ауту выполнения скрипта - PullRequest
3 голосов
/ 23 декабря 2010

(Flex 3) У меня есть компонент TextArea, который должен содержать содержимое буфера обмена пользователя. TextArea выполняет работу относительно хорошо в большинстве случаев, но при вставке большого объема данных я не могу получить содержимое в компоненте из-за тайм-аута выполнения скрипта.

Я сделал честную сделку по расследованию, чтобы попытаться найти надежную работу. Я обнаружил, что TextArea использует IUITextField (который в моем случае является экземпляром TextField во время выполнения) и выполняет обработку получения данных вставки, а затем выдает событие, когда это делается.

Я не нашел способа взглянуть на источник TextField, поскольку он является классом в библиотеке playerglobal.swc.

Есть ли способ для меня, возможно, увидеть источник этого класса или есть что-то, чего мне не хватает в моем подходе, чтобы найти способ сделать эту работу?

П.С .: Я знаю, что был бы альтернативный способ достижения желаемых результатов, но я бы хотел, чтобы это конкретное решение работало.

Ответы [ 5 ]

3 голосов
/ 19 октября 2011

Поскольку TextField не хочет принимать наше событие вставки, мы найдем кого-то еще, кто это сделает.Вот обходной путь mx: TextArea:

MXML:

<mx:Canvas id="canvas" paste="pasteHandler(event)">
    <mx:TextArea id="textArea" textInput="if (event.text.length > 1) event.preventDefault();" 
     keyDown="keyDown(event)" mouseEnabled="false"/>
</mx:Canvas>

AS:

private function keyDown(event:KeyboardEvent):void{
    //When CTRL-V is pressed set focus to canvas
    if(event.keyCode==86 && event.ctrlKey)
        canvas.setFocus();
}

Вам также необходимо включить элементы буфера обмена в ContextMenu Canvas и сбросить фокусв TextArea после завершения вставки.

Получил решение для блокировки вставки из: flex: как предотвратить вставку (ctrl + V) при вводе текста flex3?

2 голосов
/ 19 октября 2011

К сожалению, я думаю, что вы не можете перехватить все типы событий пользовательского ввода в текстовых полях, используя Flex 3.

Если вы можете переключиться на Flex 4 (таким образом, используя новый FTE), то вам следуетдобавьте прослушиватель для события TextOperationEvent.CHANGING, которое является отменяемым событием.В обработчике вы можете проверить объем добавляемого текста и, если его слишком много, отменить событие (event.preventDefault()) и добавить его в несколько кадров.

Приятная вещь об этом событиив том, что он запускается также для события, такого как «нажатие клавиши удаления с выделенным текстом» или операций вырезания / копирования / вставки / удаления.На самом деле я использую его для ранней проверки некоторых текстовых полей, которые я никак не могу обнаружить в Flex 3.

EDIT

Может быть, я нашел решение .. Я заметил, что свойство "restrict", которое позволяет фильтровать символы, разрешенные в текстовом поле, поддерживается во встроенном классе TextField, и это не добавленная функцияс помощью обертывающего компонента Flex: интересно для нашего porpuse.

Поэтому я попытался поиграть с ним, и обнаружил, что установка restrict="" в вашей TextArea предотвратит блокирование пользовательского интерфейса при вставке большого блока текста,потому что он применяется "рано" на уровне игрока.Приятно то, что даже с этим параметром TextArea будет отправлять событие textInput, поэтому вы можете отслеживать событие вставки и решать, что делать с новым текстом: обновите свойство text за один шаг,запустить инкрементное обновление или просто проигнорировать событие.

Скопируйте и вставьте следующий код, чтобы увидеть пример (протестировано с Flex 3.6 и Flash Player 11):

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
    xmlns:mx="http://www.adobe.com/2006/mxml"
    minWidth="955" minHeight="600"
    layout="vertical"
    horizontalAlign="center">

    <mx:Script>
        <![CDATA[

            protected function generateClipboard():void {
                var largeData:String = "";
                while (largeData.length < 1000000) {
                    largeData += "Lorem ipsum ... ";
                }
                System.setClipboard(largeData);
            }

            protected function ontextarea1_textInput(event:TextEvent):void {
                if (event.text.length < 100) {
                    // NAIVE CODE: just to demonstrate that you can
                    // programmatically modify the text.. you should "merge"
                    // the new text with existing content here
                    textArea.text += event.text;
                } else {
                    // do something here, like starting
                    // to add text block by block in
                    // multiple frames
                }
            }

        ]]>
    </mx:Script>

    <mx:TextArea id="textArea" width="100%" height="100%" restrict="" textInput="ontextarea1_textInput(event)"/>

    <mx:Button click="generateClipboard()" label="Set clipboard"/>

</mx:Application>

Сложная часть будетбыть правильно установленным текстом в событии textInput и «добавочной» загрузкой большого текста.

2 голосов
/ 23 декабря 2010

Эта проблема часто возникает, когда в список отображения добавляется большое количество сглаженного текста. Как только текст отрендерен, все снова в порядке. Вы можете обойти эту проблему, если обрабатываете предопределенный текст, разделив большие части текста на множество мелких и добавив их на сцену по частям (скажем, 20 строк текста одновременно), ожидая один кадр между каждым, что позволяет обновить экран.

Я еще не пробовал, но я бы предложил добавить прослушиватель событий в TextArea и проверить Event.RENDER, если текст был изменен. Если это так, вы можете удалить весь текст, который был добавлен с момента последнего события рендеринга, и покадрово добавить его, как в примере выше.

Кроме того, попробуйте использовать встроенные шрифты вместо встроенных и отключить сглаживание или снизить его качество.

1 голос
/ 21 октября 2011

С такими большими текстами это невозможно (вы упомянули, что текст размером около 7 МБ).

Если вы посмотрите на источник mx: TextArea, он вставляет текст, просто устанавливая его текстовое свойство TextField для введенной вами строки:
Из TextArea.as вокруг строки 2050:

 if (textChanged || htmlTextChanged)
    {
        // If the 'text' and 'htmlText' properties have both changed,
        // the last one set wins.
        if (isHTML)
            textField.htmlText = explicitHTMLText;
        else
            textField.text = _text;

        textFieldChanged(false, true)

        textChanged = false;
        htmlTextChanged = false;
    }

Если вы создадите пример приложения с TextField и попытаетесь установить его текстовое свойство с большой строкой, вы получите тайм-ауты сценария. (Я получил таймаут, когда я попробовал его с длиной строки 1 МБ):

   import flash.text.TextField;
    tf = new TextField();
    addChild(tf);
    tf.multiline = true;
    tf.wordWrap = true;
    tf.width= 600
    tf.height = 500
    var str:String = "";
    for (var i:int=0; i<100000; i++) {
        str = str+"0123456789"
    }
    tf.text = str

Это самый простой способ отображения текста и времени ожидания. Flex постарается сделать с этим текстовым полем еще дюжину вещей, прежде чем выложить его ... поэтому он также сдается в текстах гораздо меньшего размера.

Единственное возможное решение - создать собственное текстовое поле и постепенно добавлять текст - Adobe рекомендует использовать TextField.appendText ():

import flash.text.TextField;
public function test(){
    tf = new TextField();
    addChild(tf);
    tf.multiline = true;
    tf.wordWrap = true;
    tf.width= 600
    tf.height = 500
    addEventListener(Event.ENTER_FRAME,onEF);
}

private var cnt:int = 0;

private function onEF(event:Event):void{
    if (cnt>200) return;
    trace (cnt);
    var str:String = "";
        //pasting around 50K at once seems ideal.
    for (var i:int=0; i<5000; i++) {
        str = str+"0123456789"
    }
    tf.appendText(str);
    cnt++;
}

Этот скрипт может добавить 10 МБ текста в TextField ... хотя после добавления 1 МБ он становится все медленнее (в конце итерации в конце потребовалось около 1 секунды). И если вы попытаетесь сделать что-нибудь с этим текстовым полем (например, изменение размера, изменение текста без appendText (), добавление его на сцену после его завершения ...), вы получите тайм-аут сценария.

Таким образом, полное решение состоит в том, чтобы захватить трюк обертки холста события вставки (решение Уильяма), а затем постепенно добавлять текст в пользовательский компонент, который использует appendText для добавления текста. (используйте flash.text.TextField и добавьте его на сцену с помощью mx_internal :: $ addChild ()). Я не проверял, можете ли вы удалить полученное чудовище с помощью removeChild, не вызывая таймаут сценария :).

1 голос
/ 18 октября 2011

В текстовом поле Spark событие вставки корректно срабатывает.Возможно, есть более чистый способ сделать это, но это решение я нашел.

MXML:

<s:TextArea id="textArea" changing="changeHandler(event)" 
paste="pasteHandler(event)" />

AS:

private var pastedText:String;
private var textPosition:int=0;
//Number of characters to read per frame
private var chunkSize:int=1000;

private function changeHandler(event:TextOperationEvent):void{
    if(String(event.operation)=="[object PasteOperation]"){
        event.preventDefault();
    }
}

private function pasteHandler(event:Event):void{
    pastedText = String(Clipboard.generalClipboard.getData(ClipboardFormats.TEXT_FORMAT));
    this.addEventListener(Event.ENTER_FRAME,frameHandler);
}

private function frameHandler(event:Event):void{
    if( textPosition + chunkSize > pastedText.length ){
        chunkSize = pastedText.length - textPosition;
        this.removeEventListener(Event.ENTER_FRAME,frameHandler);
    }
    textArea.text += pastedText.substr(textPosition,chunkSize);
    textPosition += chunkSize;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...