Flex组合框中显示项目的自定义项目渲染器

时间:2008-11-06 18:30:11

标签: flex actionscript-3 combobox mxml itemrenderer

我在组合框中使用自定义项目渲染器来显示自定义绘图而不是默认文本标签。

这适用于下拉列表,但显示的项目(当列表关闭时)仍然是我的对象的文本表示。

有没有办法让显示的项目呈现与下拉列表中项目相同的方式?

6 个答案:

答案 0 :(得分:9)

默认情况下,您无法执行此操作。但是,如果扩展ComboBox,则可以轻松添加此功能。这是一个简单的例子,它是一个粗略的版本,可能需要测试/调整,但它显示了如何实现这一目标。

package
{
    import mx.controls.ComboBox;
    import mx.core.UIComponent;

    public class ComboBox2 extends ComboBox
    {
        public function ComboBox2()
        {
            super();
        }

        protected var textInputReplacement:UIComponent;

        override protected function createChildren():void {
            super.createChildren();

            if ( !textInputReplacement ) {
                if ( itemRenderer != null ) {
                    //remove the default textInput
                    removeChild(textInput);

                    //create a new itemRenderer to use in place of the text input
                    textInputReplacement = itemRenderer.newInstance();
                    addChild(textInputReplacement);
                }
            }
        }

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

            if ( textInputReplacement ) {
                textInputReplacement.width = unscaledWidth;
                textInputReplacement.height = unscaledHeight;
            }
        }
    }
}

答案 1 :(得分:5)

我尝试了上述解决方案,但发现当组合框关闭时,selectedItem没有显示。需要额外的代码行才能将itemRenderer数据属性绑定到selectedItem:

            if ( !textInputReplacement ) {
                    if ( itemRenderer != null ) {
                            //remove the default textInput
                            removeChild(textInput);

                            //create a new itemRenderer to use in place of the text input
                            textInputReplacement = itemRenderer.newInstance();

                            // ADD THIS BINDING:
                            // Bind the data of the textInputReplacement to the selected item
                            BindingUtils.bindProperty(textInputReplacement, "data", this, "selectedItem", true);

                            addChild(textInputReplacement);
                    }
            }

答案 2 :(得分:3)

我进一步扩展了Dane的代码。在某些情况下,点击没有打开我的渲染器的下拉框,我注意到正常的Flex ComboBox外观没有触发。因此,在 replaceTextInput()中,我添加了一些额外的事件侦听器,并保存对用于显示外观的ComboBox按钮的引用。现在它的行为就像普通的ComboBox一样。

