I have a ViewPager that look like this:
I need the previews on the sides to ZoomOut when you swipe and only maintain the size of the center view, something like this:
I already tried some examples with ViewPager.PageTransformer() but those examples are always with a fade animation(That I don't need) and not showing previews(Which I need).
final int paddingPx = 300;
final float MIN_SCALE = 0.8f;
final float MAX_SCALE = 1f;
viewPager.setClipToPadding(false);
viewPager.setPadding(paddingPx, 0, paddingPx, 0);
viewPager.setPageTransformer(false, transformer);
This makes pages smaller than viewpager width:
Here is my Transformer:
PageTransformer transformer = new PageTransformer() {
#Override
public void transformPage(View page, float position) {
float pagerWidthPx = ((ViewPager) page.getParent()).getWidth();
float pageWidthPx = pagerWidthPx - 2 * paddingPx;
float maxVisiblePages = pagerWidthPx / pageWidthPx;
float center = maxVisiblePages / 2f;
float scale;
if (position + 0.5f < center - 0.5f || position > center) {
scale = MIN_SCALE;
} else {
float coef;
if (position + 0.5f < center) {
coef = (position + 1 - center) / 0.5f;
} else {
coef = (center - position) / 0.5f;
}
scale = coef * (MAX_SCALE - MIN_SCALE) + MIN_SCALE;
}
page.setScaleX(scale);
page.setScaleY(scale);
}
};
The difficult thing about this is position parameter of transformPage method. When pages are fill viewpagers width this param changes from 0 to 1. But when pages are smaller this param is tricky.
position is value of left side of the view. Page width equals to 1. position equals to 0 when pages left side is on the left side of viewpager. So position can be more than 1, depends on page width.
First you need to find viewpagers center (relatively to position parameter). Then scale down page if page moves to left or to rigth from center. In my implementation I scale down view on half page distance from center.
Here is result:
Page width can be controlled via padding.
Extra margin between pages can be added throught viewPager.setPageMargin(value) method.
Related
In the picture below, the left screen shows the starting state of the animation and the right screen shows the ending state.
What the animation does is that while the list view is being moved from top to bottom, the Welcome label will gradually move up, and the profile image view will gradually scale smaller and move to the top right corner.
When the list view is being moved back to the top, the reversed animation will happen.
I would like to ask how I can do that?
The picture about the starting and ending states of the animation
Try this transition , It might not be perfect you might have to fix a little bit
Take 2 imageviews starting an ending of animation . make ending animation imageview Invisible
scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
int scrollY = scrollView.getScrollY();
interpolate(start_profile_imageView, end_profile_imageView, scrollY );
}
});
Add these in the same class
private RectF mRect1 = new RectF();
private RectF mRect2 = new RectF();
private void interpolate(View view1, View view2, float interpolation) {
getOnScreenRect(mRect1, view1);
getOnScreenRect(mRect2, view2);
float scaleX = 1.0F + interpolation * (mRect2.width() / mRect1.width() - 1.0F);
float scaleY = 1.0F + interpolation * (mRect2.height() / mRect1.height() - 1.0F);
float translationX = 0.5F * (interpolation * (mRect2.left + mRect2.right - mRect1.left - mRect1.right));
float translationY = 0.5F * (interpolation * (mRect2.top + mRect2.bottom - mRect1.top - mRect1.bottom));
view1.setTranslationX(translationX);
view1.setTranslationY(translationY);
view1.setScaleX(scaleX);
view1.setScaleY(scaleY);
}
private RectF getOnScreenRect(RectF rect, View view) {
rect.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
return rect;
}
Also check out this project
I was reading an example on customizing ViewPager's sliding page animation that entails translating the page(View) to a certain amount. The example is from the docs. Specifically, it is about an implementation called ZoomOutPageTransformer that can be set on a ViewPager through setPageTransformer() to customize how the page slides(incorporating a zoom animation in it).
This is how the end result is supposed to look like:
Now, they describe how they are going to do it:
In your implementation of transformPage(), you can then create
custom slide animations by determining which pages need to be
transformed based on the position of the page on the screen, which is
obtained from the position parameter of the transformPage() method.
The position parameter indicates where a given page is located
relative to the center of the screen. It is a dynamic property that
changes as the user scrolls through the pages. When a page fills the
screen, its position value is 0. When a page is drawn just off the
right side of the screen, its position value is 1. If the user scrolls
halfway between pages one and two, page one has a position of -0.5 and
page two has a position of 0.5. Based on the position of the pages on
the screen, you can create custom slide animations by setting page
properties with methods such as setAlpha(), setTranslationX(), or
setScaleY().
And this is the implementation of the PageTransformer that they have provided:
public class ZoomOutPageTransformer implements ViewPager.PageTransformer {
private static final float MIN_SCALE = 0.85f;
private static final float MIN_ALPHA = 0.5f;
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
int pageHeight = view.getHeight();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 1) { // [-1,1]
// Modify the default slide transition to shrink the page as well
float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
float vertMargin = pageHeight * (1 - scaleFactor) / 2;
float horzMargin = pageWidth * (1 - scaleFactor) / 2;
if (position < 0) {
view.setTranslationX(horzMargin - vertMargin / 2);
} else {
view.setTranslationX(-horzMargin + vertMargin / 2);
}
// Scale the page down (between MIN_SCALE and 1)
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
// Fade the page relative to its size.
view.setAlpha(MIN_ALPHA +
(scaleFactor - MIN_SCALE) /
(1 - MIN_SCALE) * (1 - MIN_ALPHA));
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}
Problem:
I am unable to understand the statement,
view.setTranslationX(horzMargin - vertMargin / 2);
My understanding was that a value of 1.0 for position parameter equates to covering the screen width, i.e, w. So, if the center of a page has moved x units of position then the translation in terms of pixels/dp would be, w*x. But they are using some margin calculation for the translation amount calculation.
Can anyone explain how they have done this calculation and what would be wrong with my calculation?
There is one thing you are missing here, the PageTransformer is applied to the views AFTER they have been positioned.
So, with or without a PageTransformer attached to the PageView - when you scroll between pages - you simply doing a scroll (like in a LiseView) with SNAP capabilities.
The PageTransformer only adds effects on top of that.
So the purpose of,
float vertMargin = pageHeight * (1 - scaleFactor) / 2;
float horzMargin = pageWidth * (1 - scaleFactor) / 2;
if (position < 0) {
view.setTranslationX(horzMargin - vertMargin / 2);
} else {
view.setTranslationX(-horzMargin + vertMargin / 2);
}
is NOT to move the pages - but to compensate the page shrinking.
Without it, the page will have some ugly side effects. TRY IT, remove the lines :) - The views will go right/left but the positioning will be off.
So, the translation X does not move the page, but, in this case, simply manages the pages spacing to improve the animation feel - the above is the Math for it. What it does is to reduce the space between the pages based on the screen height/width. First it negates the space (horzMargin) then it adds a little spacing (- vertMargin / 2)
And that is why your calculation is not good (w*x) - You are trying to move the page - But it is already moving.
GitHub provide a library for animation that you want without any calculations
You don't need to create your custom ViewPager PageTransformer
Simply add below library in your app/build.gradle file.
compile 'com.ToxicBakery.viewpager.transforms:view-pager-transforms:1.2.32#aar'
MainActivity.java
public class MainActivity extends Activity {
private static final String KEY_SELECTED_PAGE = "KEY_SELECTED_PAGE";
private static final String KEY_SELECTED_CLASS = "KEY_SELECTED_CLASS";
private int mSelectedItem;
private ViewPager mPager;
private PageAdapter mAdapter;
#SuppressWarnings("deprecation")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int selectedPage = 0;
if (savedInstanceState != null) {
mSelectedItem = savedInstanceState.getInt(KEY_SELECTED_CLASS);
selectedPage = savedInstanceState.getInt(KEY_SELECTED_PAGE);
}
setContentView(R.layout.activity_main);
mAdapter = new PageAdapter(getFragmentManager());
mPager = (ViewPager) findViewById(R.id.container);
mPager.setAdapter(mAdapter);
try {
mPager.setPageTransformer(true, new TransformerItem(ZoomOutSlideTransformer.class).clazz.newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
mPager.setCurrentItem(selectedPage);
}
public static class PlaceholderFragment extends Fragment {
private static final String EXTRA_POSITION = "EXTRA_POSITION";
private static final int[] COLORS = new int[] { 0xFF33B5E5, 0xFFAA66CC, 0xFF99CC00, 0xFFFFBB33, 0xFFFF4444 };
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final int position = getArguments().getInt(EXTRA_POSITION);
final TextView textViewPosition = (TextView) inflater.inflate(R.layout.fragment_main, container, false);
textViewPosition.setText(Integer.toString(position));
textViewPosition.setBackgroundColor(COLORS[position - 1]);
return textViewPosition;
}
}
private static final class PageAdapter extends FragmentStatePagerAdapter {
public PageAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
#Override
public Fragment getItem(int position) {
final Bundle bundle = new Bundle();
bundle.putInt(PlaceholderFragment.EXTRA_POSITION, position + 1);
final PlaceholderFragment fragment = new PlaceholderFragment();
fragment.setArguments(bundle);
return fragment;
}
#Override
public int getCount() {
return 5;
}
}
private static final class TransformerItem {
final String title;
final Class<? extends PageTransformer> clazz;
public TransformerItem(Class<? extends PageTransformer> clazz) {
this.clazz = clazz;
title = clazz.getSimpleName();
}
#Override
public String toString() {
return title;
}
}
}
activity_main.xml
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="MainActivity" />
fragment_main.xml
<TextView 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:gravity="center"
android:text="#string/app_name"
android:textColor="#android:color/white"
android:textSize="72sp"
tools:context="MainActivity$PlaceholderFragment" />
I hope this may help you.:-)
As I re-read your question and this answer, I'm not sure I've really addressed your question. I think it may be of some help though so I'm posting it.
According to this, position=0 means this page is front & center (& for this example, full screen). We only care about a page with position in the -1 to +1 range, otherwise it is too far out of view to care about. You can see this in the 1st & last conditions where alpha is set to 0, making the view fully transparent.
I am unable to understand the statement,
view.setTranslationX(horzMargin - vertMargin / 2);
As I looked at it, I didn't see much value in this section of code as well. Since the margins are calculated based on the scaleFactor, & that is limited to 0.85 - 1.0, it doesn't make a lot of difference in the appearance of the transition. I'm sure that someone with a much better eye for design than me would disagree. However, I removed this portion of code as an experiment.
float vertMargin = pageHeight * (1 - scaleFactor) / 2;
float horzMargin = pageWidth * (1 - scaleFactor) / 2;
if (position < 0) {
view.setTranslationX(horzMargin - vertMargin / 2);
} else {
view.setTranslationX(-horzMargin + vertMargin / 2);
}
While I could see small differences depending on setting translationX if I looked closely, I'd never notice with casual use. More of a difference can be seen by setting MIN_SCALE to 0.5 (or smaller).
what would be wrong with my calculation?
Probably nothing as long as you limited it to a small result like the current code does. Keep in mind though that the ViewPager class is the primary controller of the animation rather than setTranslationX().
If you shrink the rectange by scaleFactor,
Assume Original Height is pageHeight and Width is pageWidth;
The new shrink Height will be scaleFactor * pageHeight
shrinkMarginTopBottom = pageHeight - scaleFactor * pageHeight = pageHeight(1 - scaleFactor);
and I assume it will be equally shirnked from bottom and top;
shrinkMarginTop = shrinkMarginTopBottom/2;
which is equal to below mentioned formula
float vertMargin = pageHeight * (1 - scaleFactor) / 2;
So post explaning the margin calculations
float vertMargin = pageHeight * (1 - scaleFactor) / 2;
float horzMargin = pageWidth * (1 - scaleFactor) / 2;
if (position < 0) {
view.setTranslationX(horzMargin - vertMargin / 2);
} else {
view.setTranslationX(-horzMargin + vertMargin / 2);
}
The x translation should have some offset for page positions 0 and 1 to get the animation effect and some distance hence subtracting the margins for both ... The division happens because +ve and -ve x translation adds up to exact margin
I may be guessing but for me to have a variable x translation and margin which varies with scale factor, vertMargin is used for creating that offset which varies with scale.. if you remove vertMargin code it will still be animating the same but a bit faster but without keeping any margin between two pages.
I have game field which is a Group with Actors. The Group locketed in Table wich is locketed in ScrollPane. I have two Buttons to zoom in and zoom out the game field. Here is my code how I do it:
TextButton zoomInBtn = new TextButton("+", menuBtnStyle);
zoomInBtn.addListener(new ClickListener() {
#Override
public void clicked(InputEvent event, float x, float y) {
float width = fieldGroup.getWidth();
float height = fieldGroup.getHeight();
float newWidth = width + width * 0.1f;
if (newWidth > myWorld.getMaxWidth()) {
newWidth = myWorld.getMaxWidth();
}
float newHeight = height * newWidth / width;
fieldGroup.setWidth(newWidth);
fieldGroup.setHeight(newHeight);
myWorld.setWidth(Math.round(newWidth));
fieldGroup.reinitialiseChildren();
Cell cell = fieldTable.getCell(fieldGroup);
cell.clearActor();
cell.setActor(fieldGroup);
}
});
TextButton zoomOutBtn = new TextButton("-", menuBtnStyle);
zoomOutBtn.addListener(new ClickListener() {
#Override
public void clicked(InputEvent event, float x, float y) {
float width = fieldGroup.getWidth();
float height = fieldGroup.getHeight();
float newWidth = width - width * 0.1f;
if (newWidth < myWorld.getMinWidth()) {
newWidth = myWorld.getMinWidth();
}
float newHeight = height * newWidth / width;
Actor widget = scrollPane.getWidget();
fieldGroup.setWidth(newWidth);
fieldGroup.setHeight(newHeight);
myWorld.setWidth(Math.round(newWidth));
fieldGroup.reinitialiseChildren();
Cell cell = fieldTable.getCell(fieldGroup);
cell.clearActor();
cell.setActor(fieldGroup);
}
});
I change the size of my fieldGroup with Image Actors in it. And then readding it to Table.
The problem is: when I zoom with buttons it always zoom around left corner. I want it to zoom from canter of ScrollPane. I know that I can do it with Ortographic Camera, but it would be difficult, I think, to make it movements so smooth as ScrollPane. So maybe there is some way to do it with ScrollPane.
Every time you zoom in or out you would have to change the x and y position of the group relative to the zoom. You can do this using the setScrollx (and y) of your scroll pane.
You need to set so that the middle of the part you are viewing stays in the middle of the scrollpane. You can work out middle of newWidth by dividing it by 2. If the scrollPane is fullScreen you can half the value obtained by Gdx.graphics.getwidth();. The difference between these two is then the value which will keep the middles aligned.
newXvalue =(newWidth/2) - ((Gdx.graphics.getWidth())/2);
scrollPane.setScrollx(newXvalue);
The same should be done for y.
Note: if you want to zoom and move at same time, this will not work, I would recommend using orthographic camera with a gesture listener for that.
Currently i am using ViewPager to do a carousel effect menu
However i am facing some initialization issue
This is how i want my carousel to look like after the app start, look at the 2nd tile which is the video tile, it is dimmer and smaller than the 1st tile which is the music tile
However what i get is like this after the app start (if i didnt move it or drag it)
To make the carousel to look like the first image, i need to manually move it or drag it first, then everything look what it should be
Below is my code snippet for setting up the viewpage
pageAdapter = new MyPageAdapter(getSupportFragmentManager(), fragments);
pager = (ViewPager) findViewById(R.id.viewpager);
pager.setPageMargin(-700);//so that it look like carousel
pager.setOffscreenPageLimit(Menu.size());
pager.setAdapter(pageAdapter);
pager.setOnPageChangeListener(pageAdapter);
pager.setPageTransformer(true, new ZoomOutPageTransformer());
Below is my code snippet for including the zoomoutpagetransformer for animation between pages
public class ZoomOutPageTransformer implements ViewPager.PageTransformer {
private static final float MIN_SCALE = 0.85f;
private static final float MIN_ALPHA = 0.5f;
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
int pageHeight = view.getHeight();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 1) { // [-1,1]
// Modify the default slide transition to shrink the page as well
float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
float vertMargin = pageHeight * (1 - scaleFactor) / 2;
float horzMargin = pageWidth * (1 - scaleFactor) / 2;
if (position < 0) {
view.setTranslationX(horzMargin - vertMargin / 2);// positive
} else {
view.setTranslationX(-horzMargin + vertMargin / 2);// negative
}
// Scale the page down (between MIN_SCALE and 1)
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
// Fade the page relative to its size.
view.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA));
textview.setText(String.valueOf(scaleFactor) + "\n" + String.valueOf(vertMargin) + "\n"
+ String.valueOf(horzMargin));
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}
Anyone has any idea how to let it look like what it should be after the app start without i move or drag it first?
Basically the PageTransformer's transformPage() method return the wrong position when you play with padding and margin on the view pager. Seems to be a framework bug as stated here.
Please see my answer in this related question providing a workaround.
I have a ViewPager in which each of it's views is a representation of card on a deck. Each card has a shadow on the border using the ViewPager margin:
cardsViewPager.setPageMargin(getResources().getDisplayMetrics().widthPixels / 20);
cardsViewPager.setPageMarginDrawable(R.drawable.shadow);
And it works as expected.
But, if I add a PageTransformer so that the cards on the right will stack on top of the cards on the left:
public class ScalePageTransformer implements PageTransformer {
private final ViewPager mViewPager;
public ScalePageTransformer(ViewPager viewPager) {
this.mViewPager = viewPager;
}
#Override
public void transformPage(View page, float position) {
if (position <= 0) {
int pageWidth = mViewPager.getWidth();
final float translateValue = position * -pageWidth;
if (translateValue > -pageWidth) {
page.setTranslationX(translateValue);
} else {
page.setTranslationX(0);
}
}
}
}
I do this by:
cardsViewPager.setPageTransformer(false, new ScalePageTransformer(cardsViewPager));
But now, the margin does not appear. If I had a zoom out effect on the PageTransformer, I can see when the current card is being scaled down, that the margin drawable is below the current card on the screen. Here is a pic to describe what's happening:
The blue card is being swiped from the right to left on top of the red card. Since the red card has a scale transformation, we can see the margin drawable in black behind it.
Is there a way to force the margin drawable to be on top of the red card? Shouldn't this be the default behavior?
Change to
cardsViewPager.setPageTransformer(true, new ScalePageTransformer(cardsViewPager));
First argument is the drawing order
Try using this code for the transformation stage of your View Pages :
private static final float MIN_SCALE = 0.85f;
private static final float MIN_ALPHA = 0.5f;
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
int pageHeight = view.getHeight();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 1) { // [-1,1]
// Modify the default slide transition to shrink the page as well
float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
float vertMargin = pageHeight * (1 - scaleFactor) / 2;
float horzMargin = pageWidth * (1 - scaleFactor) / 2;
if (position < 0) {
view.setTranslationX(horzMargin - vertMargin / 2);
} else {
view.setTranslationX(-horzMargin + vertMargin / 2);
}
// Scale the page down (between MIN_SCALE and 1)
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
// Fade the page relative to its size.
view.setAlpha(MIN_ALPHA +
(scaleFactor - MIN_SCALE) /
(1 - MIN_SCALE) * (1 - MIN_ALPHA));
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
Hope this helps...