开发者

How do you combine renderers in flex?

开发者 https://www.devze.com 2023-01-28 14:04 出处:网络
I am trying to make a generic list control that has x buttons in it so that users can remove items from the list by clicking the x button.However, this list control needs to support item renderers whi

I am trying to make a generic list control that has x buttons in it so that users can remove items from the list by clicking the x button. However, this list control needs to support item renderers while still standardizing the layout of items placed next to x buttons.

So I figure the best approach would be to extend the list control and use a renderer that draws the x button and combines that with what ever renderer the user supplies. Unfortunately I cannot figure out how to do that.

I tried overriding newInstance method from the IFactory interface and used that to create开发者_开发技巧 an HBox that contains my button and inserts the newInstance result from a user supplied renderer, however it wasn't complete. It displayed as I expected, but the mouse over effect only worked on the user supplied renderer portion and not the entire HBox that my custom renderer made.

There seems to be a lot of missing details in the Flex documentation on this subject.

I am making all of this in Action Script 3.


I don't think the way to do this is by extending the List control. Instead I would try to create my custom itemRenderer and then extend it as needed. One approach to achive this can be:

  • Create you own SkinnableComponent which implements IItemRenderer or IDataRenderer.
  • Add the component a required part (the Delete button) and the behaviour you need (eg: deleteButton click handler)
  • Extend the component you have created to create concrete renderers.

EDIT: Added example

This is a simple example of how you can create an itemRenderer extending SkinnableComponent and implementing IDataRenderer. It supports 2 states, normal and hovered. Note that ItemRenderers are very expensive to create in Flex, so always try to keep them as lightweight as possible.

package 
{
    import flash.events.MouseEvent; 
    import mx.core.IDataRenderer;   
    import spark.components.supportClasses.ButtonBase;
    import spark.components.supportClasses.SkinnableComponent;

    [SkinState("normal")]
    [SkinState("hovered")]
    public class LearningRenderer extends SkinnableComponent implements IDataRenderer
    {

        /** Define the skin parts. */
        [SkinPart(required="true")]
        public var deleteButton:ButtonBase;

        public function LearningRenderer()
        {
            super();
            // Set the skin class.
            setStyle("skinClass", LearningRendererSkin);
            // Add event listeners to support HOVERED.
            addEventListener(MouseEvent.ROLL_OVER, rollOverHandler);
            addEventListener(MouseEvent.ROLL_OUT, rollOutHandler);
        }       

        /** Implement the partAdded() method. */
        override protected function partAdded(partName:String, instance:Object):void 
        {
            super.partAdded(partName, instance);
            if(instance == deleteButton){
                instance.addEventListener(MouseEvent.CLICK, button_clickHandler);
            }
        }

        /** Implement the partRemoved() method. */
        override protected function partRemoved(partName:String, instance:Object):void
        {
            if(instance == deleteButton){
                instance.removeEventListener(MouseEvent.CLICK, button_clickHandler);
            }
        }

        /** Implement the getCurrentSkinState() method. */
        override protected function getCurrentSkinState():String 
        {   
            var returnState:String = "normal";
            if (bRollOver)
                returnState = "hovered";
            return returnState;
        }

        public function get data():Object
        {
            return _data;
        }

        public function set data(value:Object):void
        {
            _data = value;
        }

        /** Add methods, properties, and metadata. */
        private var _data:Object;
        private var bRollOver:Boolean = false;

        /**
         * Handlers
         */         
        protected function button_clickHandler(eventObj:MouseEvent):void {
            //Handle click
        }

        protected function rollOverHandler(eventObj:MouseEvent):void{
            bRollOver = true;
            invalidateSkinState();
        }

        protected function rollOutHandler(eventObj:MouseEvent):void{
            bRollOver = false;
            invalidateSkinState();
        }       

    }
}

And this is the skin with the required deleteButton:

<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" 
        xmlns:s="library://ns.adobe.com/flex/spark" 
        xmlns:mx="library://ns.adobe.com/flex/mx">
    <!-- host component -->
    <fx:Metadata>
        [HostComponent("LearningRenderer")]
    </fx:Metadata>
    <!-- states -->
    <s:states>
        <s:State name="normal" />
        <s:State name="hovered" />
    </s:states>
    <s:Button id="deleteButton" label="Delete"/>        
</s:Skin>


I found the easiest solution was to just use a data grid and an item renderer for the last column. I extended the advanced data grid to make a "EditabledList" class, and I modified the "public function set columns" method to automatically add my remove button column to the end. Then I made a remove button item renderer for that column. Binding the click action was a little tricky since I only had a reference to the data for that row and not a reference to the list. I got around that by overriding the "public set dataProvider" method to replace each items in the list with my own custom object that has the reference to the list, a unique id, and a "removeMe" method. I called this object an EditableListItem. The unfortunate side effect is that data binds must be prefixed with an extra ".data" since the user's objects are stored in the "data" property of my EditableListItem's.

This is what my renderer looks like.

<fx:Component className="buttonRenderer">
    <s:MXAdvancedDataGridItemRenderer height="100%" width="100%">
        <s:Button left="5" top="5" skinClass="{buttonSkin}" click="EditableList.clickButton(data)"
            useHandCursor="true" buttonMode="true" toolTip="Remove item"/>
    </s:MXAdvancedDataGridItemRenderer>
</fx:Component>

The buttonSkin just rendered a custom image with roll over effects.


First of all - make sure you're using the Spark list controls, not the MX controls. The MX item renderers were notoriously hard to deal with. The spark renders, in comparison, are fairly well-behaved and easy to deal with.

Now... start here Flex Quick Start - Using Spark Item Renderers.

Finally - here's a much more in-depth discussion of how to Define a custom Spark Item Render.


I know you said "I'm making all of this in ActionScript 3" (I assume that means that you're avoiding mxml for some reason). ...but if you're using list controls and item renderers, then you're using the Flex framework, even if you're not coding in MXML.

So, the above resources are applicable, whether it's MXML or pure AS3. Just remember, that each MXML document corresponds to an AS3 class that gets generated by the mxml compiler. The root xml tag of the mxml document tells you what class the generated class will extend.

so when you see an mxml file named "MyRenderer.mxml", and its contents look like this:

<s:ItemRenderer>
   ...
</s:ItemRenderer>

you know that corresponds to a pure AS3 class definition like the following:

class MyRenderer extends ItemRenderer {
    ...
}

In my opinion -- when you get into doing ItemRenderers and using the Spark (or even MX) components, MXML is much easier to work with than pure AS3. After all, the framework components were designed to be used from mxml.

0

精彩评论

暂无评论...
验证码 换一张
取 消