以下是代码:

    package
    {
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;

    import mx.binding.utils.BindingUtils;
    import mx.controls.Button;
    import mx.controls.ComboBox;
    import mx.core.IFactory;
    import mx.core.UIComponent;
    import mx.events.DropdownEvent;

    /**
     * Extension of the standard ComboBox that will use the assigned 'itemRenderer'
     * for both the list items and the selected item.
     * 
     * Based on code from:
     * http://stackoverflow.com/questions/269773/flex-custom-item-renderer-for-the-displayed-item-in-the-combobox
     */
    public class ComboBoxFullRenderer extends ComboBox
    {
    protected var textInputReplacement:UIComponent;
    private var _increaseW:Number = 0;
    private var _increaseH:Number = 0;


    /**
     * Keeps track of the current open/close state of the drop down list. 
     */
    protected var _isOpen:Boolean = false;

    /**
     * Stores a reference to the 'Button' which overlays the ComboBox.  Allows
     * us to pass events to it so skins are properly triggered. 
     */
    protected var _buttonRef:Button = null;


    /**
     * Constructor. 
     */
    public function ComboBoxFullRenderer() {
        super();
    }


    /**
     * Sets a value to increase the width of our ComboBox to adjust sizing. 
     * 
     * @param val Number of pixels to increase the width of the ComboBox.
     */
    public function set increaseW(val:Number):void {
        _increaseW = val;
    }

    /**
     * Sets a value to increase the height of our ComboBox to adjust sizing. 
     * 
     * @param val Number of pixels to increase the height of the ComboBox.
     */
    public function set increaseH(val:Number):void {
        _increaseH = val;
    }


    /**
     * Override the 'itemRenderer' setter so we can also replace the selected
     * item renderer.
     *  
     * @param value The renderer to be used to display the drop down list items
     *   and the selected item.
     */
    override public function set itemRenderer(value:IFactory):void {
        super.itemRenderer = value;
        replaceTextInput();
    }


    /**
     * Override base 'createChildren()' routine to call our 'replaceTextInput()'
     * method to replace the standard selected item renderer.
     *  
     * @see #replaceTextInput();
     */
    override protected function createChildren():void {
        super.createChildren();
        replaceTextInput();
    }


    /**
     * Routine to replace the ComboBox 'textInput' child with our own child
     * that will render the selected data element.  Will create an instance of
     * the 'itemRenderer' set for this ComboBox. 
     */
    protected function replaceTextInput():void {
        if ( !textInputReplacement ) {
            if ( this.itemRenderer != null && textInput != null ) {
                //remove the default textInput
                removeChild(textInput);

                //create a new itemRenderer instance to use in place of the text input
                textInputReplacement = this.itemRenderer.newInstance();
                // Listen for clicks so we can open/close the drop down when
                // renderer components are clicked.  
                textInputReplacement.addEventListener(MouseEvent.CLICK, _onClick);
                // Listen to the mouse events on our renderer so we can feed them to
                // the ComboBox overlay button.  This will make sure the button skins
                // are activated.  See ComboBox::commitProperties() code.
                textInputReplacement.addEventListener(MouseEvent.MOUSE_DOWN, _onMouseEvent);
                textInputReplacement.addEventListener(MouseEvent.MOUSE_UP, _onMouseEvent);
                textInputReplacement.addEventListener(MouseEvent.ROLL_OVER, _onMouseEvent);
                textInputReplacement.addEventListener(MouseEvent.ROLL_OUT, _onMouseEvent);
                textInputReplacement.addEventListener(KeyboardEvent.KEY_DOWN, _onMouseEvent);

                // Bind the data of the textInputReplacement to the selected item
                BindingUtils.bindProperty(textInputReplacement, "data", this, "selectedItem", true);

                // Add our renderer as a child.
                addChild(textInputReplacement);

                // Listen for open close so we can maintain state.  The
                // 'isShowingDropdown' property is mx_internal so we don't
                // have access to it. 
                this.addEventListener(DropdownEvent.OPEN, _onOpen);
                this.addEventListener(DropdownEvent.CLOSE, _onClose);

                // Save a reference to the mx_internal button for the combo box.
                //  We will need this so we can call its dispatchEvent() method.
                for (var i:int = 0; i < this.numChildren; i++) {
                    var temp:Object = this.getChildAt(i);
                    if (temp is Button) {
                        _buttonRef = temp as Button;
                        break;
                    } 
                }
            }
        }
    }


    /**
     * Detect open events on the drop down list to keep track of the current
     * drop down state so we can react properly to a click on our selected
     * item renderer.
     *  
     * @param event The DropdownEvent.OPEN event for the combo box.
     */
    protected function _onOpen(event:DropdownEvent) : void {
        _isOpen = true;
    }


    /**
     * Detect close events on the drop down list to keep track of the current
     * drop down state so we can react properly to a click on our selected
     * item renderer.
     *  
     * @param event The DropdownEvent.CLOSE event for the combo box.
     */
    protected function _onClose(event:DropdownEvent) : void {
        _isOpen = false;
    }


    /**
     * When we detect a click on our renderer open or close the drop down list
     * based on whether the drop down is currently open/closed.
     *  
     * @param event The CLICK event from our selected item renderer.
     */
    protected function _onClick(event:MouseEvent) : void {
        if (_isOpen) {
            this.close(event);
        } else {
            this.open();
        }
    }


    /**
     * React to certain mouse/keyboard events on our selected item renderer and
     * pass the events to the ComboBox 'button' so that the skins are properly
     * applied.
     *  
     * @param event A mouse or keyboard event to send to the ComboBox button.
     * 
     */
    protected function _onMouseEvent(event:Event) : void {
        if (_buttonRef != null) {
            _buttonRef.dispatchEvent(event);
        }
    }
    } // end class
    } // end package

答案 3 :(得分:0)

