UI Navigation Problems : Cant go to a page more than one time

Hi,
I did a little example application to Navigate from a Main Page with 3 buttons to page1, page2 or page3 by clicking on the main Page buttons and each page got a back button to go back to the main Page. I did this little application inspired by the demo-widget example from microEJ Github.

The problem is that I can go to each page once but if I am in the main Page and I want to go again in an already visited page, it throw the following error.

exceptionthrown

It seems to be link to this method problematic_method
The content of my page already got a parent so,
My principal question is about when/where did I have to detached Widget Parents or detached desktop widgets’?

Thank you!

regards,

Tike

I provide below the code of my mainPage, page and Navigation classes to help

Main Page :

/**
 *
 */
package mainpage;

import com.mycompany.Navigation;
import com.mycompany.Page;
import com.mycompany.Pages;

import ej.mwt.Widget;
import ej.mwt.stylesheet.cascading.CascadingStylesheet;
import ej.widget.basic.Button;
import ej.widget.basic.OnClickListener;
import ej.widget.container.LayoutOrientation;
import ej.widget.container.List;

/**
 *
 */
public class MainPage implements Page {

	@Override
	public String getName() {
		return "MainPage"; //$NON-NLS-1$
	}

	@Override
	public void populateStylesheet(CascadingStylesheet stylesheet) {
		//
	}

	@Override
	public Widget getContentWidget() {
		List pageButtonList = new List(LayoutOrientation.HORIZONTAL);

		for (int i = 0; i < Pages.getNumPages(); i++) {
			final Page page = Pages.getPage(i);
			Button pageButton = new Button(page.getName());
			pageButton.setOnClickListener(new OnClickListener() {
				@Override
				public void onClick() {
					Navigation.showPage(page);
				}
			});
			pageButtonList.addChild(pageButton);
		}
		return pageButtonList;
	}

}

Page :

/**
 *
 */
package page1;

import com.mycompany.Navigation;
import com.mycompany.Page;

import ej.mwt.Widget;
import ej.mwt.stylesheet.cascading.CascadingStylesheet;
import ej.widget.basic.ImageButton;
import ej.widget.basic.Label;
import ej.widget.basic.OnClickListener;
import ej.widget.container.Canvas;

/**
 *
 */
public class Page1 implements Page {
	private static final String IMAGE_PATH = "/images/back.png";

	@Override
	public String getName() {
		return "Page1";
	}

	@Override
	public void populateStylesheet(CascadingStylesheet stylesheet) {
	}

	@Override
	public Widget getContentWidget() {
		Canvas pageContent = new Canvas();
		Label title = new Label(getName());
		ImageButton backButton = new ImageButton(IMAGE_PATH);
		backButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick() {
				Navigation.showMainPage();
			}
		});
		pageContent.addChild(title, 0, 0, Widget.NO_CONSTRAINT, Widget.NO_CONSTRAINT);
		pageContent.addChild(backButton, 0, 50, Widget.NO_CONSTRAINT, Widget.NO_CONSTRAINT);

		return pageContent;
	}
}

Navigation :

/**
 *
 */
package com.mycompany;

import ej.annotation.Nullable;
import ej.microui.MicroUI;
import ej.microui.display.Display;
import ej.mwt.Desktop;
import ej.mwt.Widget;
import ej.mwt.stylesheet.CachedStylesheet;
import ej.mwt.stylesheet.Stylesheet;
import ej.mwt.stylesheet.cascading.CascadingStylesheet;
import mainpage.MainPage;

/**
 *
 */
public class Navigation {
	@Nullable
	private static Desktop mainDesktop;

	private Navigation() {
	}

	public static void main(String[] args) {
		MicroUI.start();
		Desktop desktop = createDesktop(new MainPage());
		mainDesktop = desktop;
		Display.getDisplay().requestShow(desktop);
	}

	public static void showMainPage() {
		Desktop desktop = mainDesktop;
		assert desktop != null;

		Widget pageWidget = desktop.getWidget();
		assert (pageWidget != null);
		Display.getDisplay().requestShow(desktop);
	}

	public static void showPage(Page page) {
		Desktop desktop = createDesktop(page);
		Display.getDisplay().requestShow(desktop);
	}

	private static Desktop createDesktop(Page page) {
		Stylesheet stylesheet = createStylesheet(page);
		Widget widget = page.getContentWidget();

		Desktop desktop = PageHelper.createDesktop();
		desktop.setStylesheet(stylesheet);
		desktop.setWidget(widget);
		return desktop;
	}

	private static Stylesheet createStylesheet(Page page) {
		CascadingStylesheet stylesheet = new CascadingStylesheet();
		page.populateStylesheet(stylesheet);
		return new CachedStylesheet(stylesheet);
	}
}

Thank you

Hello tike,

The reason why you have this error is that you are trying to add a widget to a hierarchy while it is already part of another. This is why an IllegalArgumentException is thrown by the Widget.setParent() method that you mentioned: this means that you are trying to add the widget as a child of a container, however it is already the child of another one.

I think that the issue comes from the fact that you are making the Canvas of class Page1 a static field:

  • The first time you show Page1, the canvas is added to a new Desktop (in the createDesktop(Page page) method). However, in the lifecycle of your application, you never removes this canvas from its first desktop (i.e., the canvas remains the root widget of this desktop).
  • When you want to show Page1 a second time, you are trying to add the same canvas to a new desktop again: the framework detects that the canvas already has a parent and throws an exception to prevent any inconsistency in the widget hierarchy.

Having this field is also an issue because every time you want to show Page1 again you will end up adding a new title and a new back button to your canvas. You will have the widget tree of Page1 growing by two widgets each time, which is not what you want I suppose.
It is also a memory leaking issue in this case because the widget tree of Page1 is retained in heap for the whole execution time.

In your case, best is to remove this field and to create the canvas in the method getContentWidget() of Page1:

@Override
public Widget getContentWidget(){
Canvas content = new Canvas();
/* create the title and button ... */
content.addChild(title, ...);
content.addChild(backButton, ...);
return content;
}

This way, each time you want to show Page1, a new desktop is created with a new widget tree (new canvas, new title, new back button). When you will leave Page1, the desktop and children widgets will get unreferenced and garbage collected. And so on.

Hope it helps,

Best regards,

Thomas Pons for MicroEJ

1 Like

Hello Thomas,

Thank you for your answer, I understand the origin of the issue and the solution you gave worked very well !

Best regards,

Tike