可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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?
回答1:
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.
回答2:
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.:-)
回答3:
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()
.
回答4:
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.