Skip to main content

Creating juxtaposition tables, Part 1: Use Flex to create JTables

Sophia Krasikov (kras@us.ibm.com), Advisory Software Engineer, IBM
Sophia Krasikov, an Research Software Engineer, holds a master's degree in applied math and computer science. Her areas of interest are Web development and visualization software.
Michael Desmond (mdesmond@us.ibm.com), PhD student, University of Limerick, Ireland
Michael Desmond is a PhD student at the University of Limerick Ireland where he studies programmer disorientation and interface design.

Summary:  Use an Adobe® Flex-based juxtaposition table, a two-dimensional visualization assistant, to arrange, classify, and compare potentially large quantities of data. With the juxtaposition table, you can define your own custom perspectives in a convenient and compact view. This article demonstrates how to dynamically create the table's columns and alter the table's contents. You will also learn how to display numerous items in a single cell and how to give cells their shape and color.

View more content in this series

Date:  15 Sep 2009
Level:  Intermediate PDF:  A4 and Letter (183KB | 19 pages)Get Adobe® Reader®
Activity:  3459 views
Comments:  

Introduction

To ease the workload of non-IT professionals, including business consultants, we are building a prototype modeling tool, with Microsoft® PowerPoint–like presentation features, that helps consultants organize large amounts of unstructured data and prepare presentation documents for customers. The first version of our tool was built on Eclipse. However, to take advantage of better graphics, we decided to switch to Rich Internet Application technologies. We considered the pros of cons of two popular platforms—Ajax or Adobe Flex—and decided to go with Flex.

Our Flex-based application allows users to construct slides using a variety of visualization assistants, including bulleted lists, graphic editors, tag clouds, relation explorers, and tables. In this article, we'll explain one particular assistant that allows users to arrange, classify, and compare potentially large quantities of data. We call such assistant a "Juxtaposition Table" or JTable. With JTable, users can visualize data in two dimensions and interactively change the horizontal and vertical data sets at runtime. This supports the examination of a database or information space from a variety of user-defined perspectives in a single view.

Before looking at the code, let us introduce our basic scenario, in which we, as an example, are a survey center, specializing in public tastes of favorite books, movies, songs, and sites. A polling group can have any number of participants as well as an expandable list of polling categories. For simplicity’s sake, we kept our polling group small. The participants were: John, Jennifer, and Ivan. After completing the survey, we transferred the results into a simple database, and now we want to make the results available on our Web site for general perusal. We could provide one large html table which contains all the data from our survey, but this may be rather cumbersome for users due to the table's size and the information density. Alternatively, we could provide a number of tables which present the data from a variety of predefined perspectives. However, this still is not ideal as users are restricted to our predefined table plots, and scrolling between the tables may become bothersome.

An elegant solution to our problem is a JTable. If we present our data in the JTable, users can choose a custom perspective to suit their informational needs by changing the horizontal and vertical data sets. For instance, a user might wish to see John’s favorite books; this may be achieved by selecting "John" as the horizontal dimension in the table and "Favorite Books" as the vertical dimension. The user might then become interested in examining the survey participants' movie preferences. Again this is achieved by selecting "All People" in the horizontal dimension and "Favorite Movies" in the vertical dimension. Using JTable, the user is free to define his or her own custom perspectives in a convenient and compact view. Run the JTable application by downloading DataGridDeveloperWorksExample1 in the Download table below. After that, you can view the survey data in different combinations.

We use Flex’s AdvancedDataGrid component to implement the JTable. In this two-part series, we will demonstrate how we customized and extended the AdvancedDataGrid component provided in the Flex 3 data visualization package. You can read more about it in the Resources section of this article. In this article we will also demonstrate how we used horizontal and vertical control bars to alter the JTable content at runtime and how we made the first column and the first row of the table into row and column headers. In a subsequent article we will demonstrate the fluid expansion and contraction of items in cells and the capability of moving items from cell to cell by using drag-and-drop.

These articles assume readers are familiar with the Flex 3 programming environment and the ActionScript language.

