当前位置: > > > Flex4 - 个性化DataGrid皮肤(列头样式修改,边框圆角,投影等)

Flex4 - 个性化DataGrid皮肤(列头样式修改,边框圆角,投影等)

在Flex4中,许多组件都可以自定义皮肤,下面是自定义修改DataGrid的skin样例,实现功能如下:
1,列头分割线隐藏。
2,列头默认headerRenderer修改,改成透明的
3,列头背景色渐变,添加高光效果
4,列头圆角,整个表格及边框圆角
5,表格添加投影
6,使用了主题颜色chromeColor

效果图如下:(第一个是原始DataGrid样式,其他使用了皮肤。同时后面两个使用了主题颜色chromeColor)
   

代码如下:
--- 表格皮肤 MyDataGridSkin.mxml ---
<?xml version="1.0" encoding="utf-8"?>
<s:SparkSkin 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx"
    xmlns:fb="http://ns.adobe.com/flashbuilder/2009"
    alpha.disabled="0.5" minWidth="89" minHeight="84" xmlns:local="*">
    
    <fx:Metadata>
    <![CDATA[
        [HostComponent("spark.components.DataGrid")]
    ]]>
    </fx:Metadata>
    
    <s:states>
        <s:State name="normal" />
        <s:State name="disabled" />
    </s:states>
    
    <fx:Declarations>
        <!--- @private -->        
        <fx:Component id="alternatingRowColorsBackground">
            <s:Rect implements="spark.components.gridClasses.IGridVisualElement">
                <fx:Script>
                    <![CDATA[
                        import spark.components.DataGrid;
                        import spark.components.Grid;
                        
                        /**
                         * @private
                         */
                        public function prepareGridVisualElement(grid:Grid, rowIndex:int, columnIndex:int):void
                        {
                            const dataGrid:DataGrid = grid.dataGrid;
                            if (!dataGrid)
                                return;
                            
                            const colors:Array = dataGrid.getStyle("alternatingRowColors");
                            if (colors && (colors.length > 0))
                            {
                                dataGrid.styleManager.getColorNames(colors); // lazily replace color names with ints
                                rowBackgroundFillColor.color = colors[rowIndex % colors.length];
                            }
                            else
                            {          
                                // This should be the same as bgFill.color.
                                rowBackgroundFillColor.color = 0xFFFFFF;
                            }
                        }
                    ]]>
                </fx:Script>  
                <s:fill>
                    <!--- @private -->   
                    <s:SolidColor id="rowBackgroundFillColor" color="0xFFFFFF"/>
                </s:fill>
            </s:Rect>
        </fx:Component>
        
        <!--- @private -->        
        <fx:Component id="caretIndicator">
            <s:Rect implements="spark.components.gridClasses.IGridVisualElement">
                <fx:Script>
                    <![CDATA[
                        import spark.components.DataGrid;
                        import spark.components.Grid;
                        
                        /**
                         * @private
                         */
                        public function prepareGridVisualElement(grid:Grid, rowIndex:int, columnIndex:int):void
                        {
                            const dataGrid:DataGrid = grid.dataGrid;
                            if (!dataGrid)
                                return;
                            
                            const color:uint = dataGrid.getStyle("caretColor");
                            caretIndicatorFill.color = color;
                        }
                    ]]>
                </fx:Script>
                
                <s:stroke>
                    <!--- @private -->
                    <s:SolidColorStroke id="caretIndicatorFill" color="0x0167FF" weight="1"/>
                </s:stroke>
            </s:Rect>
        </fx:Component>
        
        <!--- @private -->
        <fx:Component id="columnSeparator">
            <s:Line>
                <s:stroke>
                    <s:SolidColorStroke color="0xE6E6E6" weight="1" caps="square"/>
                </s:stroke>
            </s:Line>
        </fx:Component>
        
        <!--- 列头间隔 (修改:隐藏列头间隔) -->
        <fx:Component id="headerColumnSeparator">
            <s:Line>
                <s:stroke>
                    <s:SolidColorStroke color="0x696969" weight="1" caps="square" alpha="0"/>
                </s:stroke>
            </s:Line>
        </fx:Component>
        
        <!--- 修改:默认列头改成透明的 -->
        <fx:Component id="headerRenderer">
            <local:MyTransparentGridHeaderRenderer />
        </fx:Component>
        
        <!--- @private -->
        <fx:Component id="hoverIndicator">
            <s:Rect implements="spark.components.gridClasses.IGridVisualElement">
                <fx:Script>
                    <![CDATA[
                        import spark.components.DataGrid;
                        import spark.components.Grid;
                        
                        /**
                         * @private
                         */
                        public function prepareGridVisualElement(grid:Grid, rowIndex:int, columnIndex:int):void
                        {
                            const dataGrid:DataGrid = grid.dataGrid;
                            if (!dataGrid)
                                return;
                            
                            const color:uint = dataGrid.getStyle("rollOverColor");
                            hoverIndicatorFill.color = color;
                        }
                    ]]>
                </fx:Script>
                
                <s:fill>
                    <!--- @private -->
                    <s:SolidColor id="hoverIndicatorFill" color="0xCEDBEF"/>
                </s:fill>
            </s:Rect>
        </fx:Component>
        
        <!--- @private -->
        <fx:Component id="rowSeparator">
            <s:Line>
                <s:stroke>
                    <s:SolidColorStroke color="0xE6E6E6" weight="1" caps="square"/>
                </s:stroke>
            </s:Line>
        </fx:Component>
        
        <!--- @private -->
        <fx:Component id="selectionIndicator">
            <s:Rect implements="spark.components.gridClasses.IGridVisualElement">
                <fx:Script>
                    <![CDATA[
                        import spark.components.DataGrid;
                        import spark.components.Grid;
                        
                        /**
                         * @private
                         */
                        public function prepareGridVisualElement(grid:Grid, rowIndex:int, columnIndex:int):void
                        {
                            const dataGrid:DataGrid = grid.dataGrid;
                            if (!dataGrid)
                                return;
                            
                            const color:uint = dataGrid.getStyle("selectionColor");
                            selectionIndicatorFill.color = color;
                        }
                    ]]>
                </fx:Script>
                
                <s:fill>
                    <!--- @private -->
                    <s:SolidColor id="selectionIndicatorFill" color="0xA8C6EE"/>
                </s:fill>                
            </s:Rect>
        </fx:Component>
        
        <!--- @private -->
        <fx:Component id="editorIndicator">
            <s:Rect>
                <s:fill>
                    <s:SolidColor color="0xFFFFFF"/>
                </s:fill>                
            </s:Rect>
        </fx:Component>                    
        
    </fx:Declarations>
    
    <fx:Script fb:purpose="styling">
    <![CDATA[
        static private const exclusions:Array = ["scroller", "background", "columnHeaderGroup"];
        static private const contentFill:Array = ["bgFill"];
        
        /**
         * @private
         */
        override public function get colorizeExclusions():Array {return exclusions;}
        
        /**
         * @private
         */
        override public function get contentItems():Array {return contentFill};
        
        /**
         * @private
         */
        override protected function initializationComplete():void
        {
            useChromeColor = true;
            super.initializationComplete();
        }
        
        /**
         * @private
         */
        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
        {
            if (getStyle("borderVisible") == true)
            {
                border.visible = true;
                background.left = background.top = background.right = background.bottom = 1;
                scroller.minViewportInset = 1;
            }
            else
            {
                border.visible = false;
                background.left = background.top = background.right = background.bottom = 0;
                scroller.minViewportInset = 0;
            }
            
            borderStroke.color = getStyle("borderColor");
            borderStroke.alpha = getStyle("borderAlpha");
            
			//我们在组件设置主题颜色,那我们的列头背景颜色也会被修改
			titlebj.color=getStyle("chromeColor");
            super.updateDisplayList(unscaledWidth, unscaledHeight);
        }
    ]]>
    </fx:Script>
    
	<!-- 修改:添加投影  -->
	<s:RectangularDropShadow id="dropShadow" blurX="10" blurY="10" alpha="0.5" distance="3"
							 angle="90" color="#000000" left="0" top="0" right="0" bottom="0" blRadius="8" brRadius="8"
							 tlRadius="8" trRadius="8" />
	
    <!-- column header, content -->
    <s:VGroup horizontalAlign="justify" gap="0" left="0" right="0" top="0" bottom="0">
        
        <!--- @private -->
		<!-- 修改:添加列头圆角背景填充 -->
		<s:Group left="0" right="0">
			<!--绘制一个矩形左上坐右都为0边距这样容器多大,我们就显示多大-->
			<s:Rect left="0" right="0" top="0" bottom="0" topLeftRadiusX="8" topLeftRadiusY="8"
					topRightRadiusX="8" topRightRadiusY="8">
				<s:fill>
					<!--我们设置了一个纯色填充方便该动态改变标题背景-->
					<s:SolidColor color="0xe77a2b" id="titlebj"/>
				</s:fill>
			</s:Rect>
			<!--绘制一个矩形左上坐右都为0边距这样容器多大,我们就显示多大-->
			<s:Rect left="0" right="0" top="0" bottom="0" topLeftRadiusX="8" topLeftRadiusY="8"
					topRightRadiusX="8" topRightRadiusY="8">
				<s:fill>
					<!--我们这次渐变就不能使用纯色因为有背景我们只用白色设置下透明度就好-->
					<s:LinearGradient rotation="90">
						<s:GradientEntry color="0xffffff" alpha="0.8"/>
						<s:GradientEntry color="0xffffff" alpha="0.5"/>
					</s:LinearGradient>
				</s:fill>
			</s:Rect>
			<!--高光层那高度设置为60%,颜色都为白色,透明度设置下-->
			<s:Rect left="0" right="0" top="0" height="60%" topLeftRadiusX="8" topLeftRadiusY="8"
					topRightRadiusX="8" topRightRadiusY="8">
				<s:fill>
					<!--我们设置了一个线性填充,角度为90度填充-->
					<s:LinearGradient rotation="90">
						<s:GradientEntry color="0xffffff" alpha="0.3"/>
						<s:GradientEntry color="0xffffff" alpha="0.2"/>
					</s:LinearGradient>
				</s:fill>
			</s:Rect>
	        <s:GridColumnHeaderGroup id="columnHeaderGroup"
				left="0" right="0" top="0" bottom="0"
	            paddingLeft="1" paddingTop="1" paddingRight="1" minHeight="30" 
	            columnSeparator="{headerColumnSeparator}"
	            headerRenderer="{headerRenderer}"/>
		</s:Group>

        <s:Group height="100%">
            
            <!--- 修改:内容区域左下,右下改圆角 -->
            <s:Rect id="background" left="1" right="1" top="1" bottom="1"
					bottomLeftRadiusX="8" bottomLeftRadiusY="8" bottomRightRadiusX="8" bottomRightRadiusY="8">
                <s:fill>
                    <!--- Defines the color of the background. The default color is 0xFFFFFF. -->
                    <s:SolidColor id="bgFill" color="0xFFFFFF" />
                </s:fill>
            </s:Rect>
            
            <!-- header separator, scroller and grid -->
            <s:VGroup horizontalAlign="justify" height="100%" width="100%" gap="-1">
                <!--- @private -->
                <s:Line id="headerSeparator">
                    <s:stroke>
                        <s:SolidColorStroke color="0x696969" weight="1" caps="square"/>
                    </s:stroke>
                </s:Line>          
                
                <!--- @private -->
                <s:Scroller id="scroller" minViewportInset="1" hasFocusableChildren="false" height="100%">
                    <!--- @private -->
                    <s:Grid id="grid" itemRenderer="spark.skins.spark.DefaultGridItemRenderer">
                        <s:GridLayer name="backgroundLayer"/>
                        <s:GridLayer name="selectionLayer"/>
                        <s:GridLayer name="editorIndicatorLayer"/>                            
                        <s:GridLayer name="rendererLayer"/>
                        <s:GridLayer name="overlayLayer"/>
                    </s:Grid>                    
                </s:Scroller>
            </s:VGroup>
            
        </s:Group>
        
    </s:VGroup>
    
    <!-- 修改:边框添加圆角样式 -->
    <!--- @private -->
    <s:Rect left="0" right="0" top="0" bottom="0" id="border" radiusX="8" radiusY="8">
        <s:stroke>
            <!--- @private -->
            <s:SolidColorStroke id="borderStroke" weight="2"/>
        </s:stroke>
    </s:Rect>    

