Flex Mobile - повышение скорости прокрутки списка

Представьте себе приложение для планшета, в котором две области содержимого отображаются рядом. Они полностью заполняют экран, поэтому их высота составляет 100%, а ширина - 50%. Предположим, мы добавляем список в один контейнер. Естественно, этот список будет занимать половину всего экрана.

Теперь к моей проблеме, возможно ли, что прокрутка с высокой частотой кадров невозможна со списками такого размера? У меня самый простой AS3 ItemRenderer, и я по-прежнему не могу получить более 30 кадров в секунду во время прокрутки. Теперь странная часть, если я добавлю что-то в другой контейнер, скажем, другой список или другие компоненты, производительность прокрутки списка упадет до 20 с. Так что и рядом с 40+ кадрами в секунду вы не видите рекламу Adobe на их шоу MAX.

Я тестирую iPad2 и 3, и даже со статическими значениями прокрутка не очень хороша. Теперь, если я включаю потоковую передачу значений и вызываю метод set data ItemRenderer, частота кадров падает еще на 2–3 кадра.

Мой (почти) полный рендерер выглядит так, но даже если я разделю его, чтобы просто отобразить одно текстовое поле, отключил все, что происходит в set data, а также установил только размер одного текстового поля в layoutContents, производительность такая, как описано, около 30, если отображается только список, и 20 секунд, если отображается и другое.

    //FCStyleableTextField is just a StyleableTextField with an additional ID

    private var _textFields:Vector.<FCStyleableTextField>;
    private var _oldValues:Dictionary;
    private var _sym:Symbol;

    public function GridRenderer() {
        super();
        _textFields = new Vector.<FCStyleableTextField>();
        _oldValues = new Dictionary();
    }

    override protected function createChildren():void {

        var _symLabel:FCStyleableTextField = new FCStyleableTextField();
        _symLabel.editable = false;
        _symLabel.selectable = false;
        _symLabel.multiline = false;
        _symLabel.id="sym";
        _symLabel.setStyle("fontSize", fontSize);
        _symLabel.textColor = 0xc0c0c0;
        _textFields.push(_symLabel);
        addChild(_symLabel);

        var fidLen:int = fids.length;
        for (var i:int = 0; i<fidLen; i++) {
            var _fid_lbl:FCStyleableTextField = new FCStyleableTextField();
            _fid_lbl.selectable = false;
            _fid_lbl.editable = false;
            _fid_lbl.multiline = false;
            _fid_lbl.id = String(fids[i]);
            _fid_lbl.textColor = 0xc0c0c0;
            _fid_lbl.setStyle("textAlign", "right");
            _fid_lbl.setStyle("fontSize", fontSize);
            _fid_lbl.text = " ";
            _textFields.push(_fid_lbl);
            addChild(_fid_lbl);

            if(i>visibleColumns) {
                _fid_lbl.includeInLayout = false;
                _fid_lbl.visible = false;
            }
        }
    }


    override public function set data(value:Object):void {
        if(!value) return;

        if(data) {
            // check if the value's symbolName is different than the current
            // data's symbolName, if so, the itemRenderer has been 
            // recycled, thus we need to reset the old fid values
            if((value as Symbol).symbolName != (data as Symbol).symbolName)
                _oldValues = new Dictionary();
        }

        super.data = value;

        _sym = data as Symbol;

        try {
            var textLen:int = _textFields.length;
            for (var i:int = 0; i<textLen;i++) {
                var lbl:FCStyleableTextField = _textFields[i];
                if(lbl.id == "sym") {
                    lbl.text = _sym.symbolName;
                    lbl.truncateToFit();
                } else {
                    if(lbl.id == _sym.fidList.fidMap[lbl.id].fidId && lbl.text != _sym.fidList.fidMap[lbl.id].fieldValue) {
                        var time:int = new Date().time;
                        var timerName:String = _sym.symbolName+","+lbl.id+","+grid;
                        globalTimer.addTimer(timerName, time, "reset", lbl, null, null);

                        var _oldVal:* = _oldValues[lbl.id];
                        var _newVal:* = _sym.fidList.fidMap[lbl.id].fieldValue;

                        // basic color formatting
                        if(Number(_newVal) > Number(_oldVal))
                            lbl.textColor = 0x40c040;
                        else if(Number(_newVal) < Number(_oldVal))
                            lbl.textColor = 0xf05050;

                        // add + to change and changePercent fids if value is positive
                        if(lbl.id == "56") {
                            if(_newVal >0)
                                lbl.text = "+" + _newVal;
                            else
                                lbl.text = String(_newVal);
                        } else if(lbl.id == "11") {
                            if(_newVal >0)
                                lbl.text = "+" + _newVal;
                            else 
                                lbl.text = String(_newVal);
                        } else 
                            lbl.text = String(_newVal);

                        if(!_sym.fidList.fidMap[lbl.id].fieldValue)
                            lbl.text =" ";

                        _oldValues[lbl.id] = _newVal;
                    } 
                }

                lbl.truncateToFit();
            }       
        } catch (e:Error) { /* nothing to do here -> try/catch required due to async symbolassembly */ }
    } 

    override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void {
        var viewWidth:Number  = unscaledWidth  - paddingLeft - paddingRight;
        var viewHeight:Number = unscaledHeight - paddingTop  - paddingBottom;
        var _previousLabel:FCStyleableTextField;
        var textLen:int = _textFields.length;

        for(var i:int =0; i<textLen;i++) {
            var lbl:FCStyleableTextField = _textFields[i];
            graphics.beginFill(0x808080, .3);
            lbl.height = viewHeight;
            lbl.y = paddingTop;

            if(lbl.id=="sym") {
                lbl.width = 95;
            } else if (lbl.id == "35000") {
                lbl.width = 24;
            } else { 
                lbl.width = optimalColWidth;
            }

            _previousLabel ? lbl.x = (_previousLabel.x + _previousLabel.width): lbl.x = paddingLeft;
            graphics.drawRect(lbl.x+lbl.width, 1, 1, unscaledHeight-1);

            lbl.commitStyles();
            _previousLabel = lbl;
            graphics.endFill();
        }
    }

