I have an app i am doing, which has a FragmentPagerAdapter setup with 3 fragments to swipe between on the main page.
I have been trying to get it setup so as you swipe between fragments, the action bar changes color (fades in and out).
However i am not sure how i should do this.
What method is called when you swipe between fragments? I.E where should i put the code to change the action bar colour?
And also how would i get the fade in and out effect (so it fades out one colour into another one)?
I would really appreciate anyones help.
Thanks in advance
Cheers
Corey :)
What method is called when you swipe between fragments?
You're looking for ViewPager.OnPageChangeListener.onPageScrolled. This will give you:
position Position index of the first page currently being displayed.
positionOffset Value from [0, 1) indicating the offset from the page at position.
positionOffsetPixels Value in pixels indicating the offset from position.
Although, you'll only need the first two parameters. What you'll want to do is bind a particular color to each of your fragments, retrieve both the current page and next page colors, then blend them together using the positionOffset ratio to create your new ActionBar background.
A useful algorithm for blending two colors based on a ratio can be found in Google's new SlidingTabStrip example. 0.0 will return the second color, 0.5 will return an even blend, and 1.0 will return the first color
static int blendColors(int from, int to, float ratio) {
final float inverseRation = 1f - ratio;
final float r = Color.red(from) * ratio + Color.red(to) * inverseRation;
final float g = Color.green(from) * ratio + Color.green(to) * inverseRation;
final float b = Color.blue(from) * ratio + Color.blue(to) * inverseRation;
return Color.rgb((int) r, (int) g, (int) b);
}
Here's a simple example:
ColorFragment
public class ColorFragment extends Fragment {
private static final String KEY_COLOR = "colorfragment:color";
/** Empty constructor as per the {#link Fragment} docs */
public ColorFragment() {
}
public static ColorFragment newInstance(int color) {
final Bundle args = new Bundle();
args.putInt(KEY_COLOR, color);
final ColorFragment fragment = new ColorFragment();
fragment.setArguments(args);
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final FrameLayout rootView = new FrameLayout(getActivity());
rootView.setBackgroundColor(getArguments().getInt(KEY_COLOR));
return rootView;
}
public int getColor() {
return getArguments().getInt(KEY_COLOR);
}
}
Pulling it all together
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set the ActionBar background
final ColorDrawable actionBarBackground = new ColorDrawable();
getSupportActionBar().setBackgroundDrawable(actionBarBackground);
...
final PagerAdapter pagerAdapter = ...;
...
// Bind your data to your PagerAdapter
...
final ViewPager pager = ...;
pager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
if (position >= pagerAdapter.getCount() - 1) {
// Guard against ArrayIndexOutOfBoundsException
return;
}
// Retrieve the current and next ColorFragment
final ColorFragment from = (ColorFragment) pagerAdapter.getItem(position);
final ColorFragment to = (ColorFragment) pagerAdapter.getItem(position + 1);
// Blend the colors and adjust the ActionBar
final int blended = blendColors(to.getColor(), from.getColor(), positionOffset);
actionBarBackground.setColor(blended);
}
});
pager.setAdapter(pagerAdapter);
}
Results
http://gfycat.com/CautiousBewitchedJabiru
I hope that helps you out some!
You could make your FragmentPagerAdapter implements ViewPager.OnPageChangeListener.
Then, override onPageSelected
#Override
public void onPageSelected(int position) {
// get the action bar here and change color
}
As for the color change animation. I didn't try it, but this is from another stackoverflow issue :
Android objectAnimator animate backgroundColor of Layout
Related
i am successfull in showing and hiding of appbar when swiping between tabs in tablayout. but now i want to animate this hiding and appearing of tab smoothly like we do in switching for the camera tab in whatsapp.
The way to go about it is to translate the AppbarLayout using its translationY attribute in onPageScrolled() callback of the ViewPager's OnPageChangeListener interface using the AppbarLayout's bottom value.
mViewPager.addOnPageChangeListener(new OnPageChangeListener(){
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// get an instance of the appBarLayout This should probably be done at the activity level in its oncreate() method
/* example:
public ViewPager mViewPager;
public AppBarLayout appBar;
onCreate(...){
...
mViewPager = (ViewPager) findViewById(R.id.viewpager);
appBar = (AppBarLayout) findViewById(R.id.appbar);
mViewPager.addOnPageChangeListener(...);
...
}
*/
// set the position you want the app bar to be hidden on the ViewPager
int hideAppBarAtPosition = 0;
if (appBar != null) {
// get an instance of the bottom value of the appBar
float mBottom = (float) appBar.getBottom();
// going right offset changes from zero to one
if (position == hideAppBarAtPosition) {
// Translate the appBar up as the resideReveal slides into view
float y = (positionOffset * mBottom) - mBottom;
// Raise the appBar a little bit higher when it is no longer visible
if (y == -mBottom) {
float h = (float) appBar.getHeight();
if (mBottom < h) mBottom = h;
y = -mBottom - mBottom / 8f;
}
appBar.setTranslationY(y);
} else if (position == hideAppBarAtPosition - 1) {
// Translate the appBar up as the resideReveal slides into view
appBar.setTranslationY(-(positionOffset * mBottom));
} else if (appBar.getTranslationY() != 0f) {
// Reset translation of the appBar
appBar.setTranslationY(0f);
}
}
}
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
If you need more ideas on how to implement it, view this open source project https://github.com/goody-h/ResidingTab
How can I implement swipe of one image on another likewise the attached image.
In image one can see that background image is static, and user can able to swipe another image. Its a screenshot of housing.com app.
Can anyone help me in this.
Note: tried for viewpager, jazzyviewpager, Parallex view but no success
Use custom Viewpager(jazzy viewpager library with stack effect and disable zoomout functionality inside this) to acheive this.
So after so many days of search n implementations I got this exactly.
Steps to achieve:
1. Implement Viewpager.
2. User custom view pager with transition.
3. The most important one, Implement clipDrawable to clip drawable on upcoming page on view pager.
Here is the Scale transformation class uses clipdrawable :
public class ScalePageTransformer implements PageTransformer {
private static final float SCALE_FACTOR = 0.95f;
private final ViewPager mViewPager;
public ScalePageTransformer(ViewPager viewPager) {
this.mViewPager = viewPager;
}
#Override
public void transformPage(View page, float position){
View mainLayout = page.findViewById(R.id.bg_rel);
ClipDrawable clipDrawable = (ClipDrawable) mainLayout.getBackground();
if (position <= 0){
// apply zoom effect and offset translation only for pages to
// the left
// final float transformValue = Math.abs(Math.abs(position) - 1) * (1.0f - SCALE_FACTOR) + SCALE_FACTOR;
int pageWidth = mViewPager.getWidth();
final float translateValue = position* -pageWidth;
// page.setScaleX(transformValue);
// page.setScaleY(transformValue);
if (translateValue > -pageWidth){
page.setTranslationX(translateValue);
} else {
page.setTranslationX(0);
}
clipDrawable.setLevel(10000);
} else {
//entering view
int level = (int) ((1 - position) * 10000);
clipDrawable.setLevel(level);
mainLayout.setTranslationX(-1 * position * page.getWidth());
}
}
}
rel_bg: My main layout having drawables.
Hope it helps for somebody.
Background
Consider the following scenario:
There are 2 viewPagers, each of different width and height, but both have the exact same number of pages.
dragging on one should drag the other, so the effect is like a "parallax" effect.
same as #2, but for the other viewPager.
there is also an auto-switching mechanism between the viewpagers, so every 2 seconds it will auto-switch to the next page (and if on the last page, switch back to the first).
The problem
I think I got some functionality working, but it has some issues:
This is just simple XML. nothing special here.
I've used 'OnPageChangeListener' and there, in 'onPageScrolled' , I've used fake-dragging. Problem is, if the dragging of the user stops near the middle of the page (thus activates auto-scrolling the viewPager left/right), the fake dragging loses its sync, and weird things can occur: wrong page or even staying between pages...
For now, I don't handle the other viewPager with the dragging of the user, but it might be an issue too.
this can be an issue when I succeed solving #2 (or #3). For now, I'm using a handler that use a runnable and calls this command inside :
mViewPager.setCurrentItem((mViewPager.getCurrentItem() + 1) % mViewPager.getAdapter().getCount(), true);
What I've tried
I've tried this post, but it didn't work well, as it got the same issues when the user-dragging stops before finished swithching to another page.
Only similar solution that works, is with a single ViewPager (here), but I need to work with 2 ViewPagers, each affects the other.
So I've tried doing it myself: suppose one viewPager is "viewPager", and the other (that is supposed to follow "viewPager") is "viewPager2" , this is what I've done:
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
int lastPositionOffsetPixels=Integer.MIN_VALUE;
#Override
public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
if (!viewPager2.isFakeDragging())
viewPager2.beginFakeDrag();
if(lastPositionOffsetPixels==Integer.MIN_VALUE) {
lastPositionOffsetPixels=positionOffsetPixels;
return;
}
viewPager2.fakeDragBy((lastPositionOffsetPixels - positionOffsetPixels) * viewPager2.getWidth() / viewPager.getWidth());
lastPositionOffsetPixels = positionOffsetPixels;
}
#Override
public void onPageSelected(final int position) {
if (viewPager2.isFakeDragging())
viewPager2.endFakeDrag();
viewPager2.setCurrentItem(position,true);
}
#Override
public void onPageScrollStateChanged(final int state) {
}
});
I've chosen to use fake-dragging because even the docs say it could be useful for this exact same scenario :
A fake drag can be useful if you want to synchronize the motion of the
ViewPager with the touch scrolling of another view, while still
letting the ViewPager control the snapping motion and fling behavior.
(e.g. parallax-scrolling tabs.) Call fakeDragBy(float) to simulate the
actual drag motion. Call endFakeDrag() to complete the fake drag and
fling as necessary.
To make it easy to test, here's more code:
MainActivity.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ViewPager viewPager = (ViewPager) findViewById(R.id.viewPager);
final ViewPager viewPager2 = (ViewPager) findViewById(R.id.viewPager2);
viewPager.setAdapter(new MyPagerAdapter());
viewPager2.setAdapter(new MyPagerAdapter());
//do here what's needed to make "viewPager2" to follow dragging on "viewPager"
}
private class MyPagerAdapter extends PagerAdapter {
int[] colors = new int[]{0xffff0000, 0xff00ff00, 0xff0000ff};
#Override
public int getCount() {
return 3;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
((ViewPager) container).removeView((View) object);
}
#Override
public boolean isViewFromObject(final View view, final Object object) {
return (view == object);
}
#Override
public Object instantiateItem(final ViewGroup container, final int position) {
TextView textView = new TextView(MainActivity.this);
textView.setText("item" + position);
textView.setBackgroundColor(colors[position]);
textView.setGravity(Gravity.CENTER);
final LayoutParams params = new LayoutParams();
params.height = LayoutParams.MATCH_PARENT;
params.width = LayoutParams.MATCH_PARENT;
params.gravity = Gravity.CENTER;
textView.setLayoutParams(params);
textView.setTextColor(0xff000000);
container.addView(textView);
return textView;
}
}
activity_main
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="viewPager:"/>
<android.support.v4.view.ViewPager
android:id="#+id/viewPager"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="viewPager2:"/>
<android.support.v4.view.ViewPager
android:id="#+id/viewPager2"
android:layout_width="match_parent"
android:layout_height="100dp"/>
</LinearLayout>
The question
The code almost works well. I just need to know, what is missing to avoid the issues of stopping the dragging (by the user)?
The other issues might have easier solutions.
A possible solution
After a lot of work, we've found a solution that almost has no bugs. There is a rare bug that while scrolling, the other viewPager flashes. Weird thing is that it happens only when the user scrolls the second viewPager.
All other tries had other issues, like empty page or "jumpy"/"rubber" effect when finishing the scrolling to a new page.
Here's the code:
private static class ParallaxOnPageChangeListener implements ViewPager.OnPageChangeListener {
private final AtomicReference<ViewPager> masterRef;
/**
* the viewpager that is being scrolled
*/
private ViewPager viewPager;
/**
* the viewpager that should be synced
*/
private ViewPager viewPager2;
private float lastRemainder;
private int mLastPos = -1;
public ParallaxOnPageChangeListener(ViewPager viewPager, ViewPager viewPager2, final AtomicReference<ViewPager> masterRef) {
this.viewPager = viewPager;
this.viewPager2 = viewPager2;
this.masterRef = masterRef;
}
#Override
public void onPageScrollStateChanged(int state) {
final ViewPager currentMaster = masterRef.get();
if (currentMaster == viewPager2)
return;
switch (state) {
case ViewPager.SCROLL_STATE_DRAGGING:
if (currentMaster == null)
masterRef.set(viewPager);
break;
case ViewPager.SCROLL_STATE_SETTLING:
if (mLastPos != viewPager2.getCurrentItem())
viewPager2.setCurrentItem(viewPager.getCurrentItem(), false);
break;
case ViewPager.SCROLL_STATE_IDLE:
masterRef.set(null);
viewPager2.setCurrentItem(viewPager.getCurrentItem(), false);
mLastPos = -1;
break;
}
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (masterRef.get() == viewPager2)
return;
if (mLastPos == -1)
mLastPos = position;
float diffFactor = (float) viewPager2.getWidth() / this.viewPager.getWidth();
float scrollTo = this.viewPager.getScrollX() * diffFactor + lastRemainder;
int scrollToInt = scrollTo < 0 ? (int) Math.ceil(scrollTo) : (int) Math.floor(scrollTo);
lastRemainder = scrollToInt - scrollTo;
if (mLastPos != viewPager.getCurrentItem())
viewPager2.setCurrentItem(viewPager.getCurrentItem(), false);
viewPager2.scrollTo(scrollToInt, 0);
}
#Override
public void onPageSelected(int position) {
}
}
usage:
/**the current master viewPager*/
AtomicReference<ViewPager> masterRef = new AtomicReference<>();
viewPager.addOnPageChangeListener(new ParallaxOnPageChangeListener(viewPager, viewPager2, masterRef));
viewPager2.addOnPageChangeListener(new ParallaxOnPageChangeListener(viewPager2, viewPager, masterRef));
For the auto-switching, the original code works fine.
Github Project
Since it's quite hard to test it, and I want to make it easier for you guys to try it out, here's a Github repo:
[https://github.com/AndroidDeveloperLB/ParallaxViewPagers][4]
Do note that as I've mentioned, it still has issues. Mainly the "flashing" effect from time to time, but for some reason, only when scrolling on the second ViewPager.
I don't think 2 ViewPagers are apt for this. Both encapsulate their own gesture and animation logic, and were not built to be synchronized externally.
You should have a look at the CoordinatorLayout. It is designed specifically to co-ordinate transitions and animations of its child views. Each child view can have a Behaviour and can monitor changes to its dependent sibling views and update its own state.
Report the first ViewPager's scroll changes to the second ViewPager:
viewPager.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
viewPager2.scrollTo(viewPager.getScrollX(), viewPager2.getScrollY());
}
});
With complex pages, this method might make the second pager lag behind.
Edit:
Instead of waiting for the scroll to change, you can report to the method calls directly with a custom class:
public class SyncedViewPager extends ViewPager {
...
private ViewPager mPager;
public void setSecondViewPager(ViewPager viewPager) {
mPager = viewPager;
}
#Override
public void scrollBy(int x, int y) {
super.scrollBy(x, y);
if (mPager != null) {
mPager.scrollBy(x, mPager.getScrollY());
}
}
#Override
public void scrollTo(int x, int y) {
super.scrollTo(x, y);
if (mPager != null) {
mPager.scrollTo(x, mPager.getScrollY());
}
}
}
And set the pager.
viewPager.setSecondViewPager(viewPager2);
Note: neither of these methods will invoke the page change listeners on the second pager.You can just add a listener to the first one and account for both pagers there.
You can use Paralloid. I have an application with parallex effect. Not a pager but a horizontalschrollbar which is quite the same in your context. This library runs well. Alternatively use parallaxviewpager which I didn't use but looks more towards your direction.
I have a concept for a view. Please guide me as to how I can achieve it. Please check the wireframe.
I have already looked at FadingActionBar but it does not seem to help. The problem is I have multiple viewpagers in the screen and am going no where trying to achieve the desired result. It would be super-awesome if I am able to achieve a cool transition/parallax effect.
Any inputs will be much appreciated.
Edit1 :
The tabs are put on a PagerTabStrip and hooked up with the Viewpager below it. The attempt here is to scroll the view and dock the PagerTabStrip to the ActionBar and on Scroll down bring it down to reveal the ImageViewPager.
So, this can be achieved pretty easily, but it requires a little trick, more like an illusion actually. Also, I'm going to be using a ListView instead of a ScrollView for my "scrollable content", mostly because it's easier to work with in this situation and for my tabs I'll be using this open sourced library.
First, you need a View that can store y-coordinates for a given index. This custom View will be placed on top of your bottom ViewPager and appear as the "real" header for each ListView. You need to remember the header's y-coordinate for each page in the ViewPager so you can restore them later as the user swipes between them. I'll expand on this later, but for now here's what that View should look like:
CoordinatedHeader
public class CoordinatedHeader extends FrameLayout {
/** The float array used to store each y-coordinate */
private final float[] mCoordinates = new float[5];
/** True if the header is currently animating, false otherwise */
public boolean mAnimating;
/**
* Constructor for <code>CoordinatedHeader</code>
*
* #param context The {#link Context} to use
* #param attrs The attributes of the XML tag that is inflating the view
*/
public CoordinatedHeader(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* Animates the header to the stored y-coordinate at the given index
*
* #param index The index used to retrieve the stored y-coordinate
* #param duration Sets the duration for the underlying {#link Animator}
*/
public void restoreCoordinate(int index, int duration) {
// Find the stored value for the index
final float y = mCoordinates[index];
// Animate the header to the y-coordinate
animate().y(y).setDuration(duration).setListener(mAnimatorListener).start();
}
/**
* Saves the given y-coordinate at the specified index, the animates the
* header to the requested value
*
* #param index The index used to store the given y-coordinate
* #param y The y-coordinate to save
*/
public void storeCoordinate(int index, float y) {
if (mAnimating) {
// Don't store any coordinates while the header is animating
return;
}
// Save the current y-coordinate
mCoordinates[index] = y;
// Animate the header to the y-coordinate
restoreCoordinate(index, 0);
}
private final AnimatorListener mAnimatorListener = new AnimatorListener() {
/**
* {#inheritDoc}
*/
#Override
public void onAnimationCancel(Animator animation) {
mAnimating = false;
}
/**
* {#inheritDoc}
*/
#Override
public void onAnimationEnd(Animator animation) {
mAnimating = false;
}
/**
* {#inheritDoc}
*/
#Override
public void onAnimationRepeat(Animator animation) {
mAnimating = true;
}
/**
* {#inheritDoc}
*/
#Override
public void onAnimationStart(Animator animation) {
mAnimating = true;
}
};
}
Now you can create the main layout for your Activity or Fragment. The layout contains the bottom ViewPager and the CoordinatedHeader; which consists of, the bottom ViewPager and tabs.
Main layout
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.v4.view.ViewPager
android:id="#+id/activity_home_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<org.seeingpixels.example.widget.CoordinatedHeader
android:id="#+id/activity_home_header"
android:layout_width="match_parent"
android:layout_height="250dp" >
<android.support.v4.view.ViewPager
android:id="#+id/activity_home_header_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.astuetz.viewpager.extensions.PagerSlidingTabStrip
android:id="#+id/activity_home_tabstrip"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="bottom"
android:background="#android:color/white" />
</org.seeingpixels.example.widget.CoordinatedHeader>
</FrameLayout>
The only other layout you need is a "fake" header. This layout will be added to each ListView, giving the illusion the CoordinatedHeader in the main layout is the real one.
Note It's important that the height of this layout is the same as the CoordinatedHeader in the main layout, for this example I'm using 250dp.
Fake header
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="250dp" />
Now you need to prepare each Fragment that will be displayed in the bottom ViewPager to control the CoordinatedHeader by attaching a AbsListView.OnScrollListener to your ListView. This Fragment should also pass a unique index upon creation using Fragment.setArguments. This index should represent its location in the ViewPager.
Note I'm using a ListFragment in this example.
Scrollable content Fragment
/**
* {#inheritDoc}
*/
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
final Activity a = getActivity();
final ListView list = getListView();
// Add the fake header
list.addHeaderView(LayoutInflater.from(a).inflate(R.layout.view_fake_header, list, false));
// Retrieve the index used to save the y-coordinate for this Fragment
final int index = getArguments().getInt("index");
// Find the CoordinatedHeader and tab strip (or anchor point) from the main Activity layout
final CoordinatedHeader header = (CoordinatedHeader) a.findViewById(R.id.activity_home_header);
final View anchor = a.findViewById(R.id.activity_home_tabstrip);
// Attach a custom OnScrollListener used to control the CoordinatedHeader
list.setOnScrollListener(new OnScrollListener() {
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
// Determine the maximum allowed scroll height
final int maxScrollHeight = header.getHeight() - anchor.getHeight();
// If the first item has scrolled off screen, anchor the header
if (firstVisibleItem != 0) {
header.storeCoordinate(index, -maxScrollHeight);
return;
}
final View firstChild = view.getChildAt(firstVisibleItem);
if (firstChild == null) {
return;
}
// Determine the offset to scroll the header
final float offset = Math.min(-firstChild.getY(), maxScrollHeight);
header.storeCoordinate(index, -offset);
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// Nothing to do
}
});
}
Finally, you'll need to setup the Coordinated header to restore its y-coordinates when the user swipes between pages using a ViewPager.OnPageChangeListener.
Note When attaching your PagerAdapter to your bottom ViewPager, it's important to call ViewPager.setOffscreenPageLimit and set that amount to the total amount of pages in your PagerAdapter. This is so the CoordinatedHeader can store the y-coordinate for each Fragment right away, otherwise you'll run into trouble with it being out of sync.
Main Activity
/**
* {#inheritDoc}
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
// Setup the top PagerAdapter
final PagerAdapter topAdapter = new PagerAdapter(getFragmentManager());
topAdapter.buildData(DummyColorFragment.newInstance(Color.RED));
topAdapter.buildData(DummyColorFragment.newInstance(Color.WHITE));
topAdapter.buildData(DummyColorFragment.newInstance(Color.BLUE));
// Setup the top pager
final ViewPager topPager = (ViewPager) findViewById(R.id.activity_home_header_pager);
topPager.setAdapter(topAdapter);
// Setup the bottom PagerAdapter
final PagerAdapter bottomAdapter = new PagerAdapter(getFragmentManager());
bottomAdapter.buildData(DummyListFragment.newInstance(0));
bottomAdapter.buildData(DummyListFragment.newInstance(1));
bottomAdapter.buildData(DummyListFragment.newInstance(2));
bottomAdapter.buildData(DummyListFragment.newInstance(3));
bottomAdapter.buildData(DummyListFragment.newInstance(4));
// Setup the bottom pager
final ViewPager bottomPager = (ViewPager) findViewById(R.id.activity_home_pager);
bottomPager.setOffscreenPageLimit(bottomAdapter.getCount());
bottomPager.setAdapter(bottomAdapter);
// Setup the CoordinatedHeader and tab strip
final CoordinatedHeader header = (CoordinatedHeader) findViewById(R.id.activity_home_header);
final PagerSlidingTabStrip psts = (PagerSlidingTabStrip) findViewById(R.id.activity_home_tabstrip);
psts.setViewPager(bottomPager);
psts.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageScrollStateChanged(int state) {
if (state != ViewPager.SCROLL_STATE_IDLE) {
// Wait until the pager is idle to animate the header
return;
}
header.restoreCoordinate(bottomPager.getCurrentItem(), 250);
}
});
}
You can also achieve this effect by using the Android-ParallaxHeaderViewPager a good example of scrolling tab header by kmshack Github page
The Sample code is give in this Here Git Hub link
Here is the Screen shot
The solution by #adneal is also very useful one to achieve scrolling Tab header.
Hop this will help you
New update
Please check this answer
Google+ profile like scrolling Effect
I use fragment container layout instead viewpager with FadingActionBar. Also i use MaterialTabs library.
Content layout
<xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/windowBackground"
android:orientation="vertical">
<it.neokree.materialtabs.MaterialTabHost
android:id="#+id/materialTabHost"
android:layout_width="match_parent"
android:layout_height="48dp"
app:accentColor="#color/second"
app:primaryColor="#color/actionbar_background"
app:textColor="#FFFFFF" />
<FrameLayout
android:id="#+id/container"
android:layout_width="wrap_content"
android:layout_height="match_parent">
</FrameLayout>
</LinearLayout>
Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FadingActionBarHelper helper = new FadingActionBarHelper()
.actionBarBackground(R.color.actionbar_background)
.headerLayout(R.layout.actionbar_header)
.contentLayout(R.layout.content_layout);
final View root = helper.createView(this);
setContentView(root);
helper.initActionBar(this);
mTabHost = (MaterialTabHost) view.findViewById(R.id.materialTabHost);
//.......
}
#Override
public void onTabSelected(MaterialTab materialTab) {
int position = materialTab.getPosition();
switch (position) {
case 0:
switchFragment(new ProfileFragment(), position);
break;
case 1:
switchFragment(new NotificationFragment(), position);
break;
}
mTabHost.setSelectedNavigationItem(position);
}
Finally a have this result: link(gif)
I need to set the child view as center of the ViewPager and also I would like to show some part of the next and previous views to the current view sides(like current screen below 1). But currently the current view is starting at left side of the ViewPager(like expected screen below 2). How can I achieve that?
Here is my code..
MyViewPagerAdapter
public class MyViewPagerAdapter extends PagerAdapter {
private Activity mActivity;
private int mPageCount;
public MyViewPagerAdapter(Activity activity,int pageCount) {
mActivity = activity;
mPageCount = pageCount;
}
#Override
public int getCount() {
return mPageCount;
}
#Override
public boolean isViewFromObject(View view, Object obj) {
return (view ==(View)obj);
}
#Override
public Object instantiateItem(ViewGroup container,final int position) {
ViewGroup viewGroup = (ViewGroup)mActivity.getLayoutInflater().inflate(
R.layout.item_view, null);
viewGroup.setBackgroundColor(randomColor());
TextView textView = (TextView)viewGroup.findViewById(R.id.textView1);
textView.setText("Page: "+(position+1));
Button button = (Button) viewGroup.findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(mActivity, "Hey, Its clicked!!! at page "+(position+1), Toast.LENGTH_LONG).show();
}
});
container.addView(viewGroup);
return viewGroup;
}
Random rnd = new Random();
private int randomColor(){
return Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));
}
#Override
public void destroyItem(ViewGroup collection, int position, Object view) {
//must be overridden else throws exception as not overridden.
Log.d("Tag", collection.getChildCount()+"");
collection.removeView((View) view);
}
#Override
public float getPageWidth(int position) {
return 0.8f;
}
}
MainActivity
public class MainActivity extends Activity {
private ViewPager viewPager;
LinearLayout linearLayout;
private int ID = 100;
private final int count = 8;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager = (ViewPager) findViewById(R.id.viewPager);
linearLayout = (LinearLayout) findViewById(R.id.indicator_layout);
generateIndicators(count);
MyViewPagerAdapter adapter = new MyViewPagerAdapter(this, count);
viewPager.setAdapter(adapter);
viewPager.setOnPageChangeListener(new OnPageChangeListener() {
int oldPosition = 0;
#Override
public void onPageSelected(int position) {
//this changes the old position's view state image
((TextView)linearLayout.getChildAt(oldPosition)).setText("");
oldPosition = position;
//this changes the current position's view state image
((TextView)linearLayout.getChildAt(position)).setText((position+1)+"");
}
//this method will be called repeatedly upto another item comes as front one(active one)
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
//this will be called as per scroll state
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
viewPager.setOffscreenPageLimit(4);
}
private void generateIndicators(int count) {
/// Converts 14 dip into its equivalent px
int padd = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics());
for(int i=0;i<count;i++){
TextView textView = new TextView(this);
textView.setId(ID+i);
final int currentItem = i;
textView.setBackgroundResource(R.drawable.white_cell);
textView.setPadding(padd,padd,padd,padd);
/// Converts 14 dip into its equivalent px
int size = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics());
textView.setTextSize(size);
textView.setGravity(Gravity.CENTER);
/// Converts 14 dip into its equivalent px
int px = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, getResources().getDisplayMetrics());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(px, px);
linearLayout.addView(textView,params);
}
((TextView)linearLayout.getChildAt(0)).setText("1");
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<android.support.v4.view.ViewPager
android:id="#+id/viewPager"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" >
</android.support.v4.view.ViewPager>
<LinearLayout
android:id="#+id/indicator_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="19dp" >
</LinearLayout>
</RelativeLayout>
item_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dp"
android:layout_height="match_parent"
android:id="#+id/root_view"
android:orientation="vertical" >
<TextView
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Text"
android:textAppearance="?android:attr/textAppearanceLarge" />
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="click me" />
</LinearLayout>
Current screen
expected screen
For one app I implemented similar the following way, with standard ViewPager:
Make pages full-screen with the actual content in an inner layout. For example, make the full-screen layout a RelativeLayout with transparent background and the actual content another RelativeLayout centered in the parent. If I remember right, the reason for this was that with just the inner layout as a page, the ViewPager would not have taken all the screen width on some devices such as Galaxy Nexus.
Use ViewPager.setPageMargin() to set up a negative page margin i.e. how much of the next/previous page you want to show. Make sure it only overlaps the transparent region of the parent full-screen layout.
Call ViewPager.setOffscreenPageLimit() to adjust the off-screen page count to at least 2 from the default 1 to ensure smooth paging by really creating the pages off-screen. Otherwise you will see next/previous pages being drawn while already partially showing on screen.
For anyone upset that the OP didn't update his question with the solution here is a link that explains, with minimal effort, how to pull this off in XML: http://blog.neteril.org/blog/2013/10/14/android-tip-viewpager-with-protruding-children/
Basically when you declare your viewpager in XML, give it the same left and right padding and set android:clipToPadding="false". (The clipToPadding is missing in his xml sample and necessary to achieve this effect)
Finally, I have added my solution for this question in GitHub. I have done some pretty tricks to get the workaround solution. You can get the project from the below link(Actually I have planned to create a blog with the explanation , but I dint have that much time to do).
Here is the link(https://github.com/noundla/Sunny_Projects/tree/master/CenterLockViewPager)
You have to copy the files from com.noundla.centerviewpagersample.comps package to your project. And you can see the usage of that Viewpager in MainActivity class.
Please let me know if anyone has problems with this.
I found solution in this post, below the code i used:
// Offset between sibling pages in dp
int pageOffset = 20;
// Visible part of sibling pages at the edges in dp
int sidePageVisibleWidth = 10;
// Horizontal padding will be
int horPadding = pageOffset + sidePageVisibleWidth;
// Apply parameters
viewPager.setClipToPadding(false);
viewPager.setPageMargin(UIUtil.dpToPx(pageOffset, getContext()));
viewPager.setPadding(UIUtil.dpToPx(horPadding, getContext()), 0, UIUtil.dpToPx(horPadding, getContext()), 0);
dpToPx code:
public static int dpToPx(int dp, Context context) {
float density = context.getResources().getDisplayMetrics().density;
return Math.round((float) dp * density);
}
This is all you need
You can use padding for viewPager and set clipToPadding false
Java
viewPager.setClipToPadding(false);
viewPager.setPadding(50, 0, 50, 0);
Kotlin
viewPager.clipToPadding = false
viewPager.setPadding(50, 0, 50, 0)
I had to center current page in view pager with different page widths, so solution with paddings was not suitable. Also user scrolling was disabled (it was tab bar view pager, scrolled by another view pager). Here is a very simple solution to do that - just override ViewPager.ScrollTo method just like this (C# code, Xamarin):
public override void ScrollTo(int x, int y)
{
x -= (int) (MeasuredWidth * (1 - Adapter.GetPageWidth(CurrentItem)) / 2);
base.ScrollTo(x, y);
}
And if you calculate page width for each fragment don't forget to cache them in array.
Extend HorizontalScrollView class as the parent for the scrolling view. In the onMeasure() method you can specify the width and height of each child. Little cumbersome way but the effect will be good and you can have a good hold on your child view.