谢谢maclema和Maurits de Boer。我在本课程中添加了更多内容以使其符合我的需求:

  • 我覆盖了set itemRenderer,这样如果你通过AS而不是mxml设置itemRenderer就可以了。我将文本输入替换代码移动到它自己的函数以避免重复。

  • 我添加了'increaseW'和'increaseH'的setter,以便在必要时调整组合框的大小,因为我的渲染器最初对于组合框来说太大了。

  • 我从textInputReplacement宽度减去25,所以它不会与下拉按钮重叠...可能更好地使用更适合的东西来容纳不同的皮肤等。

代码:

package
{
 import mx.binding.utils.BindingUtils;
 import mx.controls.ComboBox;
 import mx.core.IFactory;
 import mx.core.UIComponent;

    public class ComboBox2 extends ComboBox
    {
        public function ComboBox2()
        {
                super();
        }

        protected var textInputReplacement:UIComponent;
        private var _increaseW:Number = 0;
        private var _increaseH:Number = 0;

  public function set increaseW(val:Number):void
  {
   _increaseW = val;
  }

  public function set increaseH(val:Number):void
  {
   _increaseH = val;
  }

  override public function set itemRenderer(value:IFactory):void
  {
   super.itemRenderer = value;
   replaceTextInput();
  }

        override protected function createChildren():void 
        {
                super.createChildren();
    replaceTextInput();

        }

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

          unscaledWidth += _increaseW;
          unscaledHeight += _increaseH;

                super.updateDisplayList(unscaledWidth, unscaledHeight);

                if ( textInputReplacement ) {
                        textInputReplacement.width = unscaledWidth - 25;
                        textInputReplacement.height = unscaledHeight;
                }
        }

        protected function replaceTextInput():void
        {
         if ( !textInputReplacement ) {
                        if ( this.itemRenderer != null ) {
                                //remove the default textInput
                                removeChild(textInput);

                                //create a new itemRenderer to use in place of the text input
                                textInputReplacement = this.itemRenderer.newInstance();
                                addChild(textInputReplacement);

                                // ADD THIS BINDING:
                             // Bind the data of the textInputReplacement to the selected item
                             BindingUtils.bindProperty(textInputReplacement, "data", this, "selectedItem", true);

                             addChild(textInputReplacement);

                        }
                }
        }
    }
}

答案 4 :(得分:0)

我一直在寻找使用Spark ComboBox的方法。

这个帖子对我非常有用,但到目前为止只有使用mx:ComboBox的答案。我认为我应该使用Spark ComboBox附加我的答案。

  1. 创建ComboBox的新外观
  2. 隐藏和禁用textInput
  3. 插入您自己的组件
  4. 这就是皮肤的样子:

    <s:SparkSkin>
    
        <... Lots of other stuff/>
    
        <s:BorderContainer height="25">
            <WHATEVER YOU NEED HERE!/>
        </s:BorderContainer>
    
        <!-- Disable the textInput and hide it -->
        <s:TextInput id="textInput"
            left="0" right="18" top="0" bottom="0" 
            skinClass="spark.skins.spark.ComboBoxTextInputSkin"
    
            visible="false" enabled="false"/> 
    
    
    </s:SparkSkin>
    

    使用Spark ComboBox,这个过程非常简单,不需要你扩展ComboBox。

答案 5 :(得分:0)

我找到了一种更简单的方法来更改所选元素的渲染器。只有当您的元素继承自Flex 4.0或更高版本的TextInput类时,此方法才有效。

在Flex v4.5中,在第{11}行的ComboBase.createChildren中,您会发现可以使用样式键textInput传递textInputClass可定义的类:

// Mechanism to use MXFTETextInput. 
var textInputClass:Class = getStyle("textInputClass");            
if (!textInputClass || FlexVersion.compatibilityVersion < FlexVersion.VERSION_4_0)
{
    textInput = new TextInput();
}
else
{
   textInput = new textInputClass();
}

只需在组合的构造函数中更改此键的值,现在您拥有selectedItem的自己的渲染器。

public function ComboAvailableProfessor()
{
    super();

    itemRenderer = new ClassFactory( ProfessorAvailableListItemRenderer );
    setStyle( 'textInputClass', ProfessorAvailableSelectedListItemRenderer );
}

最后,您必须将data属性绑定到组合中的selectedItem属性才能显示数据。

override protected function createChildren():void
{
    super.createChildren();

    BindingUtils.bindProperty( textInput, 'data', this, 'selectedItem', true );
}