就像在 LinkedIn 中的前三个屏幕
- 溅
- 登录/注册按钮
- 登录/注册表单
都有相同的图像作为背景,但是当我们从一个活动移动到另一个活动时,背景图像从右向左滚动。
我只能尝试overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right);
但这不是它的样子。
就像在 LinkedIn 中的前三个屏幕
- 溅
- 登录/注册按钮
- 登录/注册表单
都有相同的图像作为背景,但是当我们从一个活动移动到另一个活动时,背景图像从右向左滚动。
我只能尝试overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right);
但这不是它的样子。
这称为视差滚动,我使用 2 层实现它:一层用于内容,另一层用于背景。内容,您将其放置在没有背景的 ViewPager 上。请注意,您将使用由 viewpager 动画的片段(每个页面都是一个片段)而不是活动。(参见 FragmentStatePagerAdapter)
背景在背景层上,显然在 viewpager 之后并且独立于它。它可以是滚动视图中的图像,也可以是您将要移动其剪辑区域的图像,或者是您通过 drawBitmap(x,y) 渲染的图像。请参阅我的解决方案的附加源,它扩展了一个视图,其背景可以滚动,只需调用方法“setPercent”
然后你覆盖
viewPager.setOnPageChangeListener(new OnPageChangeListener(){
@Override
public void onPageScrolled(int position, float percent, int pixoffset) {
// this is called while user's flinging with:
// position is the page number
// percent is the percentage scrolled (0...1)
// pixoffset is the pixel offset related to that percentage
// so we got everything we need ....
int totalpages=mViewPagerAdapter.getCount(); // the total number of pages
float finalPercentage=((position+percent)*100/totalpages); // percentage of this page+offset respect the total pages
setBackgroundX ((int)finalPercentage);
}
}
void setBackgroundX(int scrollPosition) {
// now you have to scroll the background layer to this position. You can either adjust the clipping or
// the background X coordinate, or a scroll position if you use an image inside an scrollview ...
// I personally like to extend View and draw a scaled bitmap with a clipping region (drawBitmap with Rect parameters), so just modifying the X position then calling invalidate will do. See attached source ParallaxBackground
parallaxBackground.setPercent(position);
}
现在是视差背景视图,位于 ViewPager 后面。我在这里发布了我自己的 ParallaxBackgroundView 的完整工作版本。这实际上是经过测试的代码。
package com.regaliz.gui.views;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
/**
* Implements a horizontal parallax background. The image is set via setImageDrawable(), it is then scaled to 150% and
* you set the percentage via setPErcentage.
* @author rodo
*/
public class ParallaxBackground extends View {
private final static String TAG="ParallaxBackground";
private final static int MODE_PRESCALE=0, MODE_POSTSCALE=1;
/** How much a image will be scaled */
/** Warning: A full screen image on a Samsung 10.1 scaled to 1.5 consumes 6Mb !! So be careful */
private final static float FACTOR=1.5f;
/** The current background */
private Bitmap mCurrentBackground=null;
/** Current progress 0...100 */
private float mOffsetPercent=0;
/** Flag to activate */
private boolean isParallax=true;
/** The parallax mode (MODE_XXX) */
private int mParallaxMode=MODE_PRESCALE;
/** precalc stuff to tighten onDraw calls */
private int mCurrentFactorWidth;
private float mCurrentFactorMultiplier;
private Rect mRectDestination, mRectSource;
private Paint mPaint;
public ParallaxBackground(Context context, AttributeSet attrs) {
super(context, attrs);
construct(context);
}
public ParallaxBackground(Context context) {
super(context);
construct(context);
}
/**
* Enables or disables parallax mode
* @param status
*/
public void setParallax(boolean status) {
Log.d(TAG, "*** PARALLAX: "+status);
isParallax=status;
}
/**
* Sets the parallax memory mode. MODE_PRESCALE uses more memory but scrolls slightly smoother. MODE_POSTSCALE uses less memory but is more CPU-intensive.
* @param mode
*/
public void setParallaxMemoryMode(int mode) {
mParallaxMode=mode;
if (mCurrentBackground!=null) {
mCurrentBackground.recycle();
mCurrentBackground=null;
}
}
/**
* Seth the percentage of the parallax scroll. 0 Means totally left, 100 means totally right.
* @param percentage The perc,
*/
public void setPercent(float percentage) {
if (percentage==mOffsetPercent) return;
if (percentage>100) percentage=100;
if (percentage<0) percentage=0;
mOffsetPercent=percentage;
invalidate();
}
/**
* Wether PArallax is active or not.
* @return ditto.
*/
public boolean isParallax() {
return isParallax && (mCurrentBackground!=null);
}
/**
* We override setBackgroundDrawable so we can set the background image as usual, like in a normal view.
* If parallax is active, it will create the scaled bitmap that we use on onDraw(). If parallax is not
* active, it will divert to super.setBackgroundDrawable() to draw the background normally.
* If it is called with anything than a BitMapDrawable, it will clear the stored background and call super()
*/
@Override
public void setBackgroundDrawable (Drawable d) {
Log.d(TAG, "*** Set background has been called !!");
if ((!isParallax) || (!(d instanceof BitmapDrawable))) {
Log.d(TAG, "No parallax is active: Setting background normally.");
if (mCurrentBackground!=null) {
mCurrentBackground.recycle(); // arguably here
mCurrentBackground=null;
}
super.setBackgroundDrawable(d);
return;
}
switch (mParallaxMode) {
case MODE_POSTSCALE:
setBackgroundDrawable_postscale(d);
break;
case MODE_PRESCALE:
setBackgroundDrawable_prescale(d);
break;
}
}
private void setBackgroundDrawable_prescale(Drawable incomingImage) {
Bitmap original=((BitmapDrawable) incomingImage).getBitmap();
Log.v(TAG, "Created bitmap for background : original: "+original.getByteCount()+", w="+original.getWidth()+", h="+original.getHeight());
mCurrentBackground=Bitmap.createBitmap((int) (this.getWidth()*FACTOR), this.getHeight(), Config.ARGB_8888);
Canvas canvas=new Canvas(mCurrentBackground);
// we crop the original image up and down, as it has been expanded to FACTOR
// you can play with the Adjustement value to crop top, center or bottom.
// I only use center so its hardcoded.
float scaledBitmapFinalHeight=original.getHeight()*mCurrentBackground.getWidth()/original.getWidth();
int adjustment=0;
if (scaledBitmapFinalHeight>mCurrentBackground.getHeight()) {
// as expected, we have to crop up&down to maintain aspect ratio
adjustment=(int)(scaledBitmapFinalHeight-mCurrentBackground.getHeight()) / 4;
}
Rect srect=new Rect(0,adjustment,original.getWidth(), original.getHeight()-adjustment);
Rect drect=new Rect(0,0,mCurrentBackground.getWidth(), mCurrentBackground.getHeight());
canvas.drawBitmap(original, srect, drect, mPaint);
Log.v(TAG, "Created bitmap for background : Size: "+mCurrentBackground.getByteCount()+", w="+mCurrentBackground.getWidth()+", h="+mCurrentBackground.getHeight());
// precalc factor multiplier
mCurrentFactorMultiplier=(FACTOR-1)*getWidth()/100;
original.recycle();
System.gc();
invalidate();
}
private void setBackgroundDrawable_postscale (Drawable d) {
mCurrentBackground=((BitmapDrawable) d).getBitmap();
int currentBackgroundWidth=mCurrentBackground.getWidth(),
currentBackgroundHeight=mCurrentBackground.getHeight(),
currentFactorHeight=(int) (currentBackgroundHeight/FACTOR);
mCurrentFactorWidth=(int) (currentBackgroundWidth/FACTOR);
mCurrentFactorMultiplier=(FACTOR-1)*currentBackgroundWidth/100;
mRectDestination=new Rect(0,0,getWidth(), getHeight());
mRectSource=new Rect(0,0,mCurrentFactorWidth,currentFactorHeight);
invalidate();
}
@Override
public void onDraw(Canvas canvas) {
if ((isParallax) && (mCurrentBackground!=null)) {
if (mParallaxMode==MODE_POSTSCALE) onDraw_postscale(canvas); else onDraw_prescale(canvas);
} else super.onDraw(canvas);
}
private void onDraw_prescale(Canvas canvas) {
int oxb=(int) (mCurrentFactorMultiplier*mOffsetPercent);
canvas.drawBitmap(mCurrentBackground, -oxb, 0, mPaint);
}
private void onDraw_postscale(Canvas canvas) {
int oxb=(int) (mCurrentFactorMultiplier*mOffsetPercent);
mRectSource.left=oxb;
mRectSource.right=mCurrentFactorWidth+oxb;
canvas.drawBitmap(mCurrentBackground,mRectSource,mRectDestination, mPaint);
}
private void construct(Context context) {
mPaint=new Paint();
}
}
//// EOF ParallaxBackground.java
注意:您可以通过编程方式或在 XML 中实例化 ParallaxBackground。只要确保它在viewpager后面。要在 XML 中实例化它,您不需要做特殊的事情:
<com.regaliz.gui.views.ParallaxBackground
android:id="@+id/masterBackground"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
然后您可以像使用任何其他视图一样使用该组件
ParallaxBackground back=findViewById(R.id.masterBackground);
back.setBackgroundDrawable(R.drawable.your_cool_drawable);
注意 2:如果您使用 Jelly Bean API,您会看到 SetBackgroundDrawable(Drawable d) 已被替换为 setBackground(Drawable d) 。我目前还没有使用 JB api,但您所要做的就是将 setBackgroundDrawable 重命名为 setBackground。** 这个很重要 **
注 3: ParallaxBackgroundView 有 2 种模式:MODE_PRESCALE 和 MODE_POSTSCALE。模式 PRESCALE 缩放位图并始终将其保存在内存中,因此 onDraw 应该更快。模式 POSTSCALE 不做任何预缩放,而是在 onDraw() 上完成缩放。这相当慢,但它可能适用于无法在内存中保存巨大位图的低内存设备。
希望能帮助到你!
顺便说一句,我一直对优化我的代码感兴趣,所以如果有人有很好的建议,特别是性能或内存相关,或者增强这个类,请发布!
一种方法是扩展 ViewPager。已经有人做过了,你可以在github上查看代码。