Comparing a mockup table with the JTable from the running example

Before going to the implementation solutions for the JTable application, let us first examine a mockup of one of a possible perspectives of the data we collected from the survey and then give you an overview of our JTable.

What we wanted to achieve was a table with a horizontal header presenting a single name of a person, or names of all people, and a vertical header presenting a single category, or all categories of favorite items. Table 1 is a mockup of how we would like our table to look after a user selects "All People" for the horizontal dimension and "All questions" for the vertical dimension.


Table 1. 2D mockup table displaying favorite items associated with a polling group
JohnJenniferIvan
Favorite bookBook - 1Book - 2Book - 3
Favorite movieMovie - 1Movie - 2Movie - 3
Favorite songSong - 1Song - 2Song - 3
Favorite siteSite - 1Site - 2Site - 3

Figure 1 shows the concept realized with JTable.


Figure 1. 2D JTable displaying favorites associated with the polling group
Table rendered with JTable with clickable label elements in rows and collumns and drop down lists for selecting people and questions.

What you see in Figure 1 is a 2-dimensional lookup of our data. You can see that the header labels that are usually present in tables do not show up in this one (though the header is slightly visible at the very top of the table, as a narrow band with vertical table lines that allow the user to change the width of the columns). You should also observe that the table has identical looking and equally serviceable horizontal and vertical headers that are distinguished from the table's body. Further in the article we will use two terms: a header cell and a content cell. We define a header cell as a cell for the top and side headers that we call horizontal and vertical headers; we define a content cell as a cell that is not in a header but within the body of the table. If you take another look at Figure 1, you'll see that each header cell contains only one item encapsulated in an oval, while each content cell contains a few items, with each of the items displayed against a darker background. One more look at the figure will show two menu buttons, one above the horizontal header and a second to the left of the vertical header. The pull-down menus of the two buttons allow a user to dynamically configure the content of the headers and, consequently, the table.

Before we go on to our implementation solutions for the JTable application, we'd like to show you how we generated the data to run the JTable application.

Generating data

In a real scenario, survey data would most likely be retrieved from a database on a server. However, for the purpose of this example we used a simple DataBase class and populated it with answers given by members of the survey group. The questions themselves are stored as categories. We made our database a singleton in order to limit the DataBase class to only one instance. The following line of code gives access to that instance:

var fRowSet = DataBase.instance.getQuestions();
           

We store the answers of all participants in a repository, associating each participant’s answers with his or her name. The names of the respondents serve as keys to query the repository. We store the answers to each question in a separate repository with keys that coincide with questions, such as "Favorite Book," "Favorite Movie," "Favorite Song," and "Favorite Site." After finishing populating a single participant’s repository with answers, we add his or her answers to the all-answer repository (fMap).


Listing 1. Setting up John's answers
var fMap:Hashmap = new Hashmap();

var johnsAnswers:Hashmap = new Hashmap();
var johnsBooks:Array = new Array("The Human Stain", "One More Year","The Painted Bird");
johnsAnswers.put("Favorite Books", johnsBooks);
… 
fMap.put("John", johnsAnswers);
           

We put Jennifer’s and Ivan’s answers in the all-answer repository in a similar manner. Then, we use three methods in the DataBase class to retrieve the data:


Listing 2. Retrieving the data
public function getPeople():Array
public function getQuestions():Array
public function getAnswers(person:String = null, question:String = null):Array
           

The getPeople method returns an array of all the survey participants as strings. The getQuestions method returns an array of all the survey categories as strings. Finally, the getAnswers method takes two parameters as an input ( person’s name and a polling category) and returns an array of answers as strings. Array structures returned from these methods are used to build our data model. The getAnswers operation can be invoked with a null person or question parameter.

The rest of the article describes our design and implementation solutions for the JTable, including the modifications we needed to make to the AdvancedDataGrid component, as well as other support we created so that our JTable could work in the way we show in the running example.

Modeling data

