Flex. Понимание itemRenderer'ов. Часть 4. Состояния и переходы

Часть 4. Состояния и переходы

Взаимодействие с пользователем Вашего приложения это то, что itemRenderer делает лучше всего. Иногда это взаимодействие столь же просто, как представление наименования; иногда более продуманно использовать цвета; и иногда с интерактивность.

itemEditor's - истинно интерактивные контролы, но они не главные в этой статье. В этих примерах я продемонстрирую itemRenderer'ы, которые изменяют их визуальное представление, основанное на данных непосредственно или на действиях пользователя.

Состояния

<mx:State> во Flex является очень хорошим способом изменить представление itemRenderer'а. Состояния удобны, и когда объединены с переходами, дают пользовательскую обратную связь и приятный опыт.

В этом примере Вы будете создавать новый MXML itemRenderer (и запомните, Вы можете сделать это полностью в ActionScript, если Вы предпочитаете) для списка. Все эти itemRenderer'ы показывают изображение, заголовок, автора, цену и Button, чтобы купить книгу.

<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="/2006/mxml">
    <mx:Image id="bookImage" source="{data.image}"/>
    <mx:VBox height="115" width="100%" verticalAlign="top" verticalGap="0" paddingRight="10">
        <mx:Text text="{data.title}" fontWeight="bold" width="100%"/>
        <mx:Label text="{data.author}" />
        <mx:HBox id="priceBox" width="100%">
            <mx:Label text="{data.price}" width="100%"/>
            <mx:Button label="Buy"/>
        </mx:HBox>
    </mx:VBox>
</mx:HBox>

Вот то, что Вы хотите, однако, если книга не находится в заготовке (у данных есть <instock> узлы, которые в состоянии yes или no), цена и кнопка "Buy" будут невидимы. Я сделал вещи немного удобными для того, чтобы кодировать здесь, давая HBox-родителю цену и кнопку, а также свойство id. Это позволяет мне изменять видимость обоих из этих элементов, изменяя видимость HBox, priceBox.

Вы можете сделать это, перегрузкой функции set data, которую Вы сделаете, но вместо того, чтобы непосредственно изменить видимость priceBox, Вы будете использовать это определение состояния:

    <mx:states>
        <mx:State name="NoStockState">
            <mx:SetProperty target="{priceBox}" name="visible" value="false"/>
        </mx:State>
    </mx:states>

Просто поместите это ниже корня тэга <mx:HBox>.

Этот пример немного неправдоподобен, он чрезмерно сложен, чтобы сделать простую задачу, но он показывает, как использовать состояния. Имеется два состояния:

Функция set data определяет который использовать состояние, в зависимости от значения instock:

        override publicfunction set data(value:Object):void
        {
            super.data = value;
            if(data)
            {
                if(data.instock == "yes") 
                    currentState = "";
                else
                    currentState = "NoStockState";
            }
        }

currentState - свойство всех контролов UIComponent. Оно определяет, какое состояние активное. Переключаясь между состояниями, Flex-фреймворк начинает с основного состояния и затем применяет правила для данного состояния.

Примечание. Запомните, что itemRenderer'ы циклические, таким образом Вы должны всегда восстанавливать значения; никогда не используйте в itemRenderer'е if без else.

Если Вы чувствуете себя предприимчивыми, Вы можете закончить с перегрузкой set data в этом примере. Вместо этого установите currentState непосредственно в корневом тэге при использовании выражения привязки данных:

<mx:HBox xmlns:mx="/2006/mxml" width="400" currentState="{data.instock == 'yes' ? '' : 'NoStockState'}">

Значение currentState установлено, проверяя правильность data.instock, действующее с корневым тэгом - хорошая уловка, но это может быть трудно сопровождать.

Добавление элементов

В этом itemRenderer, цена и кнопка Buy появляется, только если значение instack установленов в yes. Вы можете сделать это без состояния, конечно, но если у Вашего itemRenderer'а имеет много контролов, которые будут добавлены или удалены, то состояние будет иметь больше смысла, поскольку появлением контролов можно управлять просто устанавливая свойство currentState у itemRenderer'а.

Вместо того, чтобы просто удалять цену и кнопку, Ваше состояние добит метку, говорящую пользователю, что элемент распродан. Вот модифицированное состояние.

    <mx:states>
        <mx:State name="NoStockState">
            <mx:SetProperty target="{priceBox}" name="visible" value="false"/>
            <mx:AddChild relativeTo="{priceBox}" position="before">
                <mx:Label text="-- currently not in stock --" color="#73DAF0"/>
            </mx:AddChild>
        </mx:State>
    </mx:states>

