[Feature] ScrollScene

  ... the case you feel the need for a new feature or want to submit one.

Re: [Feature] ScrollScene

Postby sjkm » Thu Dec 06, 2012 11:29 pm

Hey,

Thanks korn3l for this amazing piece of code!
I made some small modifications in order to use less memory and probably performance.

The modified class only creates one MoveXModifier (& Listener) and uses just that instance for further movements except if you set a new Modifier Ease function then a new MoveXModifier (& Listener) is created through the dirty-check mechanism.

Here the modified class:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. /**
  2.  * This is an implementation of cocos2d-x's <a href=
  3.  * "https://github.com/cocos2d/cocos2d-x-extensions/tree/master/extensions/CCScrollLayer"
  4.  * >CCScrollLayer</a> for AndEngine.
  5.  *
  6.  * @author korn3l / SJKM (small improvement modifications)
  7.  * @since Nov 26, 2012
  8.  */
  9.  
  10. public class ScrollScene extends Scene implements IOnSceneTouchListener {
  11.  
  12.         private static final float SLIDE_DURATION_DEFAULT = 0.3f;
  13.         private static final float MINIMUM_TOUCH_LENGTH_TO_SLIDE_DEFAULT = 30f;
  14.         private static final float MINIMUM_TOUCH_LENGTH_TO_CHANGE_PAGE_DEFAULT = 100f;
  15.  
  16.         // XXX is this really necessary since the scene has a list of children ?
  17.         private SmartList<IAreaShape> mPages = new SmartList<IAreaShape>();
  18.         private ScrollState mState;
  19.         private IOnScrollScenePageListener mOnScrollScenePageListener;
  20.         private int mCurrentPage;
  21.         private float mStartSwipe;
  22.         private float mMinimumTouchLengthToSlide;
  23.         private float mMinimumTouchLengthToChagePage;
  24.         private float lastX;
  25.         private float mPageWidth;
  26.         private float mPageHeight;
  27.         private float mOffset;
  28.         private MoveXModifier mMoveXModifier;
  29.         private IEaseFunction mEaseFunction;
  30.         private IModifierListener<IEntity> mMoveXModifierListener;
  31.         private boolean mEaseFunctionDirty = false;
  32.  
  33.         public ScrollScene() {
  34.                 this(0, 0, MINIMUM_TOUCH_LENGTH_TO_SLIDE_DEFAULT, MINIMUM_TOUCH_LENGTH_TO_CHANGE_PAGE_DEFAULT);
  35.         }
  36.  
  37.         public ScrollScene(final float pPageWidth, final float pPageHeight) {
  38.                 this(pPageWidth, pPageHeight, MINIMUM_TOUCH_LENGTH_TO_SLIDE_DEFAULT, MINIMUM_TOUCH_LENGTH_TO_CHANGE_PAGE_DEFAULT);
  39.         }
  40.  
  41.         public ScrollScene(final float pPageWidth, final float pPageHeight, float pMinimumTouchLengthToSlide, float pMinimumTouchLengthToChangePage) {
  42.                 this.mPageWidth = pPageWidth;
  43.                 this.mPageHeight = pPageHeight;
  44.  
  45.                 this.mMinimumTouchLengthToSlide = pMinimumTouchLengthToSlide;
  46.                 this.mMinimumTouchLengthToChagePage = pMinimumTouchLengthToChangePage;
  47.                 this.mEaseFunction = EaseLinear.getInstance();
  48.  
  49.                 this.setOnSceneTouchListener(this);
  50.                 this.mCurrentPage = 0;
  51.         }
  52.  
  53.         public int getPagesCount() {
  54.                 return this.mPages.size();
  55.         }
  56.  
  57.         public float getPageWidth() {
  58.                 return this.mPageWidth;
  59.         }
  60.  
  61.         public float getPageHeight() {
  62.                 return this.mPageHeight;
  63.         }
  64.  
  65.         public void registerScrollScenePageListener(IOnScrollScenePageListener pOnScrollScenePageListener) {
  66.                 this.mOnScrollScenePageListener = pOnScrollScenePageListener;
  67.         }
  68.  
  69.         public void setEaseFunction(IEaseFunction pEaseFunction) {
  70.                 this.mEaseFunction = pEaseFunction;
  71.                 this.mEaseFunctionDirty = true;
  72.         }
  73.  
  74.         public void setPageWidth(float pageWidth) {
  75.                 this.mPageWidth = pageWidth;
  76.         }
  77.  
  78.         public void setPageHeight(float pageHeight) {
  79.                 this.mPageHeight = pageHeight;
  80.         }
  81.  
  82.         public void setOffset(float offset) {
  83.                 this.mOffset = offset;
  84.         }
  85.  
  86.         public void setMinimumLengthToSlide(float pMinimumTouchLengthToSlide) {
  87.                 this.mMinimumTouchLengthToSlide = pMinimumTouchLengthToSlide;
  88.         }
  89.  
  90.         public void setMinimumLengthToChangePage(
  91.                         float pMinimumTouchLengthToChangePage) {
  92.                 this.mMinimumTouchLengthToChagePage = pMinimumTouchLengthToChangePage;
  93.         }
  94.  
  95.         public boolean isFirstPage(IAreaShape pPage) {
  96.                 if (this.mPages.getFirst().equals(pPage)) {
  97.                         return true;
  98.                 }
  99.                 return false;
  100.         }
  101.  
  102.         public boolean isLastPage(IAreaShape pPage) {
  103.                 if (this.mPages.getLast().equals(pPage)) {
  104.                         return true;
  105.                 }
  106.                 return false;
  107.         }
  108.  
  109.         public IAreaShape getCurrentPage() {
  110.                 return this.mPages.get(this.mCurrentPage);
  111.         }
  112.  
  113.         @Override
  114.         public boolean onSceneTouchEvent(final Scene pScene, final TouchEvent pSceneTouchEvent) {
  115.                 final float touchX = pSceneTouchEvent.getX();
  116.                 switch (pSceneTouchEvent.getAction()) {
  117.                 case TouchEvent.ACTION_DOWN:
  118.                         this.mStartSwipe = touchX;
  119.                         this.lastX = this.getX();
  120.                         this.mState = ScrollState.IDLE;
  121.                         return true;
  122.                 case TouchEvent.ACTION_MOVE:
  123.                         if (this.mState != ScrollState.SLIDING
  124.                                         && Math.abs(touchX - mStartSwipe) >= this.mMinimumTouchLengthToSlide) {
  125.                                 this.mState = ScrollState.SLIDING;
  126.                                 // Avoid jerk after state change.
  127.                                 this.mStartSwipe = touchX;
  128.                                 return true;
  129.                         } else if (this.mState == ScrollState.SLIDING) {
  130.                                 float offsetX = touchX - mStartSwipe;
  131.                                 this.setX(lastX + offsetX);
  132.                                 return true;
  133.                         } else {
  134.                                 return false;
  135.                         }
  136.                 case TouchEvent.ACTION_UP:
  137.                 case TouchEvent.ACTION_CANCEL:
  138.                         if (this.mState == ScrollState.SLIDING) {
  139.                                 int selectedPage = this.mCurrentPage;
  140.                                 float delta = touchX - mStartSwipe;
  141.                                 if (Math.abs(delta) >= this.mMinimumTouchLengthToChagePage) {
  142.                                         if (delta < 0.f && selectedPage < this.mPages.size() - 1)
  143.                                                 selectedPage++;
  144.                                         else if (delta > 0.f && selectedPage > 0)
  145.                                                 selectedPage--;
  146.                                 }
  147.                                 moveToPage(selectedPage);
  148.                         }
  149.                         return true;
  150.                 default:
  151.                         return false;
  152.                 }
  153.         }
  154.  
  155.         /**
  156.          * Updates all pages positions & adds them as children if needed. Can be
  157.          * used to update position of pages after screen reshape, or for update
  158.          * after dynamic page add/remove.
  159.          */
  160.         public void updatePages() {
  161.                 int i = 0;
  162.                 for (IAreaShape page : mPages) {
  163.                         page.setPosition(i * (this.mPageWidth - this.mOffset), 0);
  164.                         i++;
  165.                 }
  166.         }
  167.  
  168.         /**
  169.          * Adds new page to the right end of the scroll scene.
  170.          *
  171.          * @param pPage
  172.          */
  173.         public void addPage(final IAreaShape pPage) {
  174.  
  175.                 this.mPages.add(pPage);
  176.                 this.attachChild(pPage);
  177.  
  178.                 this.updatePages();
  179.  
  180.         }
  181.  
  182.         /**
  183.          * Adds new page and reorders pages trying to set given number for newly
  184.          * added page. If number > pages count - adds new page to the right end of
  185.          * the scroll scene. If number <= 0 - adds new page to the left end of the
  186.          * scroll scene.
  187.          */
  188.         public void addPage(final IAreaShape pPage, final int pPageNumber) {
  189.  
  190.                 this.mPages.add(pPageNumber, pPage);
  191.                 this.attachChild(pPage);
  192.  
  193.                 this.updatePages();
  194.         }
  195.  
  196.         /**
  197.          * Removes page if it's one of scroll scene pages (not children) Does
  198.          * nothing if page not found.
  199.          */
  200.         public void removePage(final IAreaShape pPage) {
  201.                 this.unregisterTouchArea(pPage);
  202.                 this.detachChild(pPage);
  203.                 this.mPages.remove(pPage);
  204.  
  205.                 updatePages();
  206.  
  207.                 this.mCurrentPage = Math.min(this.mCurrentPage, mPages.size() - 1);
  208.                 this.moveToPage(this.mCurrentPage);
  209.  
  210.         }
  211.  
  212.         /**
  213.          * Removes page with given number. Does nothing if there's no page for such
  214.          * number.
  215.          */
  216.         void removePageWithNumber(final IAreaShape pPage, final int pPageNumber) {
  217.                 if (pPageNumber < this.mPages.size())
  218.                         this.removePage(this.mPages.get(pPageNumber));
  219.         }
  220.  
  221.         /**
  222.          * Moves the scene to the page with the given number and, if it has a
  223.          * OnScrollScenePageListener registered, calls
  224.          * {@link #onMoveToPageStarted()} when started and
  225.          * {@link #onMoveToPageFinished()} method when finished.
  226.          *
  227.          * @param pageNumber
  228.          * @throws IndexOutOfBoundsException
  229.          *             if number >= totalPages or < 0.
  230.          */
  231.         private void moveToPage(final int pageNumber) {
  232.  
  233.                 if (pageNumber >= this.mPages.size()) {
  234.                         throw new IndexOutOfBoundsException("moveToPage: " + pageNumber
  235.                                         + " - wrong page number, out of bounds.");
  236.                 }
  237.                
  238.                 this.mCurrentPage = pageNumber;
  239.  
  240.                 final float toX = positionForPageWithNumber(pageNumber);
  241.  
  242.                 if(this.mMoveXModifierListener == null) {
  243.                         this.mMoveXModifierListener = new IModifierListener<IEntity>() {
  244.                                 @Override
  245.                                 public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
  246.                                         final IOnScrollScenePageListener scrollScenePageListener = ScrollScene.this.mOnScrollScenePageListener;
  247.                                         if(scrollScenePageListener != null) {
  248.                                                 scrollScenePageListener.onMoveToPageStarted(ScrollScene.this.mCurrentPage);
  249.                                         }
  250.                                 }
  251.  
  252.                                 @Override
  253.                                 public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
  254.                                         final IOnScrollScenePageListener scrollScenePageListener = ScrollScene.this.mOnScrollScenePageListener;
  255.                                         if(scrollScenePageListener != null) {
  256.                                                 scrollScenePageListener.onMoveToPageFinished(ScrollScene.this.mCurrentPage);
  257.                                         }
  258.                                 }
  259.                         };
  260.                 }
  261.                
  262.                 if(this.mEaseFunctionDirty) {  
  263.                         if(this.mMoveXModifier != null) {
  264.                                 this.mMoveXModifier.removeModifierListener(this.mMoveXModifierListener);
  265.                                 this.mMoveXModifier = null;
  266.                         }
  267.                         this.mEaseFunctionDirty = false;
  268.                 }
  269.                
  270.                 if(this.mMoveXModifier == null) {                      
  271.                         this.mMoveXModifier = new MoveXModifier(SLIDE_DURATION_DEFAULT, this.getX(), toX, this.mEaseFunction);
  272.                         this.mMoveXModifier.addModifierListener(this.mMoveXModifierListener);
  273.                 } else {
  274.                         this.mMoveXModifier.reset(SLIDE_DURATION_DEFAULT, this.getX(), toX);
  275.                 }
  276.                
  277.                 this.clearEntityModifiers();
  278.                 this.registerEntityModifier(this.mMoveXModifier);
  279.         }
  280.  
  281.         /**
  282.          * Immediately moves the scene to the given page number.
  283.          *
  284.          * @param pageNumber
  285.          * @throws IndexOutOfBoundsException
  286.          *             if number >= totalPages or < 0.
  287.          */
  288.         public void selectPage(int pageNumber) {
  289.                 if (pageNumber >= this.mPages.size()) {
  290.                         throw new IndexOutOfBoundsException("selectPage: " + pageNumber
  291.                                         + " - wrong page number, out of bounds.");
  292.                 }
  293.  
  294.                 this.setX(positionForPageWithNumber(pageNumber));
  295.                 this.mCurrentPage = pageNumber;
  296.         }
  297.  
  298.         /**
  299.          * @param pageNumber
  300.          * @return the position of the page with the given number
  301.          */
  302.         public float positionForPageWithNumber(int pageNumber) {
  303.                 return pageNumber * (this.mPageWidth - this.mOffset) * -1f;
  304.         }
  305.  
  306.         /**
  307.          *
  308.          * @param pPosition
  309.          *            meaning the X value
  310.          * @return the number of the page at the given position
  311.          */
  312.         public int pageNumberForPosition(float pPosition) {
  313.                 float pageFloat = -pPosition / (this.mPageWidth - this.mOffset);
  314.                 int pageNumber = (int) FloatMath.ceil(pageFloat);
  315.  
  316.                 if ((float) pageNumber - pageFloat >= 0.5f)
  317.                         pageNumber--;
  318.  
  319.                 pageNumber = Math.max(0, pageNumber);
  320.                 pageNumber = Math.min(this.mPages.size() - 1, pageNumber);
  321.  
  322.                 return pageNumber;
  323.         }
  324.  
  325.         /**
  326.          * Moves to the next page. Does nothing if the current page is the last page
  327.          */
  328.         public void moveToNextPage() {
  329.                 final int pageNo = this.mPages.size();
  330.  
  331.                 if (this.mCurrentPage + 1 < pageNo)
  332.                         this.moveToPage(this.mCurrentPage + 1);
  333.         }
  334.  
  335.         /**
  336.          * Moves to the previous page. Does nothing if the current page is the first
  337.          * page
  338.          */
  339.         public void moveToPreviousPage() {
  340.                 if (this.mCurrentPage > 0)
  341.                         this.moveToPage(this.mCurrentPage - 1);
  342.         }
  343.  
  344.         /**
  345.          * Removes all Pages from the scene
  346.          */
  347.         public void clearPages() {
  348.                 for (int i = this.mPages.size() - 1; i >= 0; i--) {
  349.                         final IAreaShape page = this.mPages.remove(i);
  350.                         this.detachChild(page);
  351.                         this.unregisterTouchArea(page);
  352.                 }
  353.         }
  354.  
  355.         public static interface IOnScrollScenePageListener {
  356.                 // ===========================================================
  357.                 // Constants
  358.                 // ===========================================================
  359.  
  360.                 // ===========================================================
  361.                 // Methods
  362.                 // ===========================================================
  363.                 public void onMoveToPageStarted(final int pPageNumber);
  364.  
  365.                 public void onMoveToPageFinished(final int pPageNumber);
  366.         }
  367.  
  368.         private enum ScrollState {
  369.                 IDLE, SLIDING;
  370.         }
  371.  
  372. }
  373.  
