AS3 AIR: Smooth scrolling for mobile - android

So, I'm building fairly simple application for portrait mode only, and it will have vertical scrolling for content.
Content is basicly a MovieClip containing image as background and other minor elements (couple small MovieClips as buttons etc).
The problem is that I don't seem to find a smooth way to scroll my content. I would love it to be smooth and accurate, and I've tried numerous different ways for it.
For testing purposes the background I'm using (inside my content MovieClip) is 533x1250 jpg from library.
This is how I add my content mc and backround into it:
scaleXFactor = stage.stageWidth / 640; //my stage is 640x960
...
addChild(content); //which is new MovieClip()
var bg:bg1 = new bg1();
bg.x = p1bg.y = 0;
bg.width = 640 * scaleXFactor;
bg.scaleY = bg.scaleX;
content.width = bg.width;
content.height = bg.height;
content.addChild(bg);
For scrolling I assumed I'd be safe by just simply using MOUSE_DOWN & MOUSE_UP event listeners and basic ENTER_FRAME function like this:
mouseLastPos = stage.mouseY;
if(MouseUpVar == 1){
mousePosDiff = (mouseOriPosY-mouseLastPos);
}
content.y -= mousePosDiff*1.2;
mouseOriPosY = stage.mouseY;
if(MouseUpVar == 0){ //Smoothening which is allowed by MOUSE_UP
if(mousePosDiff > 0){
mousePosDiff -= 0.001;
}else if(mousePosDiff < 0){
mousePosDiff += 0.001;
}
}
if(content.y < (content.height - stage.stageHeight)*-1){
content.y = (content.height - stage.stageHeight)*-1;
}else if(content.y > 0){
content.y = 0;
}
In debug player it's as smooth as you can ever imagine, but in real device (Samsung Note4) it's very laggy and clumsy.
I've also tried freshplanet's script, which is not as laggy, but still a little clumsy, and messes up my scaleY for some reason.
And IScrollerComponent seems to have some issues on AIR, and I didn't get any visual elements on my screen with it.
Rendering settings or cacheAsBitmap didn't seem to have any effect on this.
How should this be done right? Is it normal that this 'small' image (533x1250) causes bad lag?
I really need to use AS3 and AIR for this, so I'm hoping to find a good and "simple" solution (yeah, that doesn't happen too often).
Thanks,
kaarto

Try using cacheAsBitmap=true on your content and scrollRect instead of moving your content.

Related

Screen orientation made bottom buttons disappear

I have a very odd problem with the bottom navigation buttons. Our device has for a reason unknown to us set ro.sf.hwrotation=270 in device properties list. With this settings everything works in portrait mode as intended (the boot logo/animation, system activities, our app, ...). What does not work is Miracast display which is, as you might have guessed it, incorrectly rotated.
So I have decided to look at it more closely and as the first step changed the property to ro.sf.hwrotation=0. Luckily this has fixed the Miracast issue, all system activities are still in portrait, our app ditto except this weird issue:
The buttons are gone. When rotated to landscape, again, everything is as it should except the buttons (now even stuck to the right side):
Can somebody give me a hint where to look? I can't see anything suspicious in the logs, no crash or exception.
Most likely the buttons are somehow weirdly rotated too but then again, why only the buttons? Is there something specific to them? If I revert the setting, everything is back to normal.
Although this is hardly a generic solution, it may help someone in the future: our device had tweaked not only the rotation setting but also ro.sf.lcd_density (changed from 160 to 188).
While experimenting I noticed that Overview (recent apps) button still somehow works, it's just hardly reachable (and not visible). So the button was there.
After yet another round of experiments and I noticed an innocent change of the density setting - and guess what, after reverting the value to 160 it worked again! For some reason we had decreased the LCD's width/height this way.
Of course, it comes with a price - now that we have increased LCD resolution, the device shows one button more and even in different order.
Accidentally, when I was about to start to live with different DPI/button arrangement I found this post: https://community.nxp.com/thread/384348#comment-831065. The author handles only one side of the display so this is a version to handle all four of them:
--- a/android4.2/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/android4.2/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
## -415,7 +415,20 ## public class NavigationBarView extends LinearLayout {
for (int i=0; i<4; i++) {
mRotatedViews[i].setVisibility(View.GONE);
}
- mCurrentView = mRotatedViews[rot];
+ //mCurrentView = mRotatedViews[rot];
+
+ if (rot == Surface.ROTATION_0 && mVertical) {
+ mCurrentView = mRotatedViews[Surface.ROTATION_90];
+ } else if (rot == Surface.ROTATION_90 && !mVertical) {
+ mCurrentView = mRotatedViews[Surface.ROTATION_180];
+ } else if (rot == Surface.ROTATION_180 && mVertical) {
+ mCurrentView = mRotatedViews[Surface.ROTATION_270];
+ } else if (rot == Surface.ROTATION_270 && !mVertical) {
+ mCurrentView = mRotatedViews[Surface.ROTATION_0];
+ } else {
+ mCurrentView = mRotatedViews[rot];
+ }
+
mCurrentView.setVisibility(View.VISIBLE);
mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone);
After applying this patch I finally have a good solution - DPI is unchanged (i.e. 188), buttons are visible, HDMI is fixed and everything works as before.