Тэг <mx:AddChild> говорит о том, чтобы добавить Label в priceBox. В дополнение к установке свойства visible у priceBox в false, дружественное сообщение заменяет его.

Снова, Вы можете добавить эту метку в функции set data, или добавить её изначально и просто установить её видимость в false, и изменить её на true в функции set data. Но я думаю, что Вы можете видеть значение состояния: если требование для instock, являющегося условием no, становится более сложным, всё, что Вы должны сделать, корректировать NoStockState; код ActionScript, который переключает состояние, остаётся тем же самым.

Примечание. Вы можете изменять состояния в режиме Design во Flex Builder'е.

Расширение элементов списка

Этот пример не работает хорошо для контролов List, но хорошо работает для VBox и Repeater. Вопрос расширения элемента на месте становится рискованным, когда список должен быть прокручен. Вообразите: у Вас есть список элементов с одинаковой высотой у всех. Теперь Вы разворачиваете высоту элемента 2. Пока неплохо: элемент 2 более высок, чем другие видимые элементы. В этом-то вся загвоздка: видимые элементы. Теперь прокрутим список. Помните, что itemRenderer'ы являются циклическими. Так, когда элемент 2 прокручивается из видимости, его itemRenderer будет перемещён в низ списка. Вы должны "сбросить" его высоту. OK, это может работать довольно просто. Теперь прокрутите список, так чтобы элемент 2 вернулся в поле зрения. Вы ожидаете, что он будет расширенной высотой. Как itemRenderer узнает, чтобы сделать это? Из предыдущих частей статьи Вы знаете, что информация исходит из данных непосредственно или из некоторого внешнего источника.

Я думаю, что изменение размеров itemRenderer'а является слишком сложным и действительно не стоящим усилия. Я полагаю, что есть лучший способ сделать это - используя VBox и Repeater. Однако, захват с Repeater - то, что создаст каждый дочерний объект. Если Вы имеете 1000 записей и используете Repeater, то Вы получите 1000 экземпляров класса Вашего itemRenderer'а.

Для этого примера Вы все ещё напишете itemRenderer, но будете использовать его как дочерний VBox. Элементы списка выглядят довольно просто: название книги и её автора. Но щёлкните на itemRenderer, и он расширяется на месте. Это достигается использованием двух тактик:

Базовое состояние itemRenderer'а довольно просто:

    <mx:HBox width="100%">
        <mx:Label text="{data.author}" fontWeight="bold"/>
        <mx:Text text="{data.title}" width="100%" fontSize="12" selectable="false"/>
    </mx:HBox>

ExpandedState добавляет дополнительные элементы, которые способствуют высоте itemRenderer'а:

    <mx:states>
        <mx:State name="ExpandedState">
            <mx:AddChild position="lastChild">
                <mx:HBox width="100%">
                    <mx:Image source="{data.image}"/>
                    <mx:Spacer width="100%"/>
                    <mx:Label text="{data.price}"/>
                    <mx:Button label="Buy"/>
                </mx:HBox>
            </mx:AddChild>
        </mx:State>
    </mx:states>

Используя itemRenderer для измения размера, столь же просто как добавление перехода:

    <mx:transitions>
        <mx:Transition fromState="*" toState="*">
            <mx:Resize target="{this}"/>
        </mx:Transition>
    </mx:transitions>

Поместите это ниже тэга <mx:states>.

Переход применяется всякий раз, когда состояние изменяется, потому что его свойства fromState и toState - подстановочные знаки. Теперь все, что Вы должны сделать, добавить обработчик событий для того, чтобы щёлкнуть на itemRenderer (добавьте событие click к корневому тэгу), и измените состояние:

    <mx:Script>
        <![CDATA[
            private function expandItem():void
            {
                if(currentState == "ExpandedState")
                    currentState = "";
                else
                    currentState = "ExpandedState";
            }
        ]]>
    </mx:Script>

Заключение

Состояния - отличный способ сделать много модификаций к визуальному представлению itemRenderer'а. Вы можете группировать изменения в Состоянии, и просто заставить всё это выполняться, устанавливая свойство currentState у itemRenderer'а.

Источник


<-Назад Вперёд->