</s:SparkSkin>

--- 列头渲染器 MyTransparentGridHeaderRenderer.mxml ---
<?xml version="1.0" encoding="utf-8"?>
<!--
透明列头
-->
<s:GridItemRenderer minWidth="21" minHeight="21"
					xmlns:fx="http://ns.adobe.com/mxml/2009" 
					xmlns:s="library://ns.adobe.com/flex/spark" 
					xmlns:mx="library://ns.adobe.com/flex/mx">
	
	<fx:Declarations>
		<fx:Component id="defaultSortIndicator">
			<s:Path data="M 3.5 7.0 L 0.0 0.0 L 7.0 0.0 L 3.5 7.0" implements="spark.components.gridClasses.IGridVisualElement">
				<fx:Script>
					<![CDATA[
						import spark.components.DataGrid;
						import spark.components.Grid;
						
						/**
						 *  @private
						 */
						public function prepareGridVisualElement(grid:Grid, rowIndex:int, columnIndex:int):void
						{
							const dataGrid:DataGrid = grid.dataGrid;
							if (!dataGrid)
								return;
							
							const color:uint = dataGrid.getStyle("symbolColor");
							arrowFill1.color = color;
							arrowFill2.color = color;
						}
					]]>
				</fx:Script>
				
				<s:fill>
					<s:RadialGradient rotation="90" focalPointRatio="1">    
						<!--- @private -->
						<s:GradientEntry id="arrowFill1" color="0" alpha="0.6" />
						<!--- @private -->
						<s:GradientEntry id="arrowFill2" color="0" alpha="0.8" />
					</s:RadialGradient>
				</s:fill>
			</s:Path>
		</fx:Component>
		
		<s:Label id="labelDisplay" 
				 verticalCenter="1" left="0" right="0" top="0" bottom="0"
				 textAlign="start"
				 fontWeight="bold"
				 verticalAlign="middle"
				 maxDisplayedLines="1"
				 showTruncationTip="true" />
	</fx:Declarations>
	
	<fx:Script>
		<![CDATA[
			import spark.components.gridClasses.IGridVisualElement;
			import mx.core.IVisualElement;
			
			import spark.components.DataGrid;
			import spark.components.GridColumnHeaderGroup;
			import spark.components.gridClasses.GridColumn;
			import spark.primitives.supportClasses.GraphicElement;
			
			// chrome color constants and variables
			private static const DEFAULT_COLOR_VALUE:uint = 0xCC;
			private static const DEFAULT_COLOR:uint = 0xCCCCCC;
			private static const DEFAULT_SYMBOL_COLOR:uint = 0x000000;
			
			private static var colorTransform:ColorTransform = new ColorTransform();
			
			/**
			 *  @private
			 */
			private function dispatchChangeEvent(type:String):void
			{
				if (hasEventListener(type))
					dispatchEvent(new Event(type));
			}            
			
			//----------------------------------
			//  maxDisplayedLines
			//----------------------------------
			
			private var _maxDisplayedLines:int = 1;
			
			[Bindable("maxDisplayedLinesChanged")]
			[Inspectable(minValue="-1")]
			
			/**
			 *  The value of this property is used to initialize the 
			 *  <code>maxDisplayedLines</code> property of this renderer's 
			 *  <code>labelDisplay</code> element.
			 * 
			 *  @copy spark.components.supportClasses.TextBase#maxDisplayedLines
			 * 
			 *  @default 1
			 * 
			 *  @langversion 3.0
			 *  @playerversion Flash 10
			 *  @playerversion AIR 1.5
			 *  @productversion Flex 4.5
			 */
			public function get maxDisplayedLines():int
			{
				return _maxDisplayedLines;
			}
			
			/**
			 *  @private
			 */
			public function set maxDisplayedLines(value:int):void
			{
				if (value == _maxDisplayedLines)
					return;
				
				_maxDisplayedLines = value;
				if (labelDisplay)
					labelDisplay.maxDisplayedLines = value;
				
				invalidateSize();
				invalidateDisplayList();
				
				dispatchChangeEvent("maxDisplayedLinesChanged");
			}
			
			//----------------------------------
			//  sortIndicator
			//----------------------------------
			
			private var _sortIndicator:IFactory;
			private var sortIndicatorInstance:IVisualElement;
			
			[Bindable("sortIndicatorChanged")]
			
			/**
			 *  A visual element that's displayed when the column is sorted.
			 * 
			 *  <p>The sortIndicator visual element is added to the <code>sortIndicatorGroup</code>
			 *  by this renderer's <code>prepare()</code> method.  Any size/location constraints 
			 *  specified by the sortIndicator define its location relative to the sortIndicatorGroup.</p>
			 * 
			 *  @default null
			 * 
			 *  @langversion 3.0
			 *  @playerversion Flash 10
			 *  @playerversion AIR 1.5
			 *  @productversion Flex 4.5
			 */
			public function get sortIndicator():IFactory
			{
				return (_sortIndicator) ? _sortIndicator : defaultSortIndicator;
			}
			
			/**
			 *  @private
			 */
			public function set sortIndicator(value:IFactory):void
			{
				if (_sortIndicator == value)
					return;
				
				_sortIndicator = value;
				if (sortIndicatorInstance)
				{
					sortIndicatorGroup.includeInLayout = false;
					sortIndicatorGroup.removeElement(sortIndicatorInstance);
					sortIndicatorInstance = null;
				}
				
				invalidateDisplayList();
				dispatchChangeEvent("sortIndicatorChanged");
			}
			
			/**
			 *  @private
			 *  Create and add the sortIndicator to the sortIndicatorGroup and the 
			 *  labelDisplay into the labelDisplayGroup.
			 */
			override public function prepare(hasBeenRecycled:Boolean):void
			{
				super.prepare(hasBeenRecycled);
				
				if (labelDisplay && labelDisplayGroup && (labelDisplay.parent != labelDisplayGroup))
				{
					labelDisplayGroup.removeAllElements();
					labelDisplayGroup.addElement(labelDisplay);
				}
				
				const column:GridColumn = this.column;
				if (sortIndicator && column && column.grid && column.grid.dataGrid && column.grid.dataGrid.columnHeaderGroup)
				{
					const dataGrid:DataGrid = column.grid.dataGrid;
					const columnHeaderGroup:GridColumnHeaderGroup = dataGrid.columnHeaderGroup;
					
					if (columnHeaderGroup.isSortIndicatorVisible(column.columnIndex))
					{
						if (!sortIndicatorInstance)
						{
							sortIndicatorInstance = sortIndicator.newInstance();
							sortIndicatorGroup.addElement(sortIndicatorInstance);
							chromeColorChanged = true;
							invalidateDisplayList();
						}
						
						// Initialize sortIndicator
						sortIndicatorInstance.visible = true;
						const gridVisualElement:IGridVisualElement = sortIndicatorInstance as IGridVisualElement;
						if (gridVisualElement)
							gridVisualElement.prepareGridVisualElement(column.grid, -1, column.columnIndex);
						
						sortIndicatorGroup.includeInLayout = true;
						sortIndicatorGroup.scaleY = (column.sortDescending) ? 1 : -1;
					}
					else
					{
						if (sortIndicatorInstance)
						{
							sortIndicatorGroup.removeElement(sortIndicatorInstance);
							sortIndicatorGroup.includeInLayout = false;
							sortIndicatorInstance = null;
						}
					}
				}
			}
			
			private var chromeColorChanged:Boolean = false;
			private var colorized:Boolean = false;
			
			/**
			 *  @private
			 *  Apply chromeColor style.
			 */
			override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
			{
				// Apply chrome color
				if (chromeColorChanged)
				{
					var chromeColor:uint = getStyle("chromeColor");
					
					if (chromeColor != DEFAULT_COLOR || colorized)
					{          
						colorTransform.redOffset = ((chromeColor & (0xFF << 16)) >> 16) - DEFAULT_COLOR_VALUE;
						colorTransform.greenOffset = ((chromeColor & (0xFF << 8)) >> 8) - DEFAULT_COLOR_VALUE;
						colorTransform.blueOffset = (chromeColor & 0xFF) - DEFAULT_COLOR_VALUE;
						colorTransform.alphaMultiplier = alpha;
						
						transform.colorTransform = colorTransform;
						
						var exclusions:Array = [labelDisplay, sortIndicatorInstance];
						
						// Apply inverse colorizing to exclusions
						if (exclusions && exclusions.length > 0)
						{
							colorTransform.redOffset = -colorTransform.redOffset;
							colorTransform.greenOffset = -colorTransform.greenOffset;
							colorTransform.blueOffset = -colorTransform.blueOffset;
							
							for (var i:int = 0; i < exclusions.length; i++)
							{
								var exclusionObject:Object = exclusions[i];
								
								if (exclusionObject &&
									(exclusionObject is DisplayObject ||
										exclusionObject is GraphicElement))
								{
									colorTransform.alphaMultiplier = exclusionObject.alpha;
									exclusionObject.transform.colorTransform = colorTransform;
								}
							}
						}
						
						colorized = true;
					}
					
					chromeColorChanged = false;
				}
				
				super.updateDisplayList(unscaledWidth, unscaledHeight);
			}
			
			/**
			 *  @private
			 */
			override public function styleChanged(styleProp:String):void
			{
				var allStyles:Boolean = !styleProp || styleProp == "styleName";
				
				super.styleChanged(styleProp);
				
				if (allStyles || styleProp == "chromeColor")
				{
					chromeColorChanged = true;
					invalidateDisplayList();
				}
			}
		]]>
	</fx:Script>
	
	<s:states>
		<s:State name="normal" />
		<s:State name="hovered" />
		<s:State name="down" />
	</s:states>      
	
	<s:HGroup left="7" right="7" top="5" bottom="5" gap="2" verticalAlign="middle">
		
		<!-- layer 7: Container for labelDisplay:TextBase  -->
		<s:Group id="labelDisplayGroup" width="100%" />
		
		<!-- layer 8: Container for sortIndicator:IVisualElement  -->
		<s:Group id="sortIndicatorGroup" includeInLayout="false" />
		
	</s:HGroup>
	
</s:GridItemRenderer>

--- 测试类 DataGridTest.mxml ---
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
			   xmlns:s="library://ns.adobe.com/flex/spark" 
			   xmlns:mx="library://ns.adobe.com/flex/mx">
	<fx:Declarations>
		<s:ArrayCollection id="dgData">
			<fx:Object name="张三" sex="男"/>
			<fx:Object name="小红" sex="女"/>
			<fx:Object name="小丽" sex="女"/>
			<fx:Object name="小美" sex="女"/>
		</s:ArrayCollection>
	</fx:Declarations>
	
	<s:TileGroup horizontalCenter="0" verticalCenter="0" width="700"
				 horizontalGap="20" verticalGap="20">
		<s:DataGrid width="250" dataProvider="{dgData}">
			<s:columns>
				<s:ArrayList>
					<s:GridColumn headerText="姓名" dataField="name"/>
					<s:GridColumn headerText="性别" dataField="sex"/>
				</s:ArrayList>
			</s:columns>
		</s:DataGrid>
		<s:DataGrid width="250" dataProvider="{dgData}"
					skinClass="MyDataGridSkin">
			<s:columns>
				<s:ArrayList>
					<s:GridColumn headerText="姓名" dataField="name"/>
					<s:GridColumn headerText="性别" dataField="sex"/>
				</s:ArrayList>
			</s:columns>
		</s:DataGrid>
		<s:DataGrid width="250" dataProvider="{dgData}"
					skinClass="MyDataGridSkin" chromeColor="0xe77a2b">
			<s:columns>
				<s:ArrayList>
					<s:GridColumn headerText="姓名" dataField="name"/>
					<s:GridColumn headerText="性别" dataField="sex"/>
				</s:ArrayList>
			</s:columns>
		</s:DataGrid>
		<s:DataGrid width="250" dataProvider="{dgData}"
					skinClass="MyDataGridSkin" chromeColor="0x00BDF2">
			<s:columns>
				<s:ArrayList>
					<s:GridColumn headerText="姓名" dataField="name"/>
					<s:GridColumn headerText="性别" dataField="sex"/>
				</s:ArrayList>
			</s:columns>
		</s:DataGrid>
	</s:TileGroup>

</s:Application>

源代码下载:
DataGridTest.zip
评论0