Making a android-desktop-style PageView

This is a very simple example to achieve the android multi screen wallpaper effect on iOS, written in Swift. Sorry for the poor and redundant English as I am a non-native speaker and new to coding.
Many android phones have a multi screen desktop with the feature that when you swipe between screens, the wallpaper moves horizontally according to your gesture. Usually the wallpaper will move in a smaller scale comparing to the icons or search boxes to convey a sense of depth.
To do this, I use a UIScrollView instead of a UIPageViewController.I tried the latter at first, which makes the viewController hierarchy become very complex, and I couldn't use the touchesMove method correctly, as when you pan around pages, a new child viewController will come in and interrupt the method.
Instead, I tried UIScrollView and here is what I have reached.
drag a wallpaper imageView into viewController, set the image, set the frame to (0,0,width of image, height of screen)
set the viewController size to freedom and set the width to theNumberOfPagesYouWantToDisplay * screenWidth (only to make the next steps easier, the width of the viewController will not affect the final result.)
drag a scrollView that fits in the viewController
add the "content UIs" into the scrollView to become a childView of it. Or you can do it using codes.
add a pageControl if you would like.
in your viewController.swift add the following code
import UIKit
class ViewController: UIViewController, UIScrollViewDelegate{
let pagesToDisplay = 10 // You can modify this to test the result.
let scrollViewWidth = CGFloat(320)
let scrollViewHeight = CGFloat(568) // You can also modify these based on the screen size of your device, this is an example for iPhone 5/5s
#IBOutlet weak var backgroundView: UIImageView!
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var pageControl: UIPageControl!
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
scrollView.pagingEnabled = true
// Makes the scrollView feel like pageViewController.
scrollView.frame = CGRectMake(0,0,scrollViewWidth,scrollViewHeight)
//Remember to set the frame back to the screen size.
scrollView.contentSize = CGSizeMake(CGFloat(pagesToDisplay) * scrollViewWidth, scrollViewHeight)
pageControl.numberOfPages = pagesToDisplay
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
let pageIndex = Int(scrollView.contentOffset.x / scrollView.frame.size.width)
pageControl.currentPage = pageIndex
}
func scrollViewDidScroll(scrollView: UIScrollView) {
let bgvMaxOffset = scrollView.frame.width - backgroundView.frame.width
let svMaxOffset = scrollView.frame.width * CGFloat(pagesToDisplay - 1)
if scrollView.contentOffset.x >= 0 &&
scrollView.contentOffset.x <= svMaxOffset {
backgroundView.frame.origin.x = scrollView.contentOffset.x * (bgvMaxOffset / svMaxOffset)
}
}
}
Feel free to try this on your device, and it will be really appreciated if someone could give some advice on this. Thank you.

as3 air and screen size dection issue on devices

