I'm trying to achieve this:
however, while I am able to achieve the Card sliding effect, I am unable to make the cards appear one behind the other like in this picture.
viewPager.setPageTransformer(false, (page, position) -> {
if (position >= 0) {
page.setScaleX(0.7f - 0.05f * position);
page.setScaleY(0.7f);
page.setTranslationX(-page.getWidth() * position);
page.setTranslationY(30 * position);
}
Related
I am implementing ViewPager Transformer where, Text Alpha should slowly transformed from 0.3f(Unselected Page) - 1.0f(Selected Page) as we are swiping in viewpager.
Here is the Desired output. Viewpager Transformation image
As I am new to Android, I tried following approach, basically I am confused in what formula to apply.
public class AlphaTextTransformer implements ViewPager.PageTransformer {
public void transformPage(#NonNull View view, float position) {
int pageWidth = view.getWidth();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0.3 f);
} else if (position <= 0) { // [-1,0]
// Use the default slide transition when moving to the left page
view.setAlpha(0.3 f);
} else if (position <= 1) { // (0,1]
// Fade the page out.
view.setAlpha(0.3 f + Math.abs(position));
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0.3 f);
}
}
}
I show 3 pages in screen at a time.Left & Right page are partially shown with Text alpha value to 0.3f.Center page text has alpha value of 1. I don't want to immediately set alpha value to 1, but rather it will be progressive as we perform sliding. Can someone please help me with correct pointer? I would really appreciate if someone can help me with Sample code.
Thanks.
Did you check the value of postion with breakpoint or log? Maybe it does not correspond to your expected value?
You should check on this library's code to see how it works, it's with pagetransformer
I am trying to implement a vertical swipeable ViewPager with stack of cards like appearance.
I am able to achieve VerticalViewPager using ViewPager.PageTransformer and swapping the touch points.
I am getting following card appearance -
I want to achieve following appearance -
How can I achieve this effect?
Thanks in Advance.
In order to achieve this vertical View Pager without any library dependency
You can follow the steps below :
In your activity_main.xml
<android.support.v4.view.ViewPager
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="600dp"
android:id="#+id/viewpager">
</android.support.v4.view.ViewPager>
In your viewpager_contents.xml
You can create the design that you want according to the above
picture.
Create the adapter and model class to hold the data.
4.In MainActivity.java, after setting the adapter and the pageTransformer add the following code.
private class ViewPagerStack implements ViewPager.PageTransformer {
#Override
public void transformPage(View page, float position) {
if (position >= 0) {
page.setScaleX(0.7f - 0.05f * position);
page.setScaleY(0.7f);
page.setTranslationX(-page.getWidth() * position);
page.setTranslationY(30 * position);
}
}
}
For detailed reference you can watch this video:
CLICK HERE
Hope you will get the output what you needed!!
none of the above solutions worked for me peroperly.
this is my solution :
My PageTransformer class :
class ViewPagerCardTransformer : ViewPager.PageTransformer {
override fun transformPage(page: View, position: Float) {
if (position >= 0) {
page.scaleX = 0.9f - 0.05f * position
page.scaleY = 0.9f
page.alpha = 1f - 0.3f * position
page.translationX = -page.width * position
page.translationY = -30 * position
} else {
page.alpha = 1 + 0.3f * position
page.scaleX = 0.9f + 0.05f * position
page.scaleY = 0.9f
page.translationX = page.width * position
page.translationY = 30 * position
}
}
}
code in my fragment :
val adapter = MyPagerAdapter(pageItems)
viewPager.adapter = adapter
/*below line will increase the number of cards stacked on top of
each other it is set to 1 by default. don't increase it too much as you
will end up with too much view page items in your memory
*/
mDaysViewPager.offscreenPageLimit = 3
viewPager.setPageTransformer(true, ViewPagerCardTransformer())
I'm Using this code for vertical card view stack
https://github.com/AndroidDeveloper98/StackViewPager
You could use the SwipeStack library. It does exactly what you want - it is basically a viewgroup that inflates views based on your adapter implementation, placing them one on top of the other.
The magic is here https://github.com/yuyakaido/CardStackView. You can easily custom what you want. Happy coding :)
I'm trying to create a tinder-like UI in android using the ViewPager.
I have looked at this library: https://github.com/kikoso/Swipeable-Cards, but I'd like to be able to see the previous card once I swipe right, hence the preference for a ViewPager.
The specific UI detail I am looking for is:
Stacking of images with the outline of the next view underneath the current view. I have been able to achieve the stacking of views through the ViewPager.PageTransformer interface, but I am unable to get the outline of the stack of the subsequent views inside the pager - the part that gives it a 3d look - like over here - card stacked on top of another card - http://i1.cdnds.net/14/38/300x522/friends-tinder-profiles-ross.jpg
Here is my pageTransform method
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 0) { // [-1,0]
// Use the default slide transition when moving to the left page
view.setAlpha(1);
view.setTranslationX(0);
view.setScaleX(1);
view.setScaleY(1);
view.setRotation(90*(position));
} else if (position < 1) { // (0,1]
// Fade the page out.
view.setAlpha(1);
// Counteract the default slide transition
view.setTranslationX(pageWidth * -position);
view.setScaleX(1);
view.setScaleY(1);
} else if (position==1) {
view.setAlpha(1);
// view.setPadding(0,15,0,0);
}
else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
Is this possible with the viewPager?
So this was the way that I set it up - its a little hacky because I manually trigger the transformPage method as mentioned in my comment here:
Android ViewPager manually call PageTransformer transformPage
BUT, it works!
#Override
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 0) { // [-1,0]
// Use the default slide transition when moving to the left page
view.setAlpha(1);
view.setTranslationX(0);
view.setTranslationY(0);
view.setScaleX(1);
view.setScaleY(1);
view.setRotation(90*(position));
} else if (position < 1) { // (0,1]
// Fade the page out.
view.setAlpha(1);
view.setRotation(0);
// Counteract the default slide transition
view.setTranslationX(pageWidth * -position);
view.setTranslationY(50*position);
view.setScaleX(Math.max(0.9f, (1 - position)));
view.setScaleY(Math.max(0.9f, (1 - position)));
CardView cv = (CardView)view.findViewById(R.id.card_view);
cv.setPadding(0,0,0,0);
} else if (position==1) {
view.setAlpha(1);
view.setTranslationX(pageWidth*-position);
view.setTranslationY(50*position);
view.setScaleX(Math.max(0.9f, (1 - position)));
view.setScaleY(Math.max(0.9f, (1 - position)));
}
else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(1);
}
}
You can not implement tinder effect with PageTransformer interface because the position value is just 1 axis value. You should have other values like touch point coordinates in x, y axis because tinder effect uses trigonometrical functions.
You can easily do this using the Android Library Truffle-Shuffle. You can add X number of cards with any xml content inside of the cards. https://github.com/intuit/truffle-shuffle
Is it possible to setup different PageTransformers for different fragments in ViewPager?
I would like to use ZoomOutPageTransformer when the user swipes between (0, 1) and (n-1, n) fragments of ViewPager with n fragments and use the default transitions for all other cases.
What I tried:
Detect the current page with mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { ... }) and apply different transitions here. But, for example, if the user is on the first page - how to set ZoomOutPageTransformer only for the case when he will swipe to the left (0 page) and do it before animation is started?
I had a less similar requirement wherein I had applied Depth page transformer to all my pages by default and on tap of a button I changed it to Vertical page transformer and then back to Depth page. (A lil wierd but thats how it was)
I did the below:
Applied viewPager.setPageTransformer(true, new DepthPageTransformer()); for all the pages.
onClick I changed it to viewPager.setPageTransformer(true, new VerticalPageTransformer());. Now here's the catch in-order for this to function smoothly I had to use a handler in order to change the page a little later to ensure VerticalPager takes effect. I achieved this by calling viewPager.setCurrentPosition(viewPager.getCurrentItem() + 1)
After this inside onPageChangeListener's onPageSelected method I changed it back to viewPager.setPageTransformer(true, new DepthPageTransformer()); again inside a handler to ensure it's done after my vertical animation is completed.
Note - I had also accessed ViewPager's scroller to achieve the above to define my own speed for the page change.
Let me know if this makes sense, since you need it to be done based on page numbers. If it does I will share my code. :)
This is quite old, but wish it's not too late for somebody
Step 1:
Create custom ViewPager which extends from ViewPager.
Step 2:
Determine which Fragment positions (ViewPager page integers) you need to have a unique PageTransformer for them. Then add these indices into a list within the custom ViewPager.
Step 3:
Override onPageScrolled(int position, float offset, int offsetPixels):
Check if the position is in your list of indices, if so, then you have to create a Custom PageTransformer object; and then utilize the ViewPager setPageTransformer() to change the page transformation.
For positions other than that, then you've to set the default PageTransformer (or you can also you another custom PageTransformer object if you wish.
Code
public class CustomViewPager extends ViewPager {
public CustomViewPager(Context context) {
super(context);
}
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
PageTransformer mDepthPageTransformer = new PageTransformer() {
private static final float MIN_SCALE = 0.75f;
public void transformPage(#NonNull View view, float position) {
int pageWidth = view.getWidth();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0f);
} else if (position <= 0) { // [-1,0]
// Use the default slide transition when moving to the left page
view.setAlpha(1f);
view.setTranslationX(0f);
view.setScaleX(1f);
view.setScaleY(1f);
} else if (position <= 1) { // (0,1]
// Fade the page out.
view.setAlpha(1 - position);
// Counteract the default slide transition
view.setTranslationX(pageWidth * -position);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE
+ (1 - MIN_SCALE) * (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0f);
}
}
};
PageTransformer mDefaultPageTransformer = new PageTransformer() {
#Override
public void transformPage(#NonNull View page, float position) {
}
};
#Override
protected void onPageScrolled(int position, float offset, int offsetPixels) {
List<Integer> pos = new ArrayList<>();
pos.add(0);
pos.add(n - 1); // n is the no. of pages as in the question
if (pos.contains(position)) {
this.setPageTransformer(true, mDepthPageTransformer);
} else {
this.setPageTransformer(true, mDefaultPageTransformer);
}
super.onPageScrolled(position, offset, offsetPixels);
}
}
Documentation
Slide between fragments using ViewPager
setPageTransformer() method
Note: mDepthPageTransformer used in my example is taken from the above mentioned documentation.
Inspite Of using mViewPager.setOnPageChangeListener , a single PageTransformer with different animations can help.
Have a look at first paragraph here ,
http://developer.android.com/training/animation/screen-slide.html#pagetransformer
I have a ViewPager in which the pages contain ListViews.
Everything works fine and my viewPAger as well as ListViews work as expected : it is possible to swipe from page to page, and the listviews scroll vertically as they should.
Now I wanted to add a PageTransformer to smooth out paging anbd I used the ZoomOutPageTransformer offered in the google docs.
Now I have a nice animation when swiping between views but the Lists are not scrollable anymore.
Here's the code :
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
LayoutInflater inflater = LayoutInflater.from(getActivity());
viewPager = (ViewPager) view.findViewById(R.id.bookMenuPager);
viewPager.setPageTransformer(false, new ZoomOutPageTransformer());
pagerAdapter = new MenuPagerAdapter();
viewPager.setAdapter(pagerAdapter);
}
class MenuPagerAdapter extends PagerAdapter{
#Override
public int getCount() {
return 3; //change this as needed
}
#Override
public boolean isViewFromObject(View view, Object o) {
return view.equals( o );
}
#Override
public Object instantiateItem(ViewGroup collection, int position) {
LayoutInflater inflater = (LayoutInflater) collection.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if(position == 0){
if(!rootMenuAdded){
viewPager.addView(rootMenucont, 0);
rootMenuAdded = true;
}
return rootMenucont;
}else if(position == 1){
if(!level1MenuAdded){
viewPager.addView(level1MenuCont, 0);
level1MenuAdded = true;
}
return level1MenuCont;
}else if(position == 2){
if(!level2MenuAdded){
viewPager.addView(level2MenuCont, 0);
level2MenuAdded = true;
}
return level2MenuCont;
}
//we got a problem houston
return null;
}
}
and the layout for a page :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/level1MenuCont"
android:layout_height="match_parent"
android:layout_width="match_parent"
>
<ListView
android:id="#+id/level1Menu"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#f02bb6"
>
</ListView>
</RelativeLayout>
What can I do to have my lists scrolling as expected ? What does the PageTransformer break in my ListView so that it wont scroll anymore?
Is this a known bug?
Thanks for any help :)
I think I have found a workaround for this issue.
After some investigation, I think this only happens if you apply a PageTransformer that changes the coordinates of the Views so they are all on top of each other (the two example transformers do exactly this).
When you swipe in the direction such as the NEW VIEW has a Z-index LOWER than the OLD VIEW (normally a swipe backwards), what happens with those transformers is that the OLD VIEW is on top of the NEW VIEW, with Alpha==0, and is the one that later on gets the "ghost" touches.
Unfortunately, the solution by #ngatyrauks bringToFront() didn't work for me (although it definitely should).
However, I have tweaked the transformer so invisible views are changed its visibility to "GONE". And this does the trick.
I have yet to investigate if this Visibility change has any side effects (A GONE view will return null and zeros in layout etc, so maybe this breaks other things inside ViewPager), but so far it's working perfect.
I post here a tweaked DepthPageTransformer (the same in the docs) with these changes. Hope it helps anybody!
package com.regaliz.gui.fx;
import android.util.Log;
import android.view.View;
import android.support.v4.view.ViewPager;
public class DepthPageTransformer implements ViewPager.PageTransformer {
private static final String TAG="DepthTransformer";
private static float MIN_SCALE = 0.75f;
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
Log.d(TAG, "VIew "+view+" Position: "+position);
if (position <= -1) { // [-Infinity,-1) ] ***
// RLP> I Changed to include "-1" as well: When position is -1, the view is not visible
// This page is way off-screen to the left.
view.setAlpha(0);
Log.d(TAG, "VIew "+view+" Position: "+position+", way left");
view.setVisibility(View.GONE);
} else if (position <= 0) { // [ (-1,0]
// Use the default slide transition when moving to the left page
view.setAlpha(1);
view.setTranslationX(0);
view.setScaleX(1);
view.setScaleY(1);
if (position==0) {
Log.d(TAG, "View "+view+" focused now?");
}
if (view.getVisibility()!=View.VISIBLE)
view.setVisibility(View.VISIBLE);
} else if (position <= 1) { // (0,1]
// Fade the page out.
view.setAlpha(1 - position);
// Counteract the default slide transition
// I THINK THIS IS WHAT BREAKS EVERYTHING
// ViewPager normally has the views one after another, but this makes all views on top
view.setTranslationX(pageWidth * -position);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
if (position==1) {
Log.d(TAG, "View "+view+" invisible now?");
view.setVisibility(View.GONE);
// we totally hide the view. This seems to solve focus issue
} else {
if (view.getVisibility()!=View.VISIBLE)
view.setVisibility(View.VISIBLE);
}
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
// we totally hide the view. This seems to solve focus issue
// I have to check for strange side-effects, but so far I found none :)
view.setVisibility(View.GONE);
Log.d(TAG, "VIew "+view+" Position: "+position+", way right");
}
}
}
Here is the detail of the reason
after 4.1 that the framework respects a custom child drawing order as implied Z-ordering for dispatching touch events. If your views overlap after this page transformation they may not receive touch events in the expected order on older platform versions. Check which view is receiving the touch events to be certain.
If this is what you are seeing you have a few options:
Enforce the desired ordering as you add/remove child views in your PagerAdapter
Remove the X translation applied by the PageTransformer when a page is no longer fully visible - i.e. the "position" parameter reports a full -1 or 1.
And here is my solution
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
if (position <= -1 || position >= 1) { // [-Infinity,-1) ] ***
// [-Infinity,-1] or [1,+Infinity]
// This page is way off-screen to the left or way off-screen to the right.
view.setAlpha(0);
view.setTranslationX(0);
view.setScaleX(1);
view.setScaleY(1);
} else if (position <= 0) { // [ (-1,0]
// Use the default slide transition when moving to the left page
view.setAlpha(1);
view.setTranslationX(0);
view.setScaleX(1);
view.setScaleY(1);
} else if (position < 1) {
// (0,1)
// Fade the page out.
view.setAlpha(1 - position);
view.setTranslationX(pageWidth * -position);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
}
}
ref link: https://code.google.com/p/android/issues/detail?id=58918
I don't know if you got this working, but I have the same issue, with a PageDepthTransformer. I'm using a gridview though, the scrolling works, however my subsequent fragments seem to have focus and my top level Fragment doesn't register the correct onClick() events.
My work around for this was to add global layout listener to viewPager and bring the current view to the front
viewPager.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
View view = viewPager.getChildAt(currentFragmentPosition);
if (view != null) {
view.bringToFront();
}
}
)};
This seems like hackery to me, and I haven't quite got this working on rotation. But hopefully it might help you.