Error #1009, navigator is null - apache-flex

I've got some problem in Flex. Basically I want to navigate to other pages by using navigator.pushview from list through custom item renderer. This is my CustomItemRender.as. Edit:
package renderer
{
import flash.events.MouseEvent;
import mx.core.FlexGlobals;
import mx.events.FlexEvent;
import mx.events.ItemClickEvent;
import spark.components.LabelItemRenderer;
import spark.components.NavigatorContent;
import spark.components.ViewNavigator;
public class CustomItemRender extends LabelItemRenderer
{
protected var var_A:Image;
[Bindable]
public var navigator:ViewNavigator = new ViewNavigator();
public function PrgListItemRenderer()
{
super();
}
override public function set data(value:Object):void
{
super.data = value;
}
override protected function createChildren():void
{
super.createChildren();
if(!takeAtt)
{
var_A= new Image();
var_A.source = "data/pics/var_A.png";
var_A.width = 23;
var_A.height = 23;
var_A.buttonMode = true;
var_A.addEventListener(MouseEvent.CLICK, var_AItem);
addChild(var_A);
}
}
override protected function measure():void
{
super.measure();
// measure all the subcomponents here and set measuredWidth, measuredHeight,
// measuredMinWidth, and measuredMinHeight
}
/**
* #private
*
* Override this method to change how the background is drawn for
* item renderer. For performance reasons, do not call
* super.drawBackground() if you do not need to.
*/
override protected function drawBackground(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.drawBackground(unscaledWidth, unscaledHeight);
// do any drawing for the background of the item renderer here
if(selected || showsCaret)
{
graphics.beginFill(0xffffff, 1);
graphics.endFill();
}
}
public function var_AItem(event:MouseEvent):void
{
trace("navigator: "+navigator);
navigator.pushView(nextView); //this is the line that have error #1009
}
}
}
But I got Error #1009. Help me please. Thanks.

I think it's a bad idea to listen to the click event inside the item renderer.
Your basic setup should look something like this:
->ViewNavigatorApplication>
-->SomeCustomView
---> SomeListBasedComponent id="list" itemRenderer="someCustomRenderer"
Fill the list with some data, which will be presented by your itemRenderer.
Now listen to the "IndexCangeEvent" of the list (from your view) and handle the 'click' there
To your view add:
private function init():void
{
list.addEventListener(IndexChangeEvent.CHANGE , onIndexChange );
}
protected function onIndexChange(e:IndexChangeEvent):void
{
// find out which item was selected. You can use the selectedItem property for this
var item:Object = list.selectedItem;
// start the view;
navigator.pushView(MyViewClass , item.someViewData );
}
Your view will hold the reference to the ViewNavigator.
P.S. dont forget to call the init() function onCreationComplete() of your view.
To your view declaration add:
View ... creationComplete="init()" >

Related

Row renderer with height depending on row index

I have an IDropInListItemRenderer that is being used inside a List. The height of the row depends on both the data and it's position in the List's view.
How do I remeasure as soon as the row index changes?
updateDisplayList doesn't get called every time, and also by that point it's too late because the List has already measured it.
edit
The effect I'm trying to achieve is similar to iOS where the header for a section sticks to the top
Here is the basic renderer that doesn't measure correctly:
import mx.controls.Label;
import mx.controls.listClasses.BaseListData;
import mx.controls.listClasses.IDropInListItemRenderer;
import mx.controls.listClasses.IListItemRenderer;
import mx.core.UIComponent;
import mx.events.FlexEvent;
public class MyRowRenderer extends UIComponent implements IListItemRenderer, IDropInListItemRenderer {
private static const HEADER_HEIGHT:int = 20;
private static const LABEL_HEIGHT:int = 20;
private var _data:Object;
private var _label:Label;
private var _header:Label;
private var _listData:BaseListData;
public function MyRowRenderer() {
super();
}
[Bindable("dataChange")]
public function get data():Object {
return _data;
}
public function set data(value:Object):void {
this._data = value;
invalidateProperties();
dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
}
[Bindable("dataChange")]
public function get listData():BaseListData {
return _listData;
}
public function set listData(value:BaseListData):void {
_listData = value;
}
override protected function createChildren():void {
super.createChildren();
_header = new Label();
addChild(_header);
_label = new Label();
addChild(_label);
}
override protected function measure():void {
super.measure();
if (_label == null || _header == null) {
return;
}
var h:Number = LABEL_HEIGHT;
if (_listData.rowIndex == 0) {
h += HEADER_HEIGHT;
}
explicitHeight = measuredHeight = height = h;
}
override protected function commitProperties():void {
super.commitProperties();
_label.text = _data.label;
_header.text = _data.header;
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
super.updateDisplayList(unscaledWidth, unscaledHeight);
_header.visible = _header.includeInLayout = (_listData.rowIndex == 0);
if (_header.visible) {
_header.move(0, 0);
_header.setActualSize(unscaledWidth, HEADER_HEIGHT);
_label.move(0, HEADER_HEIGHT);
_label.setActualSize(unscaledWidth, LABEL_HEIGHT);
} else {
_label.move(0, 0);
_label.setActualSize(unscaledWidth, LABEL_HEIGHT);
}
}
}
}
I ended up creating a subclass of List and overriding the shiftRow function to dispatch a custom RowIndexChangeEvent. In the row renderer's set listData it adds an event listener to the owner for the RowIndexChangeEvent and invalidates it's properties, size and displaylist.

Flex: Tab Navigator tab position

I need to develop a Tab Navigator with a few tabs on the left hand side and one tab on the right hand side, I have experimented with the "tabOffset" property, but this does not seem to be able to help
Thanks in advance!
I made custom TabNavigator component.
package
{
import mx.containers.TabNavigator;
import mx.controls.Button;
import mx.events.FlexEvent;
public class CustomTabNavigator extends TabNavigator
{
public function CustomTabNavigator()
{
super();
}
override protected function createChildren(): void
{
super.createChildren();
tabBar.addEventListener(FlexEvent.CREATION_COMPLETE, addSpacer);
}
public function addSpacer(event: FlexEvent): void
{
var buttonCount: int = tabBar.getChildren().length;
var _width : Number = this.width;
var button: Button = tabBar.getChildAt(buttonCount-1) as Button;
_width = _width - button.width;
button.x = _width;
}
}
}

flex tree itemclick event, doesn't work

I'm creating a reusable flex tree component. And i would like to stick in the itemclick function. So that when a user clicks anywhere on one of the tree's Branches. the branch expands.
My problem is that I don't know how I can get the listener function to fire.
What I would like to do is create the tree completely in as3. (no mxml).
Normally I set the itemClick on tree in the mxml. but I want to do this in as3.
My component has a lot more functions in it but I have deleted them so that it becomes easier to read.
Can anyone help me out on this one? I Thought if I override the createChilderen function and add the eventlistener in there, that it would work. But no luck.
this is my code;
package
{
import mx.controls.Tree;
import mx.controls.listClasses.IListItemRenderer;
import mx.events.ItemClickEvent;
import mx.events.ListEvent;
public class MyTree extends Tree
{
public function MyTree()
{
super();
}
private function tree_itemClick(evt:ListEvent):void {
var item:Object = Tree(evt.currentTarget).selectedItem;
if (dataDescriptor.isBranch(item)) {
expandItem(item, !isItemOpen(item), true);
}
}
override protected function createChildren():void{
super.createChildren();
addEventListener(ListEvent.ITEM_CLICK, tree_itemClick, true);
}
}
}
package
{
import mx.controls.Tree;
import mx.events.ListEvent;
public class MyTree extends Tree
{
public function MyTree()
{
super();
addEventListener(ListEvent.ITEM_CLICK, itemClickHandler);
}
private function itemClickHandler(event:ListEvent):void
{
trace("Success");
}
}
}

Why is my override protected function createChildren being ignored?

Here is the error:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at view::ScoreBoard/setChipCount()[C:\Flex Builder 3\StackOverflowQuestion\src\view\ScoreBoard.as:32]
at model::MainDeckScoreBoard()[C:\Flex Builder 3\StackOverflowQuestion\src\model\MainDeckScoreBoard.as:21]
at model::MainDeckScoreBoard$cinit()
at global$init()[C:\Flex Builder 3\StackOverflowQuestion\src\model\MainDeckScoreBoard.as:5]
at main()[C:\Flex Builder 3\StackOverflowQuestion\src\main.mxml:13]
at _main_mx_managers_SystemManager/create()
at mx.managers::SystemManager/initializeTopLevelWindow()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\managers\SystemManager.as:3188]
at mx.managers::SystemManager/http://www.adobe.com/2006/flex/mx/internal::docFrameHandler()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\managers\SystemManager.as:3064]
at mx.managers::SystemManager/docFrameListener()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\managers\SystemManager.as:2916]
Here is main.mxml:
<?xml version="1.0"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="initApp()"
>
<mx:Script>
<![CDATA[
import model.MainDeckScoreBoard;
public var _mainDeckScoreBoard:MainDeckScoreBoard = MainDeckScoreBoard.instance;
private function initApp():void {
this.addChild(_mainDeckScoreBoard);
}
]]>
</mx:Script>
</mx:Application>
Here is MainDeckScoreBoard.as:
package model {
import view.ScoreBoard;
[Bindable]
public dynamic class MainDeckScoreBoard extends ScoreBoard {
/** Storage for the singleton instance. */
private static const _instance:MainDeckScoreBoard = new MainDeckScoreBoard( SingletonLock );
/** Provides singleton access to the instance. */
public static function get instance():MainDeckScoreBoard {
return _instance;
}
public function MainDeckScoreBoard( lock:Class ) {
super();
// Verify that the lock is the correct class reference.
if ( lock != SingletonLock ) {
throw new Error( "Invalid Singleton access. Use MainDeckScoreBoard.instance." );
}
this.setChipCount("0");
}
} // end class
} // end package
class SingletonLock {
} // end class
Here is ScoreBoard.as:
package view {
import mx.containers.HBox;
import view.ScoreBoardLabel;
import view.ChipCountContainer;
import view.CardRankList;
public dynamic class ScoreBoard extends HBox {
/** The chip count. */
public var _chipCount:ChipCountContainer;
public function ScoreBoard() {
super();
this.width = 489;
this.height = 40;
this.setStyle("horizontalScrollPolicy", "off");
}
override protected function createChildren():void {
super.createChildren();
if(!_chipCount) {
_chipCount = new ChipCountContainer();
this.addChild(_chipCount);
}
}
public function setChipCount(labelText:String):void {
_chipCount._chipCountLabel.text = labelText;
invalidateDisplayList();
}
}
}
Here is ChipCountContainer.as:
package view {
import mx.containers.Canvas;
public dynamic class ChipCountContainer extends Canvas {
/** The label. */
public var _chipCountLabel:ChipCountLabel;
public function ChipCountContainer() {
super();
this.width = 20;
this.height = 20;
}
override protected function createChildren():void {
super.createChildren();
if(!_chipCountLabel) {
_chipCountLabel = new ChipCountLabel();
this.addChild(_chipCountLabel);
}
}
}
}
I've methodically moved things around and waved the invalidate Display List incense while performing a create Children dance but I've only succeeded in completely confusing myself. I've searched the Flex libraries for similar constructions, and it looks OK to me, but I guess I'm just not getting the concept.
I think you're confusing the order of instantiation. Namely, if you want to use setChipCount after the children of the component have been initialized, you should wait for the initialize event to fire, i.e.:
public dynamic class MainDeckScoreBoard extends ScoreBoard {
...
public function MainDeckScoreBoard( lock:Class ) {
super();
// Verify that the lock is the correct class reference.
if ( lock != SingletonLock ) {
throw new Error( "Invalid Singleton access. Use MainDeckScoreBoard.instance." );
}
// wait for the children to be created
addEventListener(FlexEvent.INITIALIZE, onInitialize);
}
// executes when the children of this component have been created
private function onInitialize(event:FlexEvent):void {
this.setChipCount("0");
}
} // end class
For a more detailed explanation of the component instantiation lifecycle, this adobe doc may be helpful:
http://livedocs.adobe.com/flex/3/html/help.html?content=components_06.html

Flex DataGrid with row number column

I want to extend the DataGrid component so that there is a (read-only) column for the row number like you see in spreadsheets. I came across this article http://www.cflex.net/showFileDetails.cfm?ObjectID=735 but it depends on the data being unique for each row so that it can index into the array. If the data is not unique (like for an empty grid) it doesn't work. How can I implement that?
This worked for me:
<mx:Label xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.controls.AdvancedDataGrid;
private var handleDataChangedEnabled:Boolean = false;
override public function set data(value:Object):void {
super.data = value;
if (!handleDataChangedEnabled) {
addEventListener("dataChange", handleDataChanged);
}
}
public function handleDataChanged(event:Event):void {
this.text = String(listData.rowIndex + (listData.owner as AdvancedDataGrid).verticalScrollPosition + 1);
}
]]>
</mx:Script>
Of course, you would have to change AdvancedDataGrid to DataGrid.
Cheers.
ensure that the dataProvider has a unique column or property, then dont show that column/property if you dont wont to.
The key is the dataProvider
Just use this class as your itemRenderer:
RowNumColumnRenderer.as
package
{
import mx.collections.IList;
import mx.controls.AdvancedDataGrid;
import mx.controls.Label;
import mx.controls.listClasses.ListBase;
public class RowNumColumnRenderer extends Label
{
override public function set data(value:Object):void
{
super.data = value;
if (listData != null)
this.text = (AdvancedDataGrid(listData.owner).itemRendererToIndex(this) + 1).toString();
}
}
}
I was able to do this by implementing a custom itemRenderer, RowNumberRenderer.as
package com.domain
{
import mx.collections.IList;
import mx.controls.Label;
import mx.controls.listClasses.ListBase;
public class RowNumberRenderer extends Label
{
public function RowNumberRenderer()
{
super();
}
override public function set data(value:Object):void
{
super.data = value;
this.text = String(IList(ListBase(listData.owner).dataProvider).getItemIndex(data) + 1);
}
}
}
how about the following:
RendererRowIndexPlusOne.as
package
{
import mx.controls.Label;
import mx.utils.StringUtil;
import mx.utils.ObjectUtil;
public class RendererRowIndexPlusOne extends Label
{
public override function set data(item:Object):void {
super.data = item;
trace('listData.label ' + listData.label);
trace('listData.rowIndex ' + listData.rowIndex);
trace('listData.columnIndex ' + listData.columnIndex);
trace('listData.owner ' + listData.owner);
text = String(listData.rowIndex + 1);
}
}
}

Resources