How do you show a video in it's correct dimensions in codenameOne? - codenameone

I am displaying videos on a form but the video is always stretched to a square. I can't get hold of any video component to get it's true size. This is the code to display video:
imageVideoContainer = new Container(new BorderLayout(BorderLayout.CENTER_BEHAVIOR_SCALE)) {
protected Dimension calcPreferredSize() {
return new Dimension(Display.getInstance().getDisplayWidth(), Display.getInstance().getDisplayWidth());
}
};
media = MediaManager.createMedia(FileSystemStorage.getInstance().getAppHomePath() + movePath, true);
mp = new MediaPlayer(media);
mp.setAutoplay(true);
imageVideoContainer.add(BorderLayout.CENTER, mp);
container = new Container(new BoxLayout(BoxLayout.Y_AXIS));
container.add(BorderLayout.centerAbsolute(imageVideoContainer));
If I don't overwrite the calcPreferredSize it doesn't display at all. Any help appreciated. I've tried debugging to look into Media Player to get something that has a size but can't find anything.

The problem is that until the video is loaded the size isn't there. So when you add it to the form it's preferred size will be 0.
You then add it to center absolute which requires preferred size to position/size the video. A solution can be to start the video then call revalidate() to redo the layout and position the video correctly.

Related

Layout issue with Codename One

At this moment I'm only testing my app in the simulator (as I'm having issues with "Send iOS Build" mentioned in another thread [Errors with Codename One "Send iOS Build" and "Send Android Build")
I'm experiencing some layout issues where it is not making use of the width and height correctly. The elements are left-aligned and there is unused space on the right side. And I need to scroll up and down instead of having everything fit within the visual area. Please see images.
The code are:
private final void show() {
loginSignupForm = new Form("Company", new BoxLayout(0));
Tabs loginSignupTabs = new Tabs();
Style loginSignupStyle = UIManager.getInstance().getComponentStyle("Tab");
prepareAndAddSignupTab(loginSignupTabs, loginSignupStyle);
prepareAndAddLoginTab(loginSignupTabs, loginSignupStyle);
loginSignupForm.add(loginSignupTabs);
loginSignupForm.show();
}
private void prepareAndAddLoginTab(Tabs loginSignupTabs, Style loginSignupStyle) {
loginID = new TextField();
loginPassword = new TextField();
Button loginButton = getLoginButton();
Component[] loginComponents = {
new Label("Email Address"),
loginID,
new Label("Password"),
loginPassword,
loginButton,
};
Container loginContainer = BoxLayout.encloseY(loginComponents);
FontImage loginIcon = FontImage.createMaterial(FontImage.MATERIAL_QUESTION_ANSWER, loginSignupStyle);
loginSignupTabs.addTab("Login", loginIcon, loginContainer);
}
What do I need to changenter code heree to get the elements to:
1. expand to the maximum width (no free space on the right)
2. fit within the visual area (for top-to-bottom)
Please note that I'm coding the elements because I find the (new) GUI Builder quite a challenge to use.
Firstly, don't pass a constant value as an argument to Layouts, coz the values might change in future Codename One updates and this will be difficult for you to debug. new BoxLayout(0) should be new BoxLayout(BoxLayout.Y_AXIS) or simply BoxLayout.y().
The above is where the problem arose but not the only problem because BoxLayout doesn't recognize 0 as a valid argument as it has only 3 which are X_AXIS = 1, Y_AXIS = 2, and X_AXIS_NO_GROW = 3.
If you change the above to use BoxLayout.Y_AXIS, it will work, but from the screenshot above, that's not the best solution.
In conclusion, change your code to below:
private final void show() {
loginSignupForm = new Form("Company", new BorderLayout());
Tabs loginSignupTabs = new Tabs();
Style loginSignupStyle = UIManager.getInstance().getComponentStyle("Tab");
prepareAndAddSignupTab(loginSignupTabs, loginSignupStyle);
prepareAndAddLoginTab(loginSignupTabs, loginSignupStyle);
loginSignupForm.add(BorderLayout.CENTER, loginSignupTabs);
loginSignupForm.show();
}

How to control/scale image size in Codename One?