I have worked so hard on an app that displays perfectly on my Galaxy Note 3. However, it does not display right on iPhone and also one other Droid I tested on. My issue is with addChild() and then resizing it to fit the screen. For some reason when I add the Background (addBG(); The screen size works but if I load addChild to the BG, this works great on my Note 3 but not on the iPhone or another android device.
My issue is with the screenX, screenY var I created. They seem to be different for devices. Or it is a "rendering order issue" I am not sure. Would be so greatful for the help to fix this issue so it looks great on each phone. I have read some tuts on this subject but they are confusing. I think my code is close, I hope, and maybe it just needs a tweak. !
Here is a shot of the about screen on an IPhone. See the white does not fit the whole screen.
and Here is a shot from my Droid Note 3.
Declared Vars in a package:
This is not my full code, of course but only that which I believe is relevant.
public var screenX:int;
public var screenY:int;
public function Main()
{
if (stage)
{
setStage();
addBG();
}
}
public function setStage()
{
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
if (flash.system.Capabilities.screenResolutionX > stage.stageWidth)
{
screenX = stage.stageWidth;
screenY = stage.stageHeight;
}
else
{
screenX = flash.system.Capabilities.screenResolutionX;
screenY = flash.system.Capabilities.screenResolutionY;
}
}
This works: addBG();
public function addBG()
{
theBG = new BG();
addChild(theBG);
theBG.width = screenX;
theBG.height = screenY;
}
This does not: addAbout();
public function addAbout()
{
About = new viewAbout();
About.width = screenX;
About.height = screenY;
theBG.addChild(About);
TweenMax.fromTo(About,1, {alpha:0}, {alpha:1, ease:Expo.easeOut} );
}
UPDATE: And yet another more complex load is also called from a button and having the same issue. I hope you see the logic of what I am trying to do. First set the BG to the device then load and resize content to fit proportionally into the BG I loaded. The reason being, the BG will be distorted to different proportions and that's ok, but the content can not. So here is the example that loads content into the BG and then content into that container.
public function addRosaryApp()
{
Rosary = new viewRosaryApp();
Rosary.width = screenX;
Rosary.height = screenY;
theBG.addChild(Rosary);
TweenMax.fromTo(Rosary,1, {alpha:0}, {alpha:1, ease:Expo.easeOut} );
contentRosary = new contentRosaryApp();
theBG.addChild(contentRosary);
contentRosary.width = screenX;
contentRosary.scaleY = contentRosary.scaleX;
contentRosary.x = screenX/2 - contentRosary.width/2;
contentRosary.y = menuBanner.height;
}
Have you tried adding the child to stage first, and then setting the size? That's the only difference I can see between addBG and addAbout
About = new viewAbout();
theBG.addChild(About); // do this first
About.width = screenX; // then set width
About.height = screenY; // and height
I think your problem may have to do with either of several things.
My findings so far from my own devices (an Ipad Air, an iphone 4S, an LG G2 and an Acer Iconia A500) is that the only device size that's being reported correctly at all times is the stage.fullScreenWidth and the stage.fullScreenHeight
1st, Android reports Capabilities.screenResolutionX as the LONG side of your Android device.
IOS devices however report the SHORT side of your device to Capabilities.screenResolutionX.
2nd, stage.stageWidth and stage.stageHeight seem to report the wrong size on both Android and IOS.
And in fact if you check these before and after setting stage.scaleMode and stage.align then the values will differ completly (although Android will show it almost correctly).
My suggestion is that you set a base size (width and height) for your app and then compare it with the actual stage.fullScreenWidth and stage.fullScreenHeight reported by the device. That way you can get some nice scaleratios to use to scale your displayobjects. That's what I'm currently on my apps and it scales just fine :-)

Resizing a Flash loader after Event.COMPLETE doesn't work

