Hi to all Android developers out there!
Did you ever want to integrate a feature into your app that allows users to switch between multiple views in the same way as one can switch between multiple screens on the Android home screen? There are some classes in the official SDK, namely Gallery, ViewFlipper and ViewAnimator, which one could use, but none of them works the same way the Launcher does.
Because I want to use this functionality in a new app, I stripped down the Launcher's source code and created a class called RealViewSwitcher - which I open to the public again under the same license conditions as the SDK. Please download it and tell me what you think - I appreciate all ideas and bugfixes. :-)
The RealViewSwitcher class itself is available here:
http://marcreichelt.de/pub/realviewswitcher/RealViewSwitcher.java
A really simple example of how to use it can be found here:
http://marcreichelt.de/pub/realviewswitcher/realviewswitcher_example.zip
I hope this supports you in building your own Android applications - keep up the great work, and please drop me a mail if you like it. :-)
Marc
This comment has been removed by the author.
ReplyDeletePeople on SO are looking for this kind of functionality:
ReplyDeletehttp://stackoverflow.com/questions/4296175/android-nytimes-swipe-animation-gesture
how can you use it in a layout xml file?
ReplyDelete@ferdy182:
ReplyDeleteJust add it as the root layout (or a sub layout) like a normal layout view, and then add your views to it - like this:
<de.marcreichelt.android.RealViewSwitcher
android:layout_width="fill_parent"
android:layout_height="fill_parent"
... />
<!-- your sub views go here, each view is one "screen" - you might also want to add layouts here -->
</de.marcreichelt.android.RealViewSwitcher>
Thanks for doing this! I've made a few modifications and posted my version to github: https://github.com/ysamlan/horizontalpager
ReplyDeleteThanks!
ReplyDeleteThanks a lot Marc Reichelt .
ReplyDeleteWorks great! However i came across a situation where i wanted to be able to scroll through the multiple pages infinitely. In other words, when on the first page, be able to flip previous to the last page. Also when on the last page, be able to flip next to the first page. Then i came across a situation where i did not know how many pages i wanted to flip through. Ie: I dynamically generate the number of pages and be able to flip through. Here is my solution to both of these problems:
ReplyDeleteI'm not sure if its the 100% proper way to do this but this is what i did:
I knew i needed 5 pages for one use of this horizontal pager. What i did was put 5 layouts wrapped in the HorizontalPager tag (which can include images, buttons, etc) in the xml like this: [0, 1, 2, 3, 4], but this only allows scrolling from one end to the other, so i did this in the xml:
[4, 0, 1, 2, 3, 4, 0]
Then in the onScreenSwitched i say:
if (screen == (numPages+1))
{
realViewSwitcher.setCurrentScreen(1, false);
}
else if (screen == 0)
{
realViewSwitcher.setCurrentScreen(numPages, false);
}
Now it allows for infinitely scrolling through the pages. I also came across a situation where i did not know how many pages i wanted to be able to flip through, so I did something like this:
The xml had 3 layouts wrapped in the HorizontalPager like this: [prev, current, next]. Again, each of these layouts included multiple buttons, images, textViews, etc.
When the user is on the first page (page 0 of N), you set the current screen to 0. Similarly when on the last page (page N) the current screen is set to 2 (the next screen position). Any page in between 0 and N (not including 0 and N) will be set to 1 (the current screen position). When the user swipes or flicks to the next or previous page an update is triggered which will set the current screen back to 1 (current) and update the previous and next values.
Hope this helps others trying to mimic this behavior.
I was having problems with this when the orientation changed. In your example it snaps to the first screen when orientation changes, but i wanted it to snap to the current screen you are on. So what i did was took the snapToScreen code and rather than grabbing the view's getWidth() i substituted the screens width in its place and called the startScroll with duration zero. here is the code to accomplish this:
ReplyDeletepublic void orientationSnapToScreen(Context ctx)
{
Display display = ((WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int displayWidth = display.getWidth();
mNextScreen = Math.max(0, Math.min(getCurrentScreen(), getChildCount() - 1));
final int newX = mNextScreen * displayWidth;
final int delta = newX - getScrollX();
mScroller.startScroll(getScrollX(), 0, delta, 0, 0);
}
and then you just call it like this in your code:
@Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
// realViewSwitcher is a HorizontalPager
realViewSwitcher.orientationSnapToScreen(this);
}
Hope this helps :)
David,
ReplyDeleteCould you please post some code showing the dynamic paging with the three views? I have a situation where I'd like to use the pager, but have potentially hundreds of images to show, and need to keep memory at bay.
@David: Wow, this is great! About your second solution what is happening when the orientation changes: The view switcher currently does not save its state. That yet has to be implemented. Furthermore, scrollable views are not yet possible in the solution I posted here. I have a working version of this at home. I will upload it to GitHub later and post the link here. Hopefully then the "orientation change" problem will be solved, too.
ReplyDelete@Anonymous: Dynamic paging is yet not implemented, and I would say it is not that easy. In fact, I implemented it specifically for - but also limited to - a certain project which will go into the Android Market the next days. IMHO it is not yet ready for productional use other than in my application. The best method would be to do something like the Android AdapterView (used by GridView, ListView etc.) does: Create views on demand, re-use views that are not visible and delete views if they are no longer needed. I guess this will not be trivial to implement - but when it's done, it would be a really nice contribution to existing Android widgets.
I will post an update soon - hang tight! :-D
Marc
Thanks Marc.
ReplyDeleteI would love to have the dynamic paging functionality. I'm banging my head on it right now by holding 3 variables and changing their imageUrls on scroll. But when I scroll back to screen1, I don't get a true snap (scroller scrolls thru the list of 3, even though I have animate set to false).
I'll think about the reusable view solution. If you feel especially generous in the next day or so, I would also love to see the code you used to get this working.
Thanks for a great switcher!
Hi @Anonymous,
ReplyDeletejust drop me a mail. My e-mail address can be found on: http://marcreichelt.de/
I will then send you some code that does what you primarily want. :-)
Marc
Email sent. Thanks!
ReplyDeletehey guys, i just finished putting a simple example on github that uses ImageViews and the Horizontal Pager. Again, I'm not 100% sure whats going on with memory in my example but please advise me if you know.
ReplyDeletehere's my demo:
https://github.com/aveyD/Dynamic-Paging-Demo
Thanks!
hi! if i am changing the device orientation, the view is always set to element 0. how can i stay at the current selected position?
ReplyDeletethx!
hi alex. Please see my comment here:
ReplyDeletehttp://marcreichelt.blogspot.com/2010/09/android-use-realviewswitcher-to-switch.html?showComment=1300730138393#c3142836062621886772
or see line 428 in:
https://github.com/aveyD/Dynamic-Paging-Demo/blob/master/src/com/HorizontalPager/dynamicPaging/HorizontalPager.java#L428
(orientationSnapToScreen)
and line 141 in:
https://github.com/aveyD/Dynamic-Paging-Demo/blob/master/src/com/HorizontalPager/dynamicPaging/Main.java#L141
(onConfigurationChanged)
Hope that helps :)
Hi, Marc,
ReplyDeleteIn my activity, I implemented OnDoubleTapListener. However, none of the OnDoubleTapListener methods were called. It seems that Realviewswitcher blocks those gestures. Do you have any idea to solve it ?
Marc,
ReplyDeleteI'm trying to convert your view switcher into a view switcher backed up with an adapter.
I've got quite far (although I don't re-use Views yet) but I have one odd thing going on. Switching through items in the middle of the array (not from/to the first or last item) will have a flickering effect where the item I just scrolled FROM is being flashed a few milliseconds.
Please, have a look at it if you want: https://github.com/pakerfeldt/ViewSwitcher
You should be able to clone it and run as-is to see the strange effect I was talking about.
Any ideas on how to solve that?
Thank you!
ReplyDeleteI created a basic adapter impl. It's nothing so special, but I believe it could be useful for someone.
ReplyDeletehttp://code.google.com/p/android-view-slider/
Hi Marc,
ReplyDeleteHow do I capture a moment when the screen changes?
I know there's a screen change listener, but where do I connect that listener to my code? I am a bit of a junior in android dev, but still know a bunch to understand.
Thanks in advance,
Gix
Can i use it with ListView inside? it doesnt work for me
ReplyDeleteThanks!
Hi all,
ReplyDeleteI finally have a version using an Adapter as data source finished. Its based on the work from Marc. Please feel free to check it out: https://github.com/pakerfeldt/android-viewflow
Thanks, its a great thing to have!!
ReplyDeleteI can't get this working. Can someone help please. Here's how i did added views:
ReplyDelete<2LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
> <2de.marcreichelt.android.RealViewSwitcher
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/real_view_switcher">
<2GridView android:id="@+id/gridView1" android:layout_width="match_parent" android:layout_height="match_parent">
<2GridView android:id="@+id/gridView2" android:layout_width="match_parent" android:layout_height="match_parent">
<2/de.marcreichelt.android.RealViewSwitcher>
<2/LinearLayout4>
In the .java class i did:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
RealViewSwitcher realViewSwitcher = (RealViewSwitcher)findViewById(R.id.real_view_switcher);
GridView gridview = (GridView) findViewById(R.id.gridView1);
GridView gridview2 = (GridView) findViewById(R.id.gridView2);
gridview.setAdapter(new ImageAdapter(this));
gridview2.setAdapter(new ImageAdapter(this))
The problem is, that i can't scroll to my second GridView, it simply doesn't do anything when i drag my main screen. Did I made some mistake here?
PS:added number 2 in the tags, because blogger didn't realy want's xml tags in the comments.
Thank You :)
ReplyDeleteHello. I´m currently using the RealViewSwitcher. It switches beetween 3 different layout. The problem is that one of them contains a listview and the another two contains gridviews. How can I make the program to swith between layouts by swapping my finger inside the listviews and gridviews area?
ReplyDeleteJoão, you could try the android-viewflow library out. I believe it should be possible, but I haven't tried it.
ReplyDeletehttps://github.com/pakerfeldt/android-viewflow
At the moment, you will need to backup the viewflow library with an Adapter containing your three elements. I guess we will implement a basic adapter thingy for those who have a static number of elements.
It does not work well in a scrollview. Can it be due to some gesture intereferences?
ReplyDeleteThanks anyway, apart from that it's great!
Jul
Hi Marc, I tried your example and sometime it gets stuck at the current and next screen like this (http://i1178.photobucket.com/albums/x370/BinhNguyen84/device-1.png). Do you know why it happens?
ReplyDeleteThanks,
Hi Marc, Its really a great sample,thanks for such a Image swipe sample.I am having one problem in that. The default image is appearing in the center of the Layout. How can i align it to top of the Layout? Please help me.
ReplyDeleteThanks Venkatesh Raghavan.
This is a very nice, 100% working clean code. Thank you.
ReplyDeleteFor those who want to use this class for a popup dialog, comment out the lines
throw new IllegalStateException("ViewSwitcher can only be used in EXACTLY mode.");
from the RealViewSwitcher.onMeasure.
its awesome. Sweet Simple but effective :)
ReplyDeleteThanks.
Great job, for sure!! ;)
ReplyDeletehi im getting ViewSwitcher can only be used in EXACTLY mode exeption. when i comment that in the code, it doesn't draw anything. just blank. im using this inside a map activity. please help
ReplyDeleteFirst post here.
ReplyDeleteI tried to use buttons or images that can be clicked, the scrolled didn't work, I'm just looking for that, any idea on how do this?
But the way, nice work!
The ViewPager in the Android compatibility library is a good fit for this, and it's more of a standard (since it's written by Google). It's an AdapterView, so it's easy on your memory, and it's fairly simple to implement.
ReplyDeleteIt looks great and in my requirement i have 5 viewes and i want to show 1.5 view in the screen every time up to last. Is it possible...
ReplyDeleteHey Man it awesome ,it really helped me a lot!!!!
ReplyDeleteThanks!!!!
Would be great if this worked with ListViews!
ReplyDeleteWorking great, thank you so much.
ReplyDeleteHi,
ReplyDeleteI m using the class and its working great!
Onething, i m not getting if i can get a long press event? if user click and hold his finger on the view under realViewSwitcher?
I m working on an existing application that uses the above class, so i may not be known of all functionality the class provides.. but the thing is i can get a click event, but not long press event for my view activity.
any help will be really appreciated.
Hi, for my perpose, i tune your logic a litle bit,
ReplyDeletethis what i do...
i add,
public static interface OnScreenMovementListener {
void onScreenMovement(int screen);
}
to listen movement on
case MotionEvent.ACTION_MOVE:
final int xDiff = (int) Math.abs(x - mLastMotionX);
boolean xMoved = xDiff > mTouchSlop;
if (xMoved) {
// Scroll if the user moved far enough along the X axis
mTouchState = TOUCH_STATE_SCROLLING;
}
if (mTouchState == TOUCH_STATE_SCROLLING) {
// Scroll to follow the motion event
final int deltaX = (int) (mLastMotionX - x);
mLastMotionX = x;
final int scrollX = getScrollX();
if (deltaX < 0) {
if (scrollX > 0) {
scrollBy(Math.max(-scrollX, deltaX), 0);
if (mOnScreenMovementListener != null)
mOnScreenMovementListener
.onScreenMovement(mCurrentScreen);
}
} else if (deltaX > 0) {
final int availableToScroll = getChildAt(
getChildCount() - 1).getRight()
- scrollX - getWidth();
if (availableToScroll > 0) {
scrollBy(Math.min(availableToScroll, deltaX), 0);
if (mOnScreenMovementListener != null)
mOnScreenMovementListener
.onScreenMovement(mCurrentScreen);
}
}
}
break;
and change your on onScreenSwitched location and tune it to
private void snapToScreen(int whichScreen) {
if (!mScroller.isFinished())
return;
whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
mNextScreen = whichScreen;
final int newX = whichScreen * getWidth();
final int delta = newX - getScrollX();
mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 2);
invalidate();
if (mOnScreenSwitchListener != null)
mOnScreenSwitchListener.onScreenSwitched(whichScreen,
mCurrentScreen);
}
to detect screen changing beter (from screen 1 to screen 2)
Hi,
ReplyDeleteYour example works like a charm, but I have a question. How can I put some buttons above the 'RealViewSwitcher'?
Thanks,
great job,
ReplyDeletethanks Marc Reichelt for RealViewSwitcher
and David for Dynamic Paging
I need a code or an idea...
I want to set two views (images) side by side in landscape format - two pages on the screen
like a magazine
Thanks,
and wish everybody a prosperous new year