I used the (new) GUI Builder and inserted an image (by way of adding a Label). However, it appears too big. Is there anyway I can scale and control the size? (I saw something which points to cloudinary but that seems too complicated. I just want to simply scale down the image.)
There are several ways to resize images in Codename One and I will mention few below:
1.
Use MultiImages in the GUI Builder. With this multiple sizes of images are generated from one image based on the sizes you specified. In your GUI Builder, Click Images -> Add Multi Images -> Select your image -> Check Preserve Aspect Ratio -> Increase the % that represents the percentage of the screen width you want the image to occupy. Set any DPI you don't require to 0.
2.
Use ScaledImageLabel or ScaledImageButton, it will resize the image the fill available space the component is occupying.
3.
Scale the image itself in code (This is not efficient, though):
public static Image getImageFromTheme(String name) {
try {
Resources resFile = Resources.openLayered("/theme");
Image image = resFile.getImage(name);
return image;
} catch (IOException ioe) {
//Log.p("Image " + name + " not found: " + ioe);
}
return null;
}
Image resizedImage = getImageFromTheme("myImage").scaledWidth(Math.round(Display.getInstance().getDisplayWidth() / 10)); //change value as necessary
4.
Mutate the image (Create an image from another image).

Share image painted on a larger graphics than the screen itself

The code bellow will paint he component on the image graphics - but it will translate it - instead of fitting it to the size of the graphics.
int ratio = 2;
Image screenshotImage = Image.createImage(getWidth()* ratio,getHeight()* ratio);
c.revalidate();
c.setVisible(true);
c.paintComponent(screenshotImage.getGraphics(), true);
I cannot use the the image and just scale it because some of the content will be truncated.
Can the component paint itself on image graphics with specified size.
Many thanks
Use:
c.setX(0);
c.setY(0);
c.setWidth(img.getWidth());
c.setHeight(img.getHeight());
if(c instanceof Container) {
c.setShouldLayout(true);
c.layoutContainer();
}
c.remove();
c.paintComponent(myImage.getGraphics(), true);
// add c back to it's parent container and revalidate
If the component is currently on a form you would want to undo all of that by calling revalidate on the parent form afterwards.

Codename One rounded image from an internet source