Google suggests that the following is not that uncommon a question: having loaded something into a Flash stage using a Loader, I want to resize it. However, if you do this before the content is loaded, resizing the image causes it to disappear.
The proposed solution is usually to use an Event listener for Event.COMPLETE. Here's my code:
public function FlixelTest()
{
super();
// support autoOrients
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
myLoader = new Loader();
myLoader.x = (stage.fullScreenWidth-640)/2;
myLoader.y = (stage.fullScreenHeight-480)/2;
addChild(myLoader);
var url:URLRequest = new URLRequest("stuff.swf");
myLoader.load(url);
myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadProdComplete);
}
function loadProdComplete(e:Event):void{
myLoader.height = 480;
myLoader.width = 640;
}
According to every posting I can find so far online, this solution should work. When the event fires, the Loader is done, and can be resized. However, it doesn't. Commenting out the lines that modify .height and .width cause the SWF to appear, uncommenting them and running again and the SWF never loads.
Could anything else be interfering here? This is using FlashBuilder to construct an Actionscript3 -> Android project.
EDIT - The solution here doesn't appear to work either: Problem resizing loader after loading swf
UPDATE - I have a working, and horrific, solution that is as followed:
function loadProdComplete(e:Event):void{
if(myLoader.width != 0){
myLoader.width = 640;
myLoader.content.width = 640;
}
else{
timer = new Timer(500);
timer.addEventListener(TimerEvent.TIMER, timertick); // there should be a comma here but yahoo replaces it with ...
timer.start();
}
}
function timertick(e:TimerEvent){
timer.stop();
stage.dispatchEvent(new Event(Event.COMPLETE));
}
It basically uses the content's width to see if it's finished loading. If it hasn't, it waits a half second and refires the COMPLETE event. This actually helps display it (although the width hasn't been adjusted, I assume that's a separate issue) - I can't believe this is the only way to get it working...
While not strictly a solution, the way I've circumvented this is by not resizing it at all - I am instead cropping an area over the window so all that's seen is the section I wanted to display.
function loadProdComplete(e:Event):void{
var gameMask : Shape = new Shape;
gameMask.graphics.beginFill(0xffcc00);
gameMask.graphics.drawRect(myLoader.x,myLoader.y,640,480);
gameMask.graphics.endFill();
myLoader.content.mask = gameMask;
}
Since the mask would've been necessary for me anyway to hide off-stage clutter, this solved two problems at once.
I won't accept this as the answer in case anyone has any alternative insights, but if anyone comes across this question, this is a posisble solution.

How can I able to move the content up and down in AIR-Android ?

I'm using Flash CS 5 and Flex 4, both to build an AIR application for android. I would like to know how to allow the user to move content(image or text) up and down(like a map,in this case only vertically).
There are no touch UI controls available yet, so you need to implement it yourself. Here's a little bit of code that might help get you started. I wrote it on the timeline so that I could test it quickly. You'll need to make a couple adjustments if you're using it in a class.
The variable content is a MovieClip that is on the stage. If it is larger than the height of the stage, you'll be able to scroll it by dragging it with the mouse (or with your finger on a touch screen). If it is smaller than the height of the stage, then it won't scroll at all because it doesn't need to.
var maxY:Number = 0;
var minY:Number = Math.min(0, stage.stageHeight - content.height);
var _startY:Number;
var _startMouseY:Number;
addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
function mouseDownHandler(event:MouseEvent):void
{
_startY = content.y;
_startMouseY = mouseY;
stage.addEventListener(MouseEvent.MOUSE_MOVE, stage_mouseMoveHandler, false, 0, true);
stage.addEventListener(MouseEvent.MOUSE_UP, stage_mouseUpHandler, false, 0, true);
}
function stage_mouseMoveHandler(event:MouseEvent):void
{
var offsetY:Number = mouseY - _startMouseY;
content.y = Math.max(Math.min(maxY, _startY + offsetY), minY);
}
function stage_mouseUpHandler(event:MouseEvent):void
{
stage.removeEventListener(MouseEvent.MOUSE_MOVE, stage_mouseMoveHandler);
stage.removeEventListener(MouseEvent.MOUSE_UP, stage_mouseUpHandler);
}
Alternatively, you could use the scrollRect property. That one is pretty nice because it will mask the content to a rectangular region for you. If you just change y like in the code above, you can draw other display objects on top of the scrolling content to simulate masking. It's faster than scrollRect too.

Categories

Resources