Android Dev Tutorial: What is `Infinity` in ViewPager Sample? - android

What is the below code in the Android Developper's Official Tutorial, Using ViewPager for Screen Slides?
public class ZoomOutPageTransformer implements ViewPager.PageTransformer {
//...
public void transformPage(View view, float position) {
//...
if (position < -1) { // **[-Infinity,-1)
//...
} else if (position <= 1) { // [-1,1]
//...
} else { // (1,+Infinity]
//...
}
}
}

The meaning of these comments is more or less:
if (position < -1) { // here go all 'position' values lesser than -1
//...
} else if (position <= 1) { // 'position' in range from -1 to 1, including both -1 and 1
//...
} else { // and then all 'position' values greater than 1 go here
//...
}
If you are looking for an "infinity" value for Float type in Android then there are Float.NEGATIVE_INFINITY and Float.POSITIVE_INFINITY values representing them.

Infinity means that the actual view is way off screen left (negative infinity) or right (positive infinity), so you don't need to apply a scale factor, just set the alpha value to 0
You have to understand that this is a code used to describe the view transition when swiping through the ViewPager fragments, so, when the view is still on screen, you apply the scaleFactor and when it's off, just set it to invisible, or alpha 0.

In the example on the site, the comment below actually explains the behaviour
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
}
What the negative infinity to -1 describes is the position parameters limits for the if block to pass (i.e. be true). In the case -Infinity < position < -1 this code will execute, which means the code below wille execute at the following points:
else if (position <= 1) { // [-1,1]
//true for when position is less than or equal to 1
//but only run when code is greater than or equal to -1
//as this is an else-if
} else { // [1,+Infinity]
//true for when position is greater than 1
}
The position variable accounts for the positioning of the fragment that is to be transformed.
So from this you can see that the limit of negative infinity and -1 means that the actual view is off screen in the left direction. It is off screen in the right direction when position is greater than one and on screen when neither of these conditions are satisfied.

Related

Applying Alpha page transformer to ViewPager

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

Sliding view used to control ViewPager paging

My goal is to have a sliding view on fragment 1 which slides a set distance before fragment 2 is pulled into visual view.
I made a visual here that shows what I am trying to do:
Pic 1 Shows the default view of fragment 1 (pink) and sliding view (green), nothing has happened in this state.
Pic 2 The sliding view is able to slide to it's cutoff (black line). It cannot move past this on this fragment.
Pic 3 The entire fragment 1 begins to slide, fragment 2 (blue) is now starting to be pulled into the view
Pic 4 Both fragments continue to slide as before.
Pic 5 Fragment 2 has now slid into view.
When the user tries to return to the previous screen the transition happens in reverse.
Right now I have implemented a ViewPager which hosts the fragments 1 and 2. The default ViewPager behavior allows me to slide between the two fragments, but I am unsure how to implement the functionality I have described.
I was able to achieve the desired transition with the use of a ViewPager PageTransformer
I am using Xamarin.Android in my project, so C# is used. Although this snippet is in C# it could easily be ported to Java code for use in other projects.
float passpoint = 7/8f;
public void TransformPage(View view, float position)
{
var pageWidth = view.Width;
var slidingGap = pageWidth - pageWidth * passpoint;
var translation = position * pageWidth;
if (position < -1)
{ // [-Infinity,-1)
// This page is way off-screen to the left.
view.Alpha = 0;
}
else if (position <= 1)
{ // [-1,1]
var r = new Rect();
//if valid, this view is the startpage
Button button = (Button)view.FindViewById(Resource.Id.slidingButton);
if (button != null)
{
var posPassPoint = -(1 - passpoint);
float ratio;
//create sliding gap
if (position > posPassPoint)
{
ratio = RatioBetween(position, posPassPoint, 0);
button.TranslationX = slidingGap * -ratio;
}
//compensate for sliding gap after it has been reached
else
{
//1- bc want the ratio to shrink
ratio = 1-RatioBetween(position, -1, posPassPoint);
}
view.TranslationX = slidingGap * ratio;
}
//valid on views other than start page (no sliding button)
if (button == null)
{
float ratio;
//create sliding gap
if (position >= passpoint)
ratio = RatioBetween(position, passpoint, 1);
//compensate for sliding gap after it has been reached
else
ratio = 1 - RatioBetween(position, 0, passpoint);
view.TranslationX = slidingGap * ratio;
}
}
else
{ // (1,+Infinity]
// This page is way off-screen to the right.
view.Alpha = 0;
}
}
float RatioBetween(float input, float upperBound, float lowerBound)
{
return (input - lowerBound) / (upperBound - lowerBound);
}

Android: setTranslationX glitches after view's anchor changed

Using setTranslationX, I'm trying to animate a view as I swipe it across the screen. Then after it passes a threshold-X, I assign the view a new RelativeLayout.RIGHT_OF.
I want it to stop animating (whether or not I continue swiping) at that point and basically lock to that new anchor.
This is where the problem is: suddenly the view jumps X position to the right of its new anchor.
I've tried, when it's >= threshold, to set setTranslationX(0), but then I see the view twitch/flash twice, once to its original 0, then to the new 0.
I would love to get rid of that double twitch/flash, but don't know how at this point.
#Override
public void onChildDraw(Canvas c ... float dX) {
threshold = anchorView.getRight();
if (animate) {
if (dX >= 0) {
translationX = Math.min(dX, threshold);
if (dX >= threshold) {
translationX = 0; // (A) if I do this, then mainView flashs twice: original 0, then new 0
setToRightOf(mainView, anchorView);
mainView.invalidate(); // has no effect
}
} else {
translationX = 0;
}
// if I don't do (A), then mainView will suddenly jump to 2*threshold
mainView.setTranslationX(translationX);
return;
}
super.onChildDraw(c ... dX);
}
Okay, instead of assigning RelativeLayout.RIGHT_OF during onDraw to set the threshold boundary, I took it out and assigned it when my touch left the screen.
But to insure I wouldn't swipe back behind that threshold while swiping, I had to add another case to check translationX and instead of previously trying to rely on the RelativeLayout anchor.
Now, I'm using setTag() and getTag() to help confirm the threshold during the swipe:
if (dX >= 0) {
if ((Object) past != tag)
translationX = Math.min(dX, threshold);
else
translationX = threshold;
if (dX >= threshold) {
if ((Object) past != tag) {
anchorView.setTag(past);
}
}
} else {
...
}
Plus a couple other places to make sure I reset anchorView's tag and the translationX when needed, then it's all good.
It works for now!
(doesn't directly solve the double flash/twitch issue, but a different approach to the same goal)
(any other recommendations besides using setTag()?)
P.S. In my earlier attempts, instead of invalidate(), I later tried mainView.requestLayout() with no success either, thinking requestLayout() also factors in position.

Android Viewpager tinder like UI with 3D card stack appearance

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

ListView inside ViewPager won't scroll when applying a 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.

Categories

Resources