I am trying to display a rounded image that I get straight from the Internet.
I used the code below to create a round mask, get the image from the Internet, then tried to either set the mask on the image or the label itself. None of these approaches worked. If I remove the mask, the image is displayed fine. If I keep the code to set the mask then all I see is an empty white circle.
I have the idea that if I apply the mask on the image itself, then it may not take effect because the image was not downloaded at the time the mask was applied.
But I don't seem to understand why calling setMask on the label is also not working.
// Create MASK
Image maskImage = Image.createImage(w, l);
Graphics g = maskImage.getGraphics();
g.setAntiAliased(true);
g.setColor(0x000000);
g.fillRect(0, 0, w, l);
g.setColor(0xffffff);
g.fillArc(0, 0, w, l, 0, 360);
Object mask = maskImage.createMask();
// GET IMAGE
com.cloudinary.Cloudinary cloudinary = new com.cloudinary.Cloudinary(ObjectUtils.asMap(
"cloud_name", "REMOVED",
"api_key", "REMOVED",
"api_secret", "REMOVED"));
// Disable private CDN URLs as this doesn't seem to work with free accounts
cloudinary.config.privateCdn = false;
Image placeholder = Image.createImage(150, 150);
EncodedImage encImage = EncodedImage.createFromImage(placeholder, false);
Image img2 = cloudinary.url()
.type("fetch") // Says we are fetching an image
.format("jpg") // We want it to be a jpg
.transformation(
new Transformation()
.radius("max").width(150).height(150).crop("thumb").gravity("faces").image(encImage, "http://upload.wikimedia.org/wikipedia/commons/4/46/Jennifer_Lawrence_at_the_83rd_Academy_Awards.jpg");
Label label = new Label(img2);
label.setMask(mask); // also tried to do img2.applyMask(mask); before passing img2
So I tried various things:
1) Removing the mask that was set through cloudinary - That did not work
2) applied the mask to the placeholder & encoded image (as expected these shouldnt affect the final version that is getting published)
3) This is what works! I am not sure if the issue is really with downloading the picture before or after applying the mask.. time can tell down the road
Label label = new Label();
img2.applyMask(mask); // If you remove this line , the image will no longer be displayed, I will only see a rounded white circle ! I am not sure what this is doing, it might be simply stalling the process until the image is downloaded? or maybe somehow calling repaint or revalidate
label.setIcon( img2.applyMask(mask));
Here is what worked for me if anyone else is having similar issues:
//CREATE MASK
Image maskImage = Image.createImage(w, l);
Graphics g = maskImage.getGraphics();
g.setAntiAliased(true);
g.setColor(0x000000);
g.fillRect(0, 0, w, l);
g.setColor(0xffffff);
g.fillArc(0, 0, w, l, 0, 360);
Object mask = maskImage.createMask();
//CONNECT TO CLOUDINARY
com.cloudinary.Cloudinary cloudinary = new com.cloudinary.Cloudinary(ObjectUtils.asMap(
"cloud_name", "REMOVED",
"api_key", "REMOVED",
"api_secret", "REMOVED"));
// Disable private CDN URLs as this doesn't seem to work with free accounts
cloudinary.config.privateCdn = false;
//CREATE IMAGE PLACEHOLDERS
Image placeholder = Image.createImage(w, l);
EncodedImage encImage = EncodedImage.createFromImage(placeholder, false);
//DOWNLOAD IMAGE
Image img2 = cloudinary.url()
.type("fetch") // Says we are fetching an image
.format("jpg") // We want it to be a jpg
.transformation(
new Transformation()
.crop("thumb").gravity("faces")
.image(encImage, url);
// Add the image to a label and place it on the form.
//GetCircleImage(img2);
Label label = new Label();
img2.applyMask(mask); // If you remove this line , the image will no longer be displayed, I will only see a rounded white circle ! I am not sure what this is doing, it might be simply stalling the process until the image is downloaded? or maybe somehow calling repaint or revalidate
label.setIcon( img2.applyMask(mask));
Shai, I seriously appreciate your time!! Thank you very much. Will have to dig more into it if it gives me any other problems later but it seems to consistently work for now.
The Cloudinary API returns a URLImage which doesn't work well with the Label.setMask() method because, technically, a URLImage is an animated image (it is a placeholder image until it finishes loading, and then "animates" to become the target image).
I have just released a new version of the cloudinary cn1lib which gives you a couple of options for working around this.
I have added two new image() methods. One that takes an ImageAdapter parameter that you can use to apply the mask to the image itself, before setting it as the icon for the label. Then you wouldn't use Label.setMask() at all.
See javadocs for this method here
The other method uses the new Async image loading APIs underneath to load the image asynchronously. The image you receive in the callback is a "real" image so you can use it with a mask normally.
See javadocs for this method here
We are looking at adding a soft warning to the Label.setMask() and setIcon() methods if you try to add an "animated" image and mask it so that it is more clear.
I think the making code you set to the label might be conflicting with the masking code you get from Cloudinary.

LoaderMax: setting array as a container (ImageLoader)

So, I have a LoaderMax instance loading images from various URLs. I want to add all loaded images to an array.
Here's my code:
var photosArray:Array = new Array(5);
var imageLoadingQueue:LoaderMax = new LoaderMax({name:"mainQueue", onComplete:completeHandler});
for (var g:uint=0; g<5; g++)
{
imageLoadingQueue.append(new ImageLoader("/img" + g + ".jpg", {name:"photo", container:photosArray[g], noCache:false, smoothing:true, width:126, height:126, scaleMode:"proportionalOutside"}));
}
imageLoadingQueue.load();
private function completeHandler(e:LoaderEvent):void
{
trace("finished loading pictures!");
//the next two lines will return an error (saying that photosArray[1] is null)
stage.addChild(photosArray[1]);
photosArray[1].x = 250;
}
A few problems:
If I set the container of the image being loaded to the Array, it won't work. I'm not being able to access the image inside the array because it says it's null.
If I set the container of the image being loaded to "this" (using the container property when appending a new ImageLoader) and, on the completeHandler, set my array equal to event.target.content, it kinda works (but it's not the ideal). The problem is that, by doing so, the images are appearing on the stage as they are loaded, and I do no want them to do so.
Any help would be heavily appreciated.
Thanks!!
David is correct, but I also wanted to mention that the LoaderMax's "content" is actually an array of all of its children's content, so you could just use that for simplicity. Keep in mind that ImageLoaders automatically create a Sprite (technically called a "ContentDisplay") to drop the image into so you probably don't need to create ANOTHER Sprite (a container for the container).
var photos:Array = imageLoadingQueue.content;
stage.addChild(photos[1]);
The other nice thing is that it creates the ContentDisplay Sprites immediately, even before any content is loaded into them, so you can place them and size them however you want while (or before or after) loading occurs.
The container needs to be a DisplayObjectContainer. ImageLoader will try to add the image to the container using addChild(), so obviously this won't work with an empty array. Create a new Sprite for each image and add it into the array first:
for (var g:uint=0; g<5; g++)
{
photosArray[g] = new Sprite();
imageLoadingQueue.append(new ImageLoader("/img" + g + ".jpg", {name:"photo", container:photosArray[g], noCache:false, smoothing:true, width:126, height:126, scaleMode:"proportionalOutside"}));
}

Resources