Parsed in 0.032 seconds, using GeSHi 1.0.8.4


Thanks again korn3l for the effort!

Regards
sjkm
sjkm
 
Posts: 25
Joined: Sun May 20, 2012 12:43 pm
Location: Zurich, Switzerland

Re: [Feature] ScrollScene

Postby korn3l » Fri Dec 07, 2012 2:28 pm

Thanks for the input sjkm !
I modified your code a bit so it won't clear/register the modifier every time moveToPage is called. It just resets the modifier now.
I pushed the changes so you can see for yourself and maybe improve it even more :P
korn3l
 
Posts: 214
Joined: Thu Apr 12, 2012 4:20 pm
Location: Iasi, Romania

Re: [Feature] ScrollScene

Postby kirman » Wed Feb 06, 2013 3:23 am

korn3l wrote:Thanks for the input sjkm !
I modified your code a bit so it won't clear/register the modifier every time moveToPage is called. It just resets the modifier now.
I pushed the changes so you can see for yourself and maybe improve it even more :P


Hi, korn3l.

I make sparated Scene from my MainActivity class and make method setScrollScene(ScrollScene scrollScene) in MainActivity class.

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. public ScrollScene setScrollScene(ScrollScene scrollScene){
  2.                 this.mScrollScene = scrollScene;
  3.                 getEngine().setScene(mScrollScene);
  4.                 return mScrollScene;
  5.         }
Parsed in 0.012 seconds, using GeSHi 1.0.8.4


Then I call this method from one of Scene with a button.

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. Mainctivity.setScrollScene(new SelectedScene());
Parsed in 0.013 seconds, using GeSHi 1.0.8.4


where SelectedScene() class is extends ScrollScene implements IOnScrollScenePageListener. But, when I addPage on this scene like the following:

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. Rectangle rect1 = new Rectangle(0, 0, 0, 0, activity.getVertexBufferObjectManager());
  2. Rectangle rect2 = new Rectangle(0, 0, MainActivity.CAMERA_WIDTH, MainActivity.CAMERA_HEIGHT, activity.getVertexBufferObjectManager());
  3. Rectangle rect3 = new Rectangle(0, 0, MainActivity.CAMERA_WIDTH, MainActivity.CAMERA_HEIGHT, activity.getVertexBufferObjectManager());
  4.  
  5. this.addPage(rect1);
  6. this.addPage(rect2);
  7. this.addPage(rect3);
Parsed in 0.013 seconds, using GeSHi 1.0.8.4


The page is collected to be one page only, So I can scroll it to the next or previous page. I assume that this is caused by the pageWidth and pageHeight are not same with my CAMERA_WIDTH and CAMERA_HEIGHT. Then I see Log of getPageWidth and getPageHeight() are 0.0.

My question, how to set the page in order not collected to one page on this scene? Or, How to set the pageWidth and pageHeight same as our CAMERA_WIDTH and CAMERA_HEIGHT?
kirman
 
Posts: 14
Joined: Wed Oct 24, 2012 11:32 am

Re: [Feature] ScrollScene

Postby kirman » Wed Feb 06, 2013 9:39 am

kirman wrote:The page is collected to be one page only, So I can scroll it to the next or previous page. I assume that this is caused by the pageWidth and pageHeight are not same with my CAMERA_WIDTH and CAMERA_HEIGHT. Then I see Log of getPageWidth and getPageHeight() are 0.0.


Hi, I've found the answer. :D
I set the setPageWidth and setPageHeight same as with my CAMERA_WIDTH and CAMERA_HEIGHT. So this is not collected to on one page again :D

Thanks alot
kirman
 
Posts: 14
Joined: Wed Oct 24, 2012 11:32 am

Re: [Feature] ScrollScene

Postby korn3l » Wed Feb 06, 2013 10:40 am

kirman wrote:
kirman wrote:The page is collected to be one page only, So I can scroll it to the next or previous page. I assume that this is caused by the pageWidth and pageHeight are not same with my CAMERA_WIDTH and CAMERA_HEIGHT. Then I see Log of getPageWidth and getPageHeight() are 0.0.


