Pages

28 Feb 2013

Create simple infinite carousel in Android with ViewPager

At first I thought it's easy, someone must have implemented it already, but after searching for it on Google, I got no luck, nobody has done and shared it on the Net, so I decided to make it myself.

I read some posts like:

So I implemented it as you can see in below video:

 


The main idea is:
  • Use ViewPager as the main view with pages are Fragment, then use ViewPager.setPageMargin(int) to set margin as a negative number, so the next and previous pages of the selected page will be showed up.
  • Override the layout which is the root view of Fragments to create scale animation.
  • Make a little hack in FragmentPagerAdapter so we can fling infinitely to both directions of the ViewPager.

For more information, you guys can get the source code here: https://github.com/mrleolink/SimpleInfiniteCarousel. I tried to comment as much as possible in source code, so I hope you understand my idea.

By the way, I don't think it's the best solution to implement the carousel pattern, so if you have any idea or there's something you don't understand about what I code, just drop me a line :)

87 comments:

  1. How can I change the "black button" to image?
    I want to add images each position of button.

    ReplyDelete
  2. Each page of the ViewPager is a fragment, and "black image" is a button in the layout of each fragment, you can change the Button in this:

    https://github.com/mrleolink/SimpleInfiniteCarousel/blob/master/res/layout/mf.xml

    to ImageView or customize the layout as you want.

    ReplyDelete
    Replies
    1. Thank you for your reply.
      As you replied, I changed it to imageview.
      But, there are 4 positions that I want to put image for each position.
      But I don't know how to change one by one.
      If I add image on mf.xml, then all positions of the image are same.
      I want..different image for each position..any suggestion please?

      Delete
    2. Oh, you have to look at:

      https://github.com/mrleolink/SimpleInfiniteCarousel/blob/master/src/net/leolink/android/simpleinfinitecarousel/MyFragment.java

      For example, if you change TextView in this file:

      https://github.com/mrleolink/SimpleInfiniteCarousel/blob/master/res/layout/mf.xml

      to ImageView, then in MyFragment.java, you have to use "l.getViewById(your_image_view_id)" to get you ImageView and set image for it as your need. Notice that you can pass your parameters through Bundle as I push an integer "pos" into a Bundle in function "newInstance" then get it back in function "onCreateView".

      Delete
    3. Thank you for your reply again! really appreciate it!

      And..I tried what you gave an advice..but..it's not working..I am a very beginner that..
      I took one week to modify it....
      I don't understand..why its not working.
      If I use "l.getViewById(your_image_view_id)" then, it also appears one photo only..
      but what i want is showing four different images for each position.
      If you have some time, could you please modify it a bit for me?..
      What I need is..as I told you before...just put four different photos instead of black button..
      you can add images whatever you want..

      Delete
    4. and really appreciate that you replied me really fast! :)

      Delete
  3. This comment has been removed by the author.

    ReplyDelete
  4. i made it!! thank you for your help and advice.
    really good example!! :)

    ReplyDelete
    Replies
    1. May I ask how did you make it work?

      Delete
  5. One more question!!!
    I cannot use Toast message in Myoffers_Fragment.java class

    Toast t = Toast.makeText(this, "System is active.", Toast.LENGTH_LONG).show();

    "makeText" make error...any suggestion??

    ReplyDelete
    Replies
    1. I'm glad you got it work.
      About Toast, you can't use "this" as context for Toast inside a fragment, you have to use:

      Toast t = Toast.makeText(this.getActivity(), "System is active.", Toast.LENGTH_LONG).show();

      Delete
    2. it's not working. error says "cannot convert from void to toast".
      What I want is, showing toast message every time I pass the product with carousel view.
      When I slide pos = 0 to pos =1, toast message "Product 1 is selected". like this..
      But, in fragment class, i cant use toast ...i don't know why...could you give me some advice please?

      Delete
    3. Let me see the snippet that you used Toast or better show me your whole file Myoffers_Fragment.java.
      p/s: don't paste here, paste to www.pastebin.com then send me the link, it's easy for me to read

      Delete
    4. now working!! haha...sorry..it's weird..it was not working..but now..is working.
      Thanks!! Do you know how to code to open "Websocket" on client side(android) ??
      As you see, I am a just beginner...so..need some help ! :p

      Delete
    5. Are you asking me to help you complete your project?
      LOL, any pro was once a beginner, try to resolve it yourself, man. Google is your best friend, he is always there ;)
      And I'm not a pro anyway, I only can help you within this tutorial's scope.
      Good luck ;)

      Delete
  6. Hello!!! LeoLink!! I have a question with your example.
    I put four products(from 0 to 3) and if I select product0, the toast message should pop up like
    "Product 0 is selected." But, i don't know why, the "pos" appears incorrectly.
    my code is..

    ImageView product_photo = (ImageView) l.findViewById(R.id.myoffer_image);

    switch(pos){
    case 0:
    product_photo.setImageResource(R.drawable.myoffers_0);
    Toast.makeText(this.getActivity(), "Product" + pos + " is selected.", Toast.LENGTH_LONG).show();
    break;

    Toast message appears "product 3 is selected " for product 0, and
    "product 2 is selected " for product 1, ...like this.

    Can you please give me some advice?

    ReplyDelete
  7. Hello again.!! I have a question!!
    I want to make a method if an image is selected.
    So, I changed to ImageButton.
    So, If product 0's image is clicked..an method should work..like this..
    any suggestion please?

    ReplyDelete
    Replies
    1. check if you placed break point in every case.

      Delete
  8. This comment has been removed by the author.

    ReplyDelete
  9. When i use ViewPage Indicator .It showing PAGES*LOOPS number of page Indicator ..This is because of the Loop Hack that you used .Is there any other way to generate Loops in viewpager.
    Thanks

    ReplyDelete
  10. Hi LeoLink, I followed the example you provided, very interesting and instructive. I tried to place an action button that when pressed to send me to another activity, but I can not run, I'm using the position variable to send to a particular activity. Could you guide me briefly about it? Thanks for your efforts and your time.

    ReplyDelete
    Replies
    1. Where did you place your button, I think it should be placed in "mf.xml" (notice that each page of the ViewPager is a fragment!). And to make the button bahave as you expected, just create an intent and start a new activity when the button is pressed.
      Why couldnt your code run? Which exception was thrown?

      Delete
  11. Thank you for this example.This is very useful.

    ReplyDelete
  12. can i select one of item it not first page when i click i i want it become to first page

    ReplyDelete
    Replies
    1. Sorry, what do you mean?
      Btw, I'm Vietnamese, so you just explain in Vietnamese if it's easier for you.

      Delete
    2. mình muốn hỏi nếu mình nhấn vào một page bất kỳ và nó chuyển sang thành page giữa thì làm thế nào? khi nó đang là page giữa mình muốn hiển thì một list icon ở bên dưới nữa :D

      Delete
    3. bạn có thể cho mình yahoo hoặc skype của bạn chứ ?

      Delete
    4. ok cảm ơn bạn mình xong rồi nhé ^^

      Delete
    5. uh, sorry nhé, dạo này bận quá ko có time check blog :D

      Delete
  13. Hi LeoLink,
    Thank your for your post, It's really useful.
    I used it but my question is the value of page margin.
    if i set it a minus value( for example -200) then in various resolutions it appears differently.
    How can i set page margin to a right value?

    ReplyDelete
    Replies
    1. Hi there, sorry for late reply!
      And about your question, it should be like that because 200 in this case means 200px, it's a fixed value, but resolutions vary ?! That's why!
      So the solution is you have to specify different margin values for different screen size. But how to do it exactly, you have to read this:

      http://developer.android.com/guide/practices/screens_support.html

      But in this case I GUESS you will have to deal with xml files, create same xml files with same name in different folders like res/values-xhdpi/dimens.xml, res/values-hdpi/dimens.xml... and specify same keys but with different values in those files...

      Hope that helps ;)

      Delete
  14. Hi Leolink,

    Thank for this fabuluos code,
    I need to know how to make the image in the center to be in TOP always.

    ReplyDelete
    Replies
    1. just move your ViewPager to top by including this property "layout_gravity=top" to your ViewPager

      Delete
  15. Hi great tutorial you have here..Im trying to add more than one carousel in the same page.is it possible?

    ReplyDelete
    Replies
    1. Yes, it is. Just add more ViewPagers then implement the same.

      Delete
  16. hi, greate sample and clear tutorial. But i have a question. How can i set the first selection item in the left ? thanks

    ReplyDelete
    Replies
    1. In https://github.com/mrleolink/SimpleInfiniteCarousel/blob/master/src/net/leolink/android/simpleinfinitecarousel/MainActivity.java, change:

      public final static int FIRST_PAGE = PAGES * LOOPS / 2;

      to

      public final static int FIRST_PAGE = 0;

      Delete
    2. Hi, thanks for fast reply. But i want to ask again after i scrolled it the focus position is stay in the middle. Can i change it to the left position ? i already change the pages to 6 and loops to 1000

      Delete
    3. what do you mean by "left position", you mean "left align"?

      Delete
    4. yeah, left position is the selected item is on the left not in the middle. how can i make it ?

      Delete
    5. hmm, it's not simple as I can explain in a few lines of code, but basically, you need to remove this line:

      pager.setPageMargin(-200);

      Then customize each fragment's layout (https://github.com/mrleolink/SimpleInfiniteCarousel/blob/master/res/layout/mf.xml) as left aligned.

      Delete
    6. i already find the solution :
      - add + 2 at FIRST_PAGE variable in MainActivity
      - add -2 in if (position == MainActivity.FIRST_PAGE -2 ) in getItem method in MyPagerAdapter

      Delete
    7. Cool, Im glad you figured out it yourself, maybe I have misunderstood meaning :D

      Delete
  17. This comment has been removed by the author.

    ReplyDelete
  18. Hi LeoLink,

    Firstly, I am thankful to you, great work. I am not getting the left side and right side images, i am getting only one center image.

    ReplyDelete
  19. Hello,

    how can I add picture on pages?

    Best regards,
    Matej Klemen

    ReplyDelete
  20. Hi,
    Thanks so much, you saved my time.
    I have only one question.
    Have used pager.setPageMargin(-560); for 720 x 1280 in landscape orientation

    but its giving me issues as this harcoded -560 is not working properly in other resolutions.

    Could you please tell what would be the generic solution for this.

    Waiting for your valuable response

    ReplyDelete
    Replies
    1. 560 in your pager.setPageMargin(-560); means 560PX, maybe you should use DP instead of PX, or calculate the page margin according to screen size?

      Delete
  21. pager.setPageMargin(takes only value in PX) // so not point giving value in DP.

    Not sure how shd I calculate page margin according to screen size.

    ReplyDelete
    Replies
    1. dp -> px:
      public int dpToPx(int dp) {
      DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
      int px = Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
      return px;
      }


      calculate screen size:
      DisplayMetrics metrics=new DisplayMetrics();
      getWindowManager().getDefaultDisplay().getMetrics(metrics);
      float height=metrics.heightPixels/metrics.xdpi;
      float width=metrics.widthPixels/metrics.ydpi;

      Delete
  22. Thanks for you work LeoLink, it works quite well.

    Here's some code that'll help get the spaces between views consistent on all screen sizes and resolutions

    DisplayMetrics dM = getResources().getDisplayMetrics();
    int widthOfScreen = dM.widthPixels;
    int widthOfView = 150; //in DP
    int spaceBetweenViews = 20; // in DP
    float offset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, widthOfView+spaceBetweenViews, dM);
    pager.setPageMargin((int) (-widthOfScreen+offset));

    ReplyDelete
    Replies
    1. spacing changes with the device resolution :(

      Delete
    2. this code works perfectly, now space between pages are constant in all resolution.

      Delete
    3. As @And Droid mentioned spacing changes with screen resolution with code suggested by kmg.

      I could able to figure out general solution which fits all screen resolution. Here it is..

      DisplayMetrics dM = getResources().getDisplayMetrics();
      int widthOfScreen = dM.widthPixels;
      int widthOfView = widthOfScreen/3; //for displaying maximum of three buttons, you can change this if you want more buttons

      int spaceBetweenViews = 20; // in DP

      float offset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, widthOfView+spaceBetweenViews, dM);

      //Calculate button width in mf.xml, which is 190 dp in pixels for the running device..

      float scale = getBaseContext().getResources().getDisplayMetrics().density;
      int px= (int) (190 * scale + 0.5f);

      float pageMargin=widthOfScreen-offset;

      //if pageMargin is less than button size make the page margin atleast to button size so that the previous,current and next buttons are displayed

      if(pageMargin<px){
      pageMargin=px;
      }

      pager.setPageMargin((int) (-pageMargin));

      Delete
  23. Hi,
    Thnks for code.
    I have issues in your demo with fragment click,while I'd click on current item its Okay but in case of prev or next item click it will get current item click.

    ReplyDelete
    Replies
    1. you mean when Fragment1 is being shown, and if you click on the portion of Fragment0 or Fragment2, only Fragment1 get onClick event?

      Delete
    2. Hi,Thanks for reply
      I am sry I mean when I click on both corners of fragment1 it will sometime get the click events of fragment0 & fragment2.

      Now I change the Widht of fragments and also change the pagemargins as per my requirements
      So is it the reason?

      Delete
    3. um, so I'm not sure then, but I'm curious why do you want to get onClick event when click on Fragment0 & Fragment2 while Fragment1 is being selected?
      Because of when you click on Fragment0(or Fragment2) while Fragment1 is being selected, the you will get Fragment0(or Fragment2) selected, so you may use ViewPager's events instead of onClick event, just my thought..

      Delete
  24. Hi, can you please make a sample of your carousel with sample images instead? Thank you so much, I really appreciate it.

    ReplyDelete
    Replies
    1. You just need to add an ImageView to the layout file of the Fragment, then set image for it based on position of the containing Fragment.

      Delete
  25. Hi, This tutorial helped me a lot and i have successfully implemented this on one of my applications. Every thing goes well but i have one small question on this.

    Q: Suppose when the user tap/click on any item, i have changed the selected item background with new image. But when i scroll the all items the selected item image is gone. Can you please tell me, is there any way to hold the selected item background as long as scroll does?

    ReplyDelete
    Replies
    1. to achieve that, you have to change your adapter's data whenever an item gets clicked, then use adapter's notifyDataSetChanged() to apply the change on your ViewPager, dont change the clicked item's background directly when it's clicked.

      Delete
    2. Hey, Thank you for your quick reply.

      Delete
  26. This comment has been removed by the author.

    ReplyDelete
  27. Hi, Thanks for this awesome Post! But I have a question. I would like to keep previous and next page item behind of current page selected. Currently I can do only the previous. Can you help me on that?

    ReplyDelete
    Replies
    1. Sorry I dont get what you mean, how about some pictures to express your idea?

      Delete
    2. My question is to have pagers like that:
      http://i.stack.imgur.com/k9R4w.png

      Delete
    3. ohh, it isn't simple as it looks, you might try setPageMargin as a very big native value like setPageMargin(-300), but I'm not sure...
      personally, I think you will have to customize pretty much to achieve it, it looks like a cover flow, I tried to implement it by ViewPager before but it couldn't make it, but if you don't mind Gallery class which has become deprecated since API 16, you can give it a try.

      Delete
  28. Hi! Thank u so much for this gr8 tutorial. But I am facing some problems. I have used your viewpager in my fragment. If I go in my same fragment in 1st attempt carousel looks properly, but if I go in my another fragment and go back again on same fragment then carousel images are not shown and one more thing I observed in this scenario is after scrolling sometimes images will start showing. I tried hard to fix this issue but not able to resolved yet. Can you please give me any guidance on the same issue.

    ReplyDelete
    Replies
    1. maybe you should try "setWillNotDraw(false)" in MyLinearLayout's constructors, or call MyLinearLayout's "invalidate()" in "onPageSelected" of the adapter.

      Delete
    2. Thank u so much for such a quick reply. I tried both ways, but unfortunately not able to solve my issue. Is my problem coming because i have added carousel fragment within my fragment & during fragment switching time carousel images are not displaying, as i have multiple fragments in my project? Also i didn't understand why this images starts looking after few scrolling done.

      Delete
    3. My another observation is viewpager layout looks properly in my scenario. But linear layout which holds carousel view is not at all displaying. I checked this by adding bg color in my both viewpager & carousel linear layout. Also first image which i seen after scrolling some time is changed as per setOffscreenPageLimit value accordingly. Kindly help if u got any idea.

      Delete
  29. Also call is not going in onDraw(Canvas canvas) method of custom layout.

    ReplyDelete
  30. Is it possible to implement arch shaped carousel using this method

    ReplyDelete
  31. Hello LeoLink, and very thanks for your tutorial. I would like to implement a function that allows change a variable (x) depending on the biggest image.
    For example: If image 1 is the biggest, x=34 and if image 2 is the biggest, x=89.
    How could I do it?

    ReplyDelete
  32. Hello LeoLink, I've just tried your tutorial. But It just shows only one current page. The previous and the next page don't show up. Could you fix it? I don't change anything in your sample. I tested on Asus and LG phone.

    ReplyDelete
  33. hi, this is not carousel effect at all because in carousel visible view comes always top and other left and right views are always backside of the main visible view.

    you can check this link for iOS and then you can understand my point of view that what i am saying about this tutorial.
    http://www.theappguruz.com/tutorial/how-to-use-icarousel-view-controller-in-ios/

    thanks to take note about this and if you find right answer please share with me because i am trying to develop open source library about viewpager animation layout in different ways and this is most important for me, so please share with me.

    thanks
    rahul mandaliya
    (SkyPe: rahulmandaliya)

    ReplyDelete
  34. thank you..i will study ..i was stuck on this .

    ReplyDelete
  35. Hey, I know this is really old, but how can I achieve that the active (middle) fragment is in front of the others?

    Thanks a lot!

    ReplyDelete
  36. Hi! I have a problem, I'm trying using your code but my aplication does not show the left and right element I onlycan see the center element, what's happening?

    ReplyDelete
  37. Hey! For all of you having trouble with the center element not being the top most one: You can fix that by invoking View#bringToFront() on the main view within the current fragment. I improved this a bit by invoking bringToFront on the current fragment's view within onPageScrolled whenever positionOffset is < 0.5f and on the next fragment's view whenever positionOffset >= 0.5f.

    For those not seeing the elements to the left and the right: Be sure that your child fragments don't occupy the whole screen width, and if they do, that the area in which the side elements should be visible has a transparent background.

    ReplyDelete
    Replies
    1. Thanks a lot, I tried bringToFront() before but couldn't get it to work. But doing like you suggested works!

      Delete
    2. This comment has been removed by the author.

      Delete
  38. how i can implement this in titanium,appcelerator studio for android????

    ReplyDelete
  39. Hi is possible to implemented a dynamic carousel so that i can add item and delete?
    Thx :)

    ReplyDelete
  40. Hi Mr.Leo Link
    I use this example to let the viewpager be aother viewpager's tab,
    it work but i have some problem,i need to let current view's text to change color, but i don't no how to do,please tell me how to do it.

    ReplyDelete