Cómo tener una imagen más amplia desplazándose en el fondo

Al igual que en LinkedIn las primeras tres pantallas

  1. Chapoteo
  2. Botones de Inicio de Sesión / Registro
  3. Formulario de Inicio de Sesión / Inscripción

Todos tienen la misma imagen que el fondo, pero cuando nos movemos de una actividad a otra, la imagen de fondo se desplaza a la izquierda de la derecha.

Sólo pude probar con overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right); Pero eso no es lo que parece.

Introduzca aquí la descripción de la imagen

Introduzca aquí la descripción de la imagen

Introduzca aquí la descripción de la imagen

Esto se llama scroll de paralaje, y lo implementé usando 2 capas: una para el contenido y otra para el fondo. El contenido, se coloca en un ViewPager sin fondo. Tenga en cuenta que en lugar de las actividades que va a utilizar Fragmentos (cada página será un fragmento) que será animado por el viewpager. (Vea FragmentStatePagerAdapter)

El fondo va en una capa de fondo, obviamente detrás de viewpager e independiente de ella. Puede ser una imagen dentro de una vista de desplazamiento, o una imagen cuya región de recorte se moverá, o una imagen que se procese mediante drawBitmap (x, y). Por favor vea el código adjunto para mi solución, que extiende una vista cuyo fondo se puede desplazar simplemente llamando a un método "setPercent"

Entonces anulas

 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); } 

Y ahora la vista de fondo de paralaje, que va detrás de ViewPager. Publico aquí una versión completa y funcional de mi propio ParallaxBackgroundView. Esto es realmente código probado.

  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 

Nota : Puede instanciar el ParallaxBackground de forma programática o en el XML. Sólo asegúrese de que está detrás de la viewpager. Para instanciarlo en un XML no necesitas hacer cosas especiales:

 <com.regaliz.gui.views.ParallaxBackground android:id="@+id/masterBackground" android:layout_width="match_parent" android:layout_height="match_parent" /> 

A continuación, puede utilizar el componente como cualquier otra vista

 ParallaxBackground back=findViewById(R.id.masterBackground); back.setBackgroundDrawable(R.drawable.your_cool_drawable); 

Nota 2: Si está usando Jelly Bean API, verá que SetBackgroundDrawable (Drawable d) ha sido sustituido por setBackground (Drawable d). No uso JB api hasta ahora, pero todo lo que tienes que hacer es cambiar el nombre de setBackgroundDrawable a setBackground. ** Esto es importante **

Nota 3: El ParallaxBackgroundView tiene 2 modos: MODE_PRESCALE y MODE_POSTSCALE. Modo PRESCALE escala un mapa de bits y lo mantiene siempre en memoria, por lo que onDraw debería ser más rápido. Modo POSTSCALE no hace ninguna prescaling, en su lugar, la escala se realiza en onDraw (). Esto es bastante lento, pero puede ser útil para dispositivos de baja memoria que no pueden permitirse mantener un mapa de bits enorme en la memoria.

¡Espero eso ayude!

Por cierto estoy siempre interesado en la optimización de mi código, por lo que si alguien tiene una gran sugerencia, especialmente el rendimiento o la memoria relacionada, o mejora esta clase por favor publicarlo!

Una forma de hacerlo es ampliar ViewPager. Ya lo ha hecho alguien y puede comprobar el código en github .

  • ¿Cuál es la mejor solución para un salto de barra de progreso indeterminado en ICS?
  • OnGlobalLayoutListener en ListView
  • Layout_gravity no funciona dentro de LinearLayout
  • Tablayout de la biblioteca de soporte de diseño de Android mediante el diseño de pestañas personalizadas, pero el diseño que envuelve las pestañas
  • Android: cómo deshabilitar los controles durante la barra de progreso está activa
  • Cómo probar los valores de TextInputLayout (sugerencia, error, etc.) usando Android Espresso?
  • Android: Círculo de dibujo con el interior del texto
  • Coloque ImageView sobre el botón android
  • Vista de texto de Autocompletar de Twitter como Android
  • Cómo agregar imagen a spinner en Android
  • DrawerLayout con Google Maps
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.