secret
Last active

ViewPager container implementation that allows surrounding pages be visible (although not interactive). PagerContainer holds a ViewPager (in this case centered), has clipChildren() set to false, and has some extra code to redraw itself and forward touche

  • Download Gist
PagerActivity.java
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
/*
* Copyright (c) 2012 Wireless Designs, LLC
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.example.pagercontainer;
 
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
 
/**
* PagerActivity: A Sample Activity for PagerContainer
*/
public class PagerActivity extends Activity {
 
PagerContainer mContainer;
 
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
 
mContainer = (PagerContainer) findViewById(R.id.pager_container);
 
ViewPager pager = mContainer.getViewPager();
PagerAdapter adapter = new MyPagerAdapter();
pager.setAdapter(adapter);
//Necessary or the pager will only have one extra page to show
// make this at least however many pages you can see
pager.setOffscreenPageLimit(adapter.getCount());
//A little space between pages
pager.setPageMargin(15);
 
//If hardware acceleration is enabled, you should also remove
// clipping on the pager for its children.
pager.setClipChildren(false);
}
 
//Nothing special about this adapter, just throwing up colored views for demo
private class MyPagerAdapter extends PagerAdapter {
 
@Override
public Object instantiateItem(ViewGroup container, int position) {
TextView view = new TextView(PagerActivity.this);
view.setText("Item "+position);
view.setGravity(Gravity.CENTER);
view.setBackgroundColor(Color.argb(255, position * 50, position * 10, position * 50));
 
container.addView(view);
return view;
}
 
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View)object);
}
 
@Override
public int getCount() {
return 5;
}
 
@Override
public boolean isViewFromObject(View view, Object object) {
return (view == object);
}
}
}
PagerContainer.java
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
/*
* Copyright (c) 2012 Wireless Designs, LLC
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.example.pagercontainer;
 
import android.content.Context;
import android.graphics.Point;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.FrameLayout;
 
/**
* PagerContainer: A layout that displays a ViewPager with its children that are outside
* the typical pager bounds.
*/
public class PagerContainer extends FrameLayout implements ViewPager.OnPageChangeListener {
 
private ViewPager mPager;
boolean mNeedsRedraw = false;
 
public PagerContainer(Context context) {
super(context);
init();
}
 
public PagerContainer(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
 
public PagerContainer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
 
private void init() {
//Disable clipping of children so non-selected pages are visible
setClipChildren(false);
 
//Child clipping doesn't work with hardware acceleration in Android 3.x/4.x
//You need to set this value here if using hardware acceleration in an
// application targeted at these releases.
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
 
@Override
protected void onFinishInflate() {
try {
mPager = (ViewPager) getChildAt(0);
mPager.setOnPageChangeListener(this);
} catch (Exception e) {
throw new IllegalStateException("The root child of PagerContainer must be a ViewPager");
}
}
 
public ViewPager getViewPager() {
return mPager;
}
 
private Point mCenter = new Point();
private Point mInitialTouch = new Point();
 
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mCenter.x = w / 2;
mCenter.y = h / 2;
}
 
@Override
public boolean onTouchEvent(MotionEvent ev) {
//We capture any touches not already handled by the ViewPager
// to implement scrolling from a touch outside the pager bounds.
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mInitialTouch.x = (int)ev.getX();
mInitialTouch.y = (int)ev.getY();
default:
ev.offsetLocation(mCenter.x - mInitialTouch.x, mCenter.y - mInitialTouch.y);
break;
}
 
return mPager.dispatchTouchEvent(ev);
}
 
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//Force the container to redraw on scrolling.
//Without this the outer pages render initially and then stay static
if (mNeedsRedraw) invalidate();
}
 
@Override
public void onPageSelected(int position) { }
 
@Override
public void onPageScrollStateChanged(int state) {
mNeedsRedraw = (state != ViewPager.SCROLL_STATE_IDLE);
}
}
main.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
<!-- Copyright (c) 2012 Wireless Designs, LLC
 
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
 
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-->
 
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
 
<com.example.pagercontainer.PagerContainer
android:id="@+id/pager_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#CCC">
<android.support.v4.view.ViewPager
android:layout_width="150dp"
android:layout_height="100dp"
android:layout_gravity="center_horizontal" />
</com.example.pagercontainer.PagerContainer>
 
</RelativeLayout>

First, thanks for this code!!

But I have some problem with it on some devices : HTC OneX (Android 4.0.4) and Nexus S (CM10 - Android 4.1.1). Otherwise, it works great on the emulator (tested with API 7 to 16) and older devices.

My problem on these devices is that setClipChildren(false) doesn't work very well : just one item is shown by default (the current), and when you slide, just the current item and the next are shown.

Have you got any ideas how to bypass this problem?

Looks like you've found a platform bug related to hardware acceleration (I found the Developer Groups thread). I've starred the issue at http://code.google.com/p/android/issues/detail?id=36788 and would recommend you do the same.

Oh, I have not had time to inform you that you already know ;)

I've starred the issue too ;)

Thanks for your involvement!!

Looks great but I am getting some pretty odd behaviour with dragging the views. Jumping and sticking to the end views for instance. Anyone else noticed this?

What happens to me is that I have set the view pager to take 1/3 of the screen, and sometimes the outer pages stay in place and only the pager scrolls. I solved this by removing the condition for invalidate, so the view always invalidates on scroll:
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//Force the container to redraw on scrolling.
//Without this the outer pages render initially and then stay static
invalidate();
}

Hello, first of all: thank you! I'm trying to implement this and I have one small problem: On my nexus (4.1), if I touch the some no-focused view (any view at right or left of the currently selected view) it reacts like a click in my button in the selected view. I tested on Galaxi Mini (2.3) and this problem do not happens.

Anyone can help me?

Hello, I don't succeeded in using it with a FragmentPagerAdapter, is there something particular to do to make it work with Fragments ?

Thanks.

@magiccyril simplely add an id for ViewPager would make it works for Fragment.

<android.support.v4.view.ViewPager
        android:id="@+id/pager"

Tested in 4.1,works fine.

and because android 4 ViewPager has overscroll effect, it would be better to disable it in this case. using
pager.setOverScrollMode(View.OVER_SCROLL_NEVER);

@coder015 you can bypass it using this:

if (Build.VERSION.SDK_INT >= 11)
{
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}

My viewpager has many item ( about 15 items), and I set mViewPager.setOffscreenPageLimit(6); and mViewPager.setCurrentItem(2); but when I run activity then see viewpager has 1 page in center is visible but page left, right are gone then I scroll viewpager ->page left and right are visible. I don't want that, I want page left and right visible immediately when activity run. How could i do?

Overall a nice solution working well.
However, I tried to use it for hours on 2.X.
It worked only if i put a drawable as a background of my pages and not a color ! (even a drawable with only solid color was working better than a color set directly)
Dunno why ...

I think there is a problem. If we use pager.setOffscreenPageLimit(adapter.getCount());

How do we know the current page selected?

Well, I've looking for something, and I finally got the pagechangelistener, but we get into a problem if we use this:

pager.setOnPageChangeListener(this);

It doesn't work as expected. Do you know what's happening here?

Thanks!!

First of all, very nice implementation! It's a very similar replacement for the Gallery widget.
I've implemented a PageTransformer to create a coverflow effect. It works for the most part, but at start up, it doesn't behave as expected. I can't force the clipped children to PageTransform programmatically.

"pager.setCurrentItem(n)" selects the nth item, but it doesn't trigger a "PageTransform".

"pager.invalidate()" which doesn't have an effect either.

Any ideas of how to trigger a PageTransform on the clipped children? I'm currently looking into fakeDrag() to see if it'll alleviate it

FYI, the fakeDrag commands work perfectly

@m3n0R the Container requires the setOnPageChangedListener, you need to add the implementation to the PagerContainer then delegate it through via the PagerContainer.

@chrisjenx, yep, you were right, it was a pitty it was to late for me :P

@cheezy64, have you implemented the coverflow effect? Can you open source it? Thank you.

Fabulous work, I employed it in my test project and it behaves just what I expetecd except for one minor UI problem: there is a huge blank area at the start and end of ViewPager respectively. I dont want it.

I want the first page at the very left edge of the screen and after scrolling, the last page at the very right edge of the screen. Do you know how to eliminate the blank area?

the lightly tweaked main.xml is just as below:

<?xml version="1.0" encoding="utf-8"?>
android:layout_width="match_parent"
android:layout_height="match_parent" >

<com.founder.gallery.PagerContainer
    android:id="@+id/pager_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="@dimen/activity_horizontal_margin"
    android:background="@android:color/transparent" >

    <android.support.v4.view.ViewPager
        android:layout_width="284dp"
        android:layout_height="238dp" />
</com.founder.gallery.PagerContainer>

Is there anyway to detect when a View other than the center View was touched? I want to allow the user to touch a view on the edge and have it become the selected view.

@frankjoshua, I have this problem too.

@frankjoshua, use this to invalidate pager scroll:

@Override
    protected void onFinishInflate() {
        try {
            mPager = (ViewPager) getChildAt(0);
            mPager.setOnPageChangeListener(this);
            mPager.setOnTouchListener(new View.OnTouchListener() {

                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    v.getParent().requestDisallowInterceptTouchEvent(true);
                    return false;
                }
            });

        } catch (Exception e) {
            throw new IllegalStateException("The root child of PagerContainer must be a ViewPager");
        }
    }

hi ,its very helpful thanks,i created title bar for my application like tabs when i scroll the items its work fine but i need the current selected item i add onpagechangelistener issue is when i swipe the page the allocation of my tab is changing how it can fix .how i get current item please help

Looks like it is fixed in Android KitKat 4.4. Changed my init to:

private void init() {
// Disable clipping of children so non-selected pages are visible
setClipChildren(false);
// Child clipping doesn't work with hardware acceleration in Android
// 3.x/4.x ****BUT IS FIXED IN KIT KAT****
// You need to set this value here if using hardware acceleration in an
// application targeted at these releases.
if (Build.VERSION.SDK_INT < 19) {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
}
Note my app is 4.0 and above so I just check if the sdk is less than 19.

Thanks, I've just discovered this class and it works as expected. Is there a way to have an infinite looper ?

Thank you. How to add images of different widths?

Thank you. I liked it. And customized your code and used for my issues.
There are a problem: Have you tested below method with high int?
For example:
@Override
public int getCount() {
return 100;
}
It is very slow :(
Know I work on improve speed as 6 (in your example).
If you tested please share your experience.
Best Regards.

I applied pager.setPageTransformer(true, new ZoomOutPageTransformer()); So delayed. It looks cool, but speed awful. Do you have any idea improve it?

I found :)
Thanks you!

@Rufatet, did you find any good performance tweaks? I'm not doing any transformations, and it's noticeably slower than a regular ViewPager.

I have the problem with onClickLIsteners. Currently I show three (right, middle, left) ImageViews at the same time, and there are more items with higher indexes that are not visible at the time. The left item are overwritten as the right item, so when I click on the left, it says that the right are clicked. And also, when the first item is selected (left are empty), there is still a "hidden" item right there.
Update: Okey, the problem is that the left item till always have the same clicklistener as the right one. Think the override of onTouchEvent creates the problem.

Hi devunwired,
Just worked as expected. Thank you very much :)

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.