Hi, I've found the answer. :D
I set the setPageWidth and setPageHeight same as with my CAMERA_WIDTH and CAMERA_HEIGHT. So this is not collected to on one page again :D

Thanks alot


Glad you figured it out ;)
korn3l
 
Posts: 214
Joined: Thu Apr 12, 2012 4:20 pm
Location: Iasi, Romania

Re: [Feature] ScrollScene

Postby zlibd790 » Tue Feb 12, 2013 5:32 am

Hmm, I can't seem to figure out how to add a single background image and just have the individual sprites scroll, like in the video. If I attach my background sprite to the scene directly, it just hooks it up to the first page and when I scroll to the next, I get a black screen with the sprite for that page attached.

I tried to attach the background to an HUD, but that seems to put it on top of everything else, so I can't scroll.

Sorry if this seems like a stupid question, but I have been at this a while, and nothing seems to be working for me.

EDIT: Just figured it out. I needed to attach my background sprite as a BackgroundSprite...who would have thought, lol.
zlibd790
 
Posts: 83
Joined: Fri Nov 09, 2012 11:16 pm

Re: [Feature] ScrollScene

Postby tnbao91 » Sat Mar 30, 2013 7:02 pm

Hi guys. Please help me. I have a scene extends scroll scene. Scene have 1 background, 4 pages and 2 sprites. Background always fixed when I scroll pages but sprites aren't. How to handle this issue?
tnbao91
 
