Пользовательский компонент не обновляет список отображения

Я создал компонент, который в основном представляет собой метр (расширяет контейнер с изменяемым скином). Оболочка компонента содержит два прямоугольника (фон и фактический счетчик). Этот компонент получает коллекцию массивов со следующими значениями полей, max и min (коллекция массивов, которую я передаю, является Bindable). Когда я инициализирую и передаю данные, которые будут использоваться для рисования, он отлично работает (имейте в виду - работает только в первый раз). Я создал кнопку, которая изменяет массив данных и показывает текущее значение счетчика. Очевидно, значение счетчика меняется каждый раз, когда я изменяю набор ArrayCollection, который я установил для рендеринга (не хочу говорить «dataProvider», потому что это не поставщик данных Flex, а просто переменная) ... вот код .. .

    public class meter extends SkinnableContainer {


    [SkinPart( required = "true" )]
    public var meter:spark.primitives.Rect;

    [SkinPart( required = "true" )]
    public var meterBackground:spark.primitives.Rect;

    private var _dataProvider:ArrayCollection;
    private var _renderDirty:Boolean = false;

    public function Meter() {
        super();
    }

    public function set dataProvider( value:Object ):void {
        if ( value )
        {
            if(value is ArrayCollection)
               {
                   _renderDirty = true;
                _dataProvider = value as ArrayCollection;
               }                
            if(_dataProvider)
            {
                _dataProvider.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, propertyChanged);//used both eventlisteners but none actually ever fire off
                _dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE,collectionChanged);
            }
            invalidateDisplayList();//only happens the first time
        }
    }

    private function collectionChanged(event:CollectionEvent):void
    {
        Alert.show("In collection Change");//this never goes off when I change the "dataProvider"
        _renderDirty = true;
        invalidateDisplayList();
    }

    private function propertyChanged(event:PropertyChangeEvent):void
    {
        Alert.show("In property Change");//this never goes off when I change the "dataProvider"
        _renderDirty=true;
         invalidateDisplayList();
    }

    public function get dataProvider():Object {
        return _dataProvider;
    }        

    override protected function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number ):void {


        if(_dataProvider))
        {
        var span:Number = unscaledWidth / _dataProvider[0].max;
        var meterWidth:Number = _dataProvider[0].value * span;

        meter.width = meterWidth;
        _renderDirty = false;
        }

    }

А это mxml-код, в котором я меняю поле «значение» ....

   <fx:Script>
    <![CDATA[
    [Bindable]
        private var meterData:ArrayCollection = new ArrayCollection([                
            {value:80, max:100, min:0}
        ]);

        protected function mySlider_changeHandler(event:Event):void
        {
            meterData[0].value = Math.round(mySlider.value)*10;                
        }

        protected function button1_clickHandler(event:MouseEvent):void
        {
            // TODO Auto-generated method stub
            var array:ArrayCollection = testMeter.dataProvider as ArrayCollection;
            var value:Number = array[0].value as Number;
            Alert.show(value.toString());
           // testMeter.meter.width= Math.random()*100;
        }

    ]]>//initial value in "meterData" does get drawn...but when it's changed with the slider..nothing happens..

   <custom:Meter id="testMeter" dataProvider="{meterData}" /> 
    <s:HSlider id="mySlider" 
               liveDragging="true"
               dataTipPrecision="0" 
               change="mySlider_changeHandler(event)"
               value="3"/>
    <s:Button click="button1_clickHandler(event)"/>

Можете ли вы помочь мне понять, что происходит ?? Спасибо, ребята !!!


person Joe    schedule 12.07.2011    source источник
comment
Это не ответ, но я недавно создал аналогичный компонент в качестве доказательства концепции: jeffryhouser.com/index.cfm/2011/6/26/ Вы можете удивиться нашим различным подходам. :-)   -  person JeffryHouser    schedule 13.07.2011


Ответы (1)


Проблема в том, что вы не сбрасываете dataProvider и не делаете ничего для запуска события collectionChange. Я разберусь с каждым индивидуально. Вот как вы сбрасываете dataProvider:

testMeter.dataProvider = newDataPRovider;

Но вы этого не делаете. Таким образом, метод set никогда не будет выполняться после первого начального набора.

Если вам нужно, чтобы событие collectionChange сработало, вам нужно изменить коллекцию. На самом деле вы этого не делаете. Вы меняете объект в коллекции. Этот код не меняет коллекцию:

meterData[0].value = Math.round(mySlider.value)*10; 

Он просто изменяет одно свойство в одном из объектов коллекции. Попробуйте что-то вроде этого:

var newObject = meterData[0];
newObject['value'] = Math.round(mySlider.value)*10
meterData.addItem(newObject); 

Этот код должен запускать событие colletionChange, даже если у вас его нет.

У меня есть еще несколько мыслей, не связанных с вашим основным вопросом. Не забудьте удалитьEventListeners в методе набора dataProvider, например:

public function set dataProvider( value:Object ):void {
    if ( value )
    {
// remove the event listeners here before changing the value
// that should allow the object to have no extra 'references' that prevent it from being garbage collected
                _dataProvider.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, propertyChanged);
                _dataProvider.removeEventListener(CollectionEvent.COLLECTION_CHANGE,collectionChanged);

            if(value is ArrayCollection)
               {
                   _renderDirty = true;
                _dataProvider = value as ArrayCollection;
               }                
            if(_dataProvider)
            {
                _dataProvider.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, propertyChanged);//used both eventlisteners but none actually ever fire off
                _dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE,collectionChanged);
            }
            invalidateDisplayList();//only happens the first time
        }
    }

И вы сказали:

(не хочу говорить «dataProvider», потому что это не провайдер данных Flex, а просто переменная)

DataProvider в списке Flex, DataGrid или DataGroup также является «просто переменной». Я не понимаю, чем реализация «Flex Framework» сильно отличается от того, что вы сделали, в любом случае концептуально. Поскольку вы специально ожидаете минимальное и максимальное значения, возможно, вам следует использовать объект значения с этими явными свойствами вместо ArrayCollection.

person JeffryHouser    schedule 12.07.2011
comment
Так что, если бы это была не коллекция массивов, а объект ?? Как я могу узнать, изменилось ли оно ?? - person Joe; 13.07.2011
comment
Вы хотите знать, когда объект изменяется, этот объект должен будет отправить событие, сообщающее "вам", что он изменился. По сути, это именно то, что делает событие collectionChange ArrayCollection. - person JeffryHouser; 13.07.2011
comment
поэтому я добавляю dispatchEvent (PropertyChange) (не уверен, как это реализовать ..) где-нибудь в моем коде ... и вуаля ??? Куда именно добавить ?? Мне жаль, но я невежественен: D - person Joe; 13.07.2011
comment
@ Джо, я не уверен, что Вуаля прав. Вы должны добавить в свой объект событие отправки, которое изменяется всякий раз, когда что-либо изменяется. Использование чего-то подобного в методе набора довольно распространено. Затем в классе, который использует объект, который изменяется, вам нужно будет прослушать событие изменения и внести соответствующие изменения. Анализируйте любой заданный класс фреймворка на предмет образцов. - person JeffryHouser; 13.07.2011