Тем не менее, я почти уверен, что это не средство визуализации элементов вызывает замедление, потому что, как я уже сказал, он стоит 2, может быть, 3 кадра по сравнению с средством визуализации, которое отображает только одно текстовое поле. Я скорее думаю, что Flex почему-то не может обрабатывать количество отображаемых векторов одновременно, возможно ли что-то подобное? И есть ли способ повысить производительность? Я уже отключил значения потоковой передачи в реальном времени, как только пользователь прокручивает список, так что flex в основном просто прокручивает растровые изображения (поскольку LabelItemRenderer автоматически включает cacheasbitmap), но это позволило получить, возможно, 4 кадра. Какие уловки, ребята, делают, чтобы прокрутка стала более плавной?


person AlBirdie    schedule 18.07.2012    source источник
comment
evtimmy.com/2011/10/424 - вы проверяли эту презентацию?   -  person Alexander Farber    schedule 18.07.2012
comment
Я не уверен, можно ли дать конкретный ответ на этот вопрос. Как вы оцениваете частоту кадров при прокрутке?   -  person JeffryHouser    schedule 18.07.2012
comment
@AlexanderFarber, да, есть. Это слайды с шоу MAX, о котором я говорил. По сути, ничего нового там нет, и ничего, что я еще не рассмотрел (и не реализовал) в рендерере, например, кеширование.   -  person AlBirdie    schedule 18.07.2012
comment
@ www.Flextras.com, я измеряю производительность с помощью монитора fps. Я не совсем уверен, есть ли только один ответ на этот вопрос. Как я уже сказал, не уверен, что здесь происходит, просто немного шокирован кадрами, которые я получаю для очень простых вещей.   -  person AlBirdie    schedule 18.07.2012
comment
Единственное, что мне, возможно, придется добавить, это то, что отладочные сборки предположительно работают на iOS намного медленнее, чем сборки выпуска. Я сам это видел.   -  person JeffryHouser    schedule 18.07.2012
comment
:) Отладочные сборки дают мне меньше 5 кадров в секунду. Собственно, я ничего не делаю, кроме как выпускаю сборки на iOS-устройствах. Итак, чтобы подтвердить это, средство визуализации элементов, показанное выше, в значительной степени оптимизировано, не так ли? Потому что тогда я могу перестать возиться с этим и поискать недостающие кадры где-нибудь в другом месте. ;)   -  person AlBirdie    schedule 18.07.2012
comment
@Al_Birdy что такое монитор fps? Мне было интересно, как измеряется FPS, когда я читал эту презентацию.   -  person Alexander Farber    schedule 18.07.2012
comment
Я не знаю, что такое FCStyleableTextField или Symbol; поэтому сложно сказать, насколько оптимизирован ваш код. Я не понимаю, почему вы, кажется, храните старые значения. Похоже, ваш layoutContents рисует заливку на полном itemRenderer для каждого элемента вашего вектора _textFields. Поскольку цвет заливки жестко запрограммирован, можете ли вы нарисовать один?   -  person JeffryHouser    schedule 19.07.2012
comment
Как я уверен, вы знаете, что метод набора данных вызывается каждый раз, когда вы прокручиваете список, и поэтому он должен быть как можно меньше. Я бы никогда не вставил туда петлю. Просто подумайте, но прервите цикл и некоторый другой код в другую функцию и вызовите эту функцию по таймеру на 1 выстрел. Каждый раз, когда вводятся установленные данные, обновляйте таймер. Вы можете установить таймер на быстрое время, например, 10 мс   -  person The_asMan    schedule 19.07.2012
comment
Это должно дать вам гораздо лучший ответ, поскольку тонна кода не обрабатывается, хотя этого не должно быть. Однако с помощью этого метода вы не увидите обновления, пока прокрутка не будет замедлена или остановлена. Итак, у вас есть некоторые плюсы и минусы, которые следует учитывать.   -  person The_asMan    schedule 19.07.2012
comment
@ www.Flextras.com, FCStyleableTextField является (как указано в коде) подклассом StyleableTextfield с дополнительным идентификатором. Символ - это модель, состоящая из строки с именем и словаря, в котором хранятся значения. LayoutContents просто рисует одну строку после каждого TextField, что делает средство визуализации похожим на сетку. Поэтому сложно сделать один рисунок, так как я не знаю заранее размеры текстовых полей. Я сохраняю старые значения, чтобы можно было форматировать цвета текста. Зеленый - большее значение, красный - меньшее.   -  person AlBirdie    schedule 19.07.2012
comment
@The_asMan, я знаю, что цикл далек от оптимального, позвольте мне посмотреть, что я могу с этим поделать. Странно то, что даже без цикла в заданных данных, просто установив текст одной метки, я все равно не могу получить значительно более высокую частоту кадров.   -  person AlBirdie    schedule 19.07.2012
comment
@Al_Birdy шаг за шагом :)   -  person The_asMan    schedule 19.07.2012
comment
@The_asMan, как обычно;).   -  person AlBirdie    schedule 19.07.2012


Ответы (1)


Выяснилось, что использование setElementSize() и setElementPosition() вместо использования ширины / высоты и x / y имеет большое значение. Получено 3 кадра в секунду при начальной скорости прокрутки и 8 кадров в секунду после рендеринга каждого элемента. Так что сейчас я довольно близок к 30 кадрам в секунду, все еще не близко к тому, что вы можете сделать с помощью нативного приложения, но я считаю, что это так же хорошо, как с Flex и таким массивным средством рендеринга.

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

person AlBirdie    schedule 31.07.2012