Posts: 2
Joined: Sun Mar 03, 2013 4:09 pm

Re: [Feature] ScrollScene

Postby sjkm » Sat Mar 30, 2013 8:12 pm

Hey tnbao91,
what are you expecting us to do?
This isn't an issue.
Your question holds already the answer you're searching for....
A background in andengine is normally fixed, that's why its called a background.
I suggest you to have a deeper look into the code.
sjkm
 
Posts: 25
Joined: Sun May 20, 2012 12:43 pm
Location: Zurich, Switzerland

Re: [Feature] ScrollScene

Postby zastotako » Sun Jun 09, 2013 11:57 pm

Hi guys, thanks for this awesome feature.

One question, i'm trying to figure out with no success, for example if i have sprite with onareatouched inside page and when i swipe across it, swipe is not registered only click on that sprite is.It's like touch event is caught by sprite and not by scene. So basically you can't swipe if across the sprite. I've also tried with SpriteButton with no success.

Thanks in advance.

EDIT:

As soon as i made this post, i figured it out. Trick is to check if mState is IDLE in onareatouch on Sprite. :)
zastotako
 
Posts: 3
Joined: Sun May 26, 2013 9:44 pm

Re: [Feature] ScrollScene

Postby tgioihan » Tue Jul 02, 2013 6:07 pm

Many thanks to you.
Its helps me alots
tgioihan
 
Posts: 10
Joined: Mon Jun 25, 2012 6:11 pm

PreviousNext

Return to Features

Who is online

Users browsing this forum: No registered users and 8 guests