The JTable data provider is built on a foundation of rows and cells. Because a table is made up of rows, each of which contains a number of cells, we achieve this mapping between the data provider and the table by first defining the Row class and the Cell class.

As the names of these classes suggest, the Row class holds objects for a single row, and a Cell class, shown in the code below (Listing 3), is for a content of a cell in a row. Each cell contains a generic data variable. What we will store in this data variable is an array of two elements: a horizontal header name and a vertical header name of a cell.


Listing 3. Cell class
				
public class Cell
{
	private var fData:*;	

	public function Cell()
	{
	}

	public function get data():*
	{
		return fData;
	}
	
	public function set data(value:*):void
	{
		fData = value;
	}
}
           

We created a wrapper class, called HeaderCell to distinguish a header cell form a content cell. We have created another wrapper, NullCell class, which is used for the upper left corner cell that's neither a "header" nor a "content" cell.

A row content is defined by its Cell objects. The Row class, as you see below in Listing 4, is a holder of Cell objects. The Row class is dynamic. Dynamic objects in Flex act as hashmaps. The Row class is altered at runtime when cell objects belonging to a row are added to it. Cell objects are being inserted into the Row object via a column index. Thus, the Cell objects are being hashed with the column index in the Row object.


Listing 4. Row class

public dynamic class Row
{
	private var fColumIndex:int = 0;
	
	public function Row()
	{
	}
	
	public function putCell(cell:Cell):void
	{
		this[fColumIndex++] = cell;	
	}
	
	public function getCellAt(index:int):Cell
	{
		return this[index] as Cell;	
	}
}

Having looked at Cell and Row classes, we are now ready to move on to other implementation solutions for the JTable application. First, we’d like to show how we were able to protect the JTable’s UI code from being altered by any data changes. We accomplished this by completely separating the data from the user interface.

We defined the table's data model in the DefaultDataModel class. The createDataPovider function in this class provides content for the table. The function takes two parameters: "array of rows" and "array of columns". These parameters are passed to the DefaultDataModel class constructor, which is shown in the fragment of code below in Listing 5.


Listing 5. Passing parameters to the DefaultDataModel class
private var fRowSet:Array;
private var fColumnSet:Array;

public function DefaultDataModel(rowSet:Array, columnSet:Array)
{
	fRowSet = rowSet;
	fColumnSet = columnSet;
}
           

Each time the user wants to change the appearance of the table by selecting a menu option, through either of the horizontal or vertical pop-up menu buttons, the data model behind the table is altered as it is shown in the fragment of code in Listing 6.


Listing 6. Changing the data model
public function get dataProvider():ArrayCollection
{
	if(!fDataProvider)
		fDataProvider = createDataProvider();
	return fDataProvider;	
}
            

As you can see from the code fragment in Listing 7, below, the createDataProvider function forks the code into three other functions: createRowAndColumnDataProvider, createColumnOnlyDataProvider and createRowOnlyDataProvider. createRowAndColumnDataProvider creates a data provider that provides content for a 2D table. createColumnOnlyDataProvider and createRowAndColumnDataProvider create providers for a 1D table.

A 1D table is displayed when a user selects the "Clear" option, either in the horizontal or vertical menu buttons (Figures 2, 3).


Listing 7. createDataProvider
protected function createDataProvider():ArrayCollection
{
	if (fRowSet && fColumnSet)
		return createRowAndColumnDataProvider();
	
	if (fColumnSet && !fRowSet)
		return createColumnOnlyDataProvider();
	
	if (fRowSet && !fColumnSet)
		return createRowOnlyDataProvider();

	return new ArrayCollection();
}
           

In the next fragment (Listing 8) we will show you one of the three "Create" data provider functions, createRowAndColumnDataProvider. This function provides the content for a 2D table. First, we instantiate a header row and add a corner cell to it. Then we loop over columns (first loop) and add the rest of the header cells to this row and add the row to the provider after the looping is done. The second loop is done over the rows. For each element in the fRowSet array, a new row is instantiated. The first cell added to a row is a horizontal header's cell; other cells added to a row are content cells.


Listing 8. DefaultDataModel class: createRowAndColumnDataProvider function
			 			
protected function createRowAndColumnDataProvider():ArrayCollection
{
	var provider:ArrayCollection = new ArrayCollection();

	var headerRow:Row = new Row();
	// Put in the top corner cell	
	headerRow.putCell(new NullCell());
		
	// Populate the row header
	for each(var item:String in fColumnSet)
	{	
		// Create the header cells	
		var cell:Cell = new HeaderCell;
		cell.data = item;
		headerRow.putCell(cell);
	}
	provider.addItem(headerRow);
	
	// Populate the contents and column header
	for each(var rowItem:String in fRowSet)
	{	
		// Content row
		var row:Row = new Row();
		
		// Column header cell
		var headerCell:Cell = new HeaderCell();
		headerCell.data = rowItem;
		row.putCell(headerCell);

		for each(var columnItem:String in fColumnSet)
		{
			// Create the content cells	
			var cell:Cell = new Cell();
			cell.data = new Array(rowItem,columnItem);
			row.putCell(cell);	
		}
	
		provider.addItem(row);
	}
	return provider;
}
           

The array collection returned from the createRowAndColumnDataProvider function will be assigned to the table through its dataProvider property.

We built another data model to provide content for horizontal and vertical menu buttons that control the content of the headers and the table. We defined this model in the PopUpButtonsDataModel class. This class has only two functions: getRowMenu and getColumnMenu. You can see one of these functions in the code fragment below (Listing 9). In this fragment we create an array collection of menu items. First we add "Clear" and "All Questions" menu items to the menu array collection, and then we add the survey categories in a simple loop. The data we store in each menu item object is an array containing only one element: the name of the category. The array collections of MenuItem objects returned from getRowMenu and getColumnMenu will be assigned to the menu buttons through their dataProvider property.


Listing 9. PopUpButtonsDataModel class: getRowMenu function
			 			
public function getRowMenu(): ArrayCollection
{
	var menu:ArrayCollection = new ArrayCollection();
	var questions:Array = DataBase.instance.getQuestions();

	var menuItem:MenuItem = new MenuItem("Clear");
	menu.addItem(menuItem);
	
	var menuItem:MenuItem = new MenuItem("All questions");
	menuItem.data = questions;
	menu.addItem(menuItem);

	for each(var question:String in questions)
	{
		var menuItem:MenuItem = new MenuItem(question);
		menuItem.data = new Array(question);
		menu.addItem(menuItem);
	}
	return menu;						
}
           

Building the UI

The UI visible in Figures 1, 2, and 3 is a canvas container that holds three user interface components: the table and two menu buttons. Our canvas class, DynamicAdvancedDataGridCanvas, extends Flex's Canvas class. We add the table and two menu buttons to the canvas container in the createChildren function (Listing 11), which overrides the same function in the base class. We used the menu buttons to allow a user to examine the survey by selecting options from the buttons' pull-down menus. (The buttons here are also known as the horizontal and vertical control bars.) We created the vertical control bar by rotating the row button 90 degrees counterclockwise (-90), as shown in Listing 11 below. However, this meant that an embedded font was required, as a standard font does not show up in the vertical button. Listing 10 shows the code we used to include an embedded font declaration in the application DataGridDeveloperExample1.mxml file.


Listing 10. Code to embed font in DataGridDeveloperExample1.mxml
[Embed(systemFont='Verdana', fontWeight="bold", fontName='embeddedFont', 
mimeType='application/x-font', advancedAntiAliasing="true")]


Listing 11. DynamicAdvancedDataGridCanvas class: createChildren function
			 			
override protected function createChildren():void
{
	super.createChildren();
	
	fColPopupMenuButton  = new SuperPopUpMenuButton();
	...
	PopupMenuButton.addEventListener(MenuEvent.ITEM_CLICK,  columnClick);
	fColPopupMenuButton.addEventListener(DropdownEvent.OPEN, colPopupOpen);
	fColPopupMenuButton.dataProvider  = 
	fPopUpButtonsDataModel.getColumnMenu().toArray();
		
	addChild(fColPopupMenuButton);
	
	fRowPopupMenuButton  = new SuperPopUpMenuButton();
        ...
 	fRowPopupMenuButton.rotation = -90;
	...
	fRowPopupMenuButton.addEventListener(MenuEvent.ITEM_CLICK,  rowClick);
	fRowPopupMenuButton.addEventListener(DropdownEvent.OPEN, rowPopupOpen);
	...
	fRowPopupMenuButton.dataProvider  = fPopUpButtonsDataModel.getRowMenu().toArray();
	addChild(fRowPopupMenuButton);
	   
	fAdvancedDataGrid = new DynamicAdvancedDataGrid();
	addChild(fAdvancedDataGrid);
	...
}
           

The menu buttons, which we added to the canvas, are event-driven, meaning our canvas class handles events that are generated when the user opens and selects an option within the drop-down menu (DropdownEvent.OPEN and MenuEvent.ITEM_CLICK events).


Figure 2. 1D table displaying all favorites of the polling group, with vertical menu open
Table is rendered with JTable, showing all names and a list of their choices beneath their names.  However, the box for questions shows 'Clear' and there are no question labels displayed.  A list is shown coming off of the questions dropbox with options for Clear, 'All questions,' and a listing of each individual question for selection.

Figure 3. 1D table displaying favorites of the polling group with the horizontal menu open.
The JTable rendering is show with the people set to 'Clear' and no                name columns. The people dropbox has a list showing Clear, 'All people,' and a list of individual names for selection.  Answers are accumulated for each question, and a scroll bar allows the user to scroll down through the questions.

In the next fragment (Listing 12) we show how we handle the DropdownEvent. The data provider is assigned through the menu button's dataProvider property. It is worth noticing how the y coordinate of the popUp window of the vertical menu button is converted from local to global coordinates in the last line of this code fragment. We do not need to do this conversion for the horizontal menu button.


Listing 12. DropdownEvent handling
public function rowPopupOpen(event:DropdownEvent):void
{
	if(fRowMenuInvalid)
	{
		fRowPopupMenuButton.dataProvider = 
		fPopUpButtonsDataModel.getRowMenu().toArray();
					
	}
					
	fRowPopupMenuButton.popUp.y = 
	localToGlobal(new Point(0,fRowPopupMenuButton.y)).y 
	- fRowPopupMenuButton.width;
}		
            

Below, in Listing 13, we show how we handle the second event, the MenuEvent. The first line changes the button's label to show the selected label in the pull-down menu. The second line retrieves the menu data object from the event. The last line in this function refreshes the table.


Listing 13. MenuEvent handling
private function rowClick(event:MenuEvent):void 
{
	fRowPopupMenuButton.label = event.label;
	fRowSet = (event.item as MenuItem).data;
	refresh();
}
           

Listing 14 shows the refresh function.


Listing 14. Refresh function
public function refresh():void
{
	var model:DefaultDataModel = new DefaultDataModel(fRowSet,fColumnSet);
	fAdvancedDataGrid.dataProvider = model.dataProvider;
}
           

Now let's explore the creation of the table itself in greater detail. We define the table with the DynamicAdvancedDataGrid class, which extends Flex's AdvancedDataGrid class.

The Flex AdvancedDataGrid is a UI component, contained in the Flex data visualization package, which is capable of displaying multiple columns of information. Each column in the AdvancedDataGrid component is represented by an AdvancedDataGridColumn object. The headerText and dataField properties are the two main properties that define an individual column. The headerText is the name that appears as the title of a column, while the dataField indicates that the data from the data provider is to be displayed in the field. The data, which is a collection of objects, is assigned to the AdvancedDataGrid component through the dataProvider property.

The simplest case for creating an application with the AdvancedDataGrid component is shown in the fragment of code in Listing 15, below. In the fragment, we bind the variable dataModel to the dataProvider property.


Listing 15. Creating a simple application with AdvancedDataGrid
	private var dataModel:ArrayCollection = 
	new ArrayCollection([ 
	{Person :"John",  Favorite Book: "The Human Stain"}, 
	{Person :"Jennifer", Favorite Book: "One More Year"}, 
	{Person :"Ivan", Favorite Book: "The Painted Bird"}]);
	
	fAdvancedDataGrid = new AdvancedDataGrid();
	
	fAdvancedDataGrid.dataProvider = dataModel;
	fAdvancedDataGrid.percentWidth = 100;
	fAdvancedDataGrid.percentHeight = 100;
	
	addChild(fAdvancedDataGrid);
           

Run this simple example by downloading DeveloperWorksDataGridBaseExample in the Download table below. In this example, two columns—Person and Favorite Book—are displayed in the table based on the format and content of the data provider.

Columns automatically pick up the "keys," Person and Favorite Book, from the data provider to determine what fields in that same data provider to use when displaying information. While these few lines of code are enough to build an application with a default AdvancedDataGrid, the component has limitations when it comes to the implementation of our JTable. We found that the three major limitations were: the raw format of the data provider, the fixed number of columns, and the lack of support for color coding of cells. Raw data providers are not sufficient for data that is always changing, because the components do not receive notification of data changes. Our solution for JTable was to refresh our table by reassigning the data provider each time a user selected a new option from one of the button's pull-down menus. We overcame the limitation of a fixed number of columns by rebuilding columns dynamically when a change in data occurred. As you will see below in Listing 16, we use a renderer to customize the data and to color individual items in cells before displaying them.

In the beginning of this section, we showed how we made a call in the createChildren function of the DynamicAdvancedDataGridCanvas to the constructor of the DynamicAdvancedDataGrid to create the table. We also showed how each time the table is refreshed, a recreated data provider is assigned to the table. The action of assigning a data provider to a table occurs in the DynamicAdvancedDataGrid class, within the "set" function. This "set" function overrides the "set" function of the base class. The last line of this function is a call to the createColumns function, which dynamically creates columns in the table:


Listing 16. Renderer to customize the data
override public function set dataProvider(value:Object):void
{
	super.dataProvider = value;
  	createColumns(dataProvider as ArrayCollection);
}
           

The table's content, in the form of an array collection, is passed to the createColumns function as a parameter. The array collection contains as many rows as will be displayed in the table. Each row has as many column objects as the number of columns that will appear in the table. The first thing we need to do in the createColumn function is to find out how many column objects are in a row. This is done by simply counting the number of objects in the very first row, which we pass as a parameter to the getNumberOfProperties function in the DynamicAdvancedDataGrid class. As soon as we determine the number of columns, we loop over this number and create the columns with Flex's AdvancedDataGridColumn class. As you see in the code fragment of the createColumns function (Listing 17), the dataField of a column is assigned an order number in which the column's object appears in the row of the data provider's collection. We assign other properties such as "resizable" and "visible" to each column and push the column into the array of columns, called "theColumns." When the loop is over, we assign the array of columns to the table's property "columns." This is done in the last statement of the createColumn function.


Listing 17. DynamicAdvancedDataGrid class: createColumns function
					
private function createColumns(dp:ArrayCollection): void 
{
	if(dp.length > 0)
	{
		var numColumns:int = getNumberOfProperties(dp[0]);
		var theColumns:Array = new Array(); 
		
		for(var i:int = 0; i < numColumns; i++)
		{  
			var dgc:AdvancedDataGridColumn = 
			new AdvancedDataGridColumn();                          
			dgc.dataField = i.toString(); 
			...	      
			theColumns.push(dgc);                                       
		}
		columns = theColumns;
	}
}	  
           

Before we go on to the final part of UI implementation, the custom item renderer, we would like to point out that in the constructor of the DynamicAdvancedDataGrid class we assigned our custom item renderer (the DynamicAdvancedDataGridItemRenderer wrapped in a ClassFactory object) to the itemRenderer property of the table:

itemRenderer = new ClassFactory( DynamicAdvancedDataGridItemRenderer);
            

Item renderers allow for the display of customized data. We created our own item renderer to customize our data before showing it to the user. We based our custom renderer on Flex's HBox component and overrode the set function of this component. We modify the current row data object after it gets passed to the item renderer.

As we've mentioned before, a content cell can have more than one item in its body. John can have his three favorite books, and Ivan can have his two favorite movies. Three items have to be displayed in a cell associated with "Favorite books" and "John," while two items have to be displayed in a cell mapped to "Favorite Movies" and "Ivan."

Therefore, we decided that each element in a cell can be displayed with the help of what we call an "item viewer." An "item viewer" is a "box" that contains a text field. The item renderer creates, within its "set" function, as many item viewers in a cell as there are number of answers found in the cell's data. The ItemViewer class extends the Box class. It holds one UI component, SuperUITextField, derived from the UITextField.

Below is a fragment of the code from the DynamicAdvancedDataGridItemRenderer set function. Notice in the code fragment below (Listing 18) that the database is queried for all answers that belong to a single content cell. After getting the answers, item viewers are created in a loop over these answers.


Listing 18. DynamicAdvancedDataGridItemRenderer class: fragment from "set" function
					
if(data && data is Row)
{
	var cell:Cell = (data as Row).getCellAt(listData.columnIndex);
	
	if (cell is NullCell) return;
	
	if(cell is HeaderCell)
	{
		var viewer:ItemViewer = new ItemViewer();
		viewer.styleName = header;
		...
		viewer.title = cell.data as String;        
		addChild(viewer);
	}
	else 
	{
		// content cell
		var parameters:Array = cell.data as Array;
		var answers:Array = 
		DataBase.instance.getAnswers(parameters[1],parameters[0])
		
		for each(var answer:String in answers)
		{
			var viewer:ItemViewer = new ItemViewer();
			viewer.styleName = "content";
			viewer.title = answer;
			addChild(viewer);
		}
	}
}
           

The last implementation detail we want to mention is the use of a viewer's styleName property, which is used to color and shape the viewer. In the fragment of the code above, each viewer was assigned a styleName. If a viewer happens to be created in a header cell, the viewer's styleName will be "header." Otherwise, the styleName is "content." The background color, corner radius, and border and thickness styles of "header" and "content" viewers are defined in class selectors of the external CSS file: "example.css" (Listing 19).


Listing 19. Styling header and content viewers
.header 
{
	borderStyle : solid;
	backgroundColor: #FFFFFF;
	cornerRadius: 8;
	borderThickness: 1;		
}
.content
{
	backgroundColor: #AABBCC;
	cornerRadius: 8;	
}

Conclusion

Now you have seen how we created our Flex-based juxtaposition table, and we hope you have had a chance to create your own custom perspectives by changing the horizontal and vertical data sets. You can apply these ideas and implementation solutions to your own applications, if those applications require the dynamic creation of columns, the display of customized data, or unique shaping and coloring of cells.

In the next article, we will show how we fluidly expand and contract items in cells and how we drag-and-drop items from one cell to another.



Downloads

DescriptionNameSizeDownload method
Sample codeDataGridDeveloperWorksExample1.zip631KBHTTP
Sample codeDataGridDeveloperWorksBaseExample.zip507KBHTTP
Sample codeDataGridDeveloperWorksExample1_Source.zip14KBHTTP

Information about download methods


Resources

About the authors

Sophia Krasikov, an Research Software Engineer, holds a master's degree in applied math and computer science. Her areas of interest are Web development and visualization software.

Michael Desmond is a PhD student at the University of Limerick Ireland where he studies programmer disorientation and interface design.

Comments



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development, Sample IT projects
ArticleID=428272
ArticleTitle=Creating juxtaposition tables, Part 1: Use Flex to create JTables
publish-date=09152009
author1-email=kras@us.ibm.com
author1-email-cc=
author2-email=mdesmond@us.ibm.com
author2-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers