Cómo animar ImageView de centro de cultivo para llenar la pantalla y viceversa (estilo de facebook)?
Fondo
La aplicación de Facebook tiene una agradable animación de transición entre una pequeña imagen en una entrada, y un modo ampliado de la misma que el usuario también puede ampliar.
Según lo veo, la animación no sólo amplía y mueve la imagen de acuerdo con su ubicación anterior y el tamaño, sino que también revela el contenido en lugar de estirar el contenido de la imageView.
- Colocar una vista de imagen como imagen de superposición en la vista previa de la cámara
- Android: ¿Cómo iniciar una animación infinita aplicada en un ImageView después de la actividad con la vista animada se ha reanudado?
- Uri regresó después de ACTION_GET_CONTENT de la galería no está trabajando en setImageURI () de ImageView
- Android: establece ImageView en la URL
- android arrastrar y soltar ImageView onTouchListener
Esto se puede ver usando el siguiente boceto que he hecho:
La pregunta
¿Cómo lo hicieron? ¿Realmente tienen 2 vistas de animación para revelar el contenido?
¿Cómo lo hicieron tan fluido como si fuera una sola vista?
El único tutorial que he visto (enlace aquí ) de una imagen que se agranda a pantalla completa no se muestra bien cuando la miniatura se establece en centro de cultivo.
No sólo eso, sino que funciona incluso en baja API de Android.
¿Alguien sabe de una biblioteca que tiene una capacidad similar?
EDIT: He encontrado una manera y publicado una respuesta , pero se basa en cambiar el layoutParams, y creo que no es eficiente y recomendado.
He intentado usar las animaciones normales y otros trucos de animación, pero por ahora eso es lo único que funcionó para mí.
Si alguien sabe qué hacer para que funcione mejor, escríbalo.
- Ajuste ImageView sin alargar la imagen
- Cómo rellenar la imagen dentro de ImageView por la costumbre de la izquierda y la coordenada superior | Androide
- GridView con zoom
- Tratando de obtener el tamaño de visualización de una imagen en un ImageView
- ImageView personalizado con sombra
- Superposición de texto sobre imageview en framelayout mediante programación - Android
- Cómo establecer la imagen de url para imageView
- ¿Cómo puedo actualizar la imagen de fresco SimpleDraweeView, si se estableció una vez por setImageURI
Ok, he encontrado una manera posible de hacerlo. He hecho el layoutParams como variables que siguen cambiando usando ObjectAnimator de la biblioteca nineOldAndroids . Creo que no es la mejor manera de lograrlo ya que causa una gran cantidad de onDraw y onLayout, pero si el contenedor tiene sólo unas pocas vistas y no cambia su tamaño, tal vez está bien.
La suposición es que el imageView que animar tomará el tamaño exacto necesario al final y que (actualmente) tanto la miniatura como la imagen animada tienen el mismo contenedor (pero debería ser fácil cambiarlo).
Como he probado, también es posible agregar funciones de zoom mediante la extensión de la clase TouchImageView . Usted apenas fija el tipo de la escala en el principio al centro-cosecha, y cuando la animación termina usted la fija de nuevo a la matriz, y si usted quiere, usted puede poner el layoutParams para llenar el envase entero (y fijar el margen a 0.0 ).
También me pregunto cómo es que el AnimatorSet no funcionó para mí, así que voy a mostrar aquí algo que funciona, con la esperanza de que alguien me podría decir lo que debo hacer.
Aquí está el código:
MainActivity.java
public class MainActivity extends Activity { private static final int IMAGE_RES_ID = R.drawable.test_image_res_id; private static final int ANIM_DURATION = 5000; private final Handler mHandler = new Handler(); private ImageView mThumbnailImageView; private CustomImageView mFullImageView; private Point mFitSizeBitmap; @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mFullImageView = (CustomImageView) findViewById(R.id.fullImageView); mThumbnailImageView = (ImageView) findViewById(R.id.thumbnailImageView); mHandler.postDelayed(new Runnable() { @Override public void run() { prepareAndStartAnimation(); } }, 2000); } private void prepareAndStartAnimation() { final int thumbX = mThumbnailImageView.getLeft(), thumbY = mThumbnailImageView.getTop(); final int thumbWidth = mThumbnailImageView.getWidth(), thumbHeight = mThumbnailImageView.getHeight(); final View container = (View) mFullImageView.getParent(); final int containerWidth = container.getWidth(), containerHeight = container.getHeight(); final Options bitmapOptions = getBitmapOptions(getResources(), IMAGE_RES_ID); mFitSizeBitmap = getFitSize(bitmapOptions.outWidth, bitmapOptions.outHeight, containerWidth, containerHeight); mThumbnailImageView.setVisibility(View.GONE); mFullImageView.setVisibility(View.VISIBLE); mFullImageView.setContentWidth(thumbWidth); mFullImageView.setContentHeight(thumbHeight); mFullImageView.setContentX(thumbX); mFullImageView.setContentY(thumbY); runEnterAnimation(containerWidth, containerHeight); } private Point getFitSize(final int width, final int height, final int containerWidth, final int containerHeight) { int resultHeight, resultWidth; resultHeight = height * containerWidth / width; if (resultHeight <= containerHeight) { resultWidth = containerWidth; } else { resultWidth = width * containerHeight / height; resultHeight = containerHeight; } return new Point(resultWidth, resultHeight); } public void runEnterAnimation(final int containerWidth, final int containerHeight) { final ObjectAnimator widthAnim = ObjectAnimator.ofInt(mFullImageView, "contentWidth", mFitSizeBitmap.x) .setDuration(ANIM_DURATION); final ObjectAnimator heightAnim = ObjectAnimator.ofInt(mFullImageView, "contentHeight", mFitSizeBitmap.y) .setDuration(ANIM_DURATION); final ObjectAnimator xAnim = ObjectAnimator.ofInt(mFullImageView, "contentX", (containerWidth - mFitSizeBitmap.x) / 2).setDuration(ANIM_DURATION); final ObjectAnimator yAnim = ObjectAnimator.ofInt(mFullImageView, "contentY", (containerHeight - mFitSizeBitmap.y) / 2).setDuration(ANIM_DURATION); widthAnim.start(); heightAnim.start(); xAnim.start(); yAnim.start(); // TODO check why using AnimatorSet doesn't work here: // final com.nineoldandroids.animation.AnimatorSet set = new AnimatorSet(); // set.playTogether(widthAnim, heightAnim, xAnim, yAnim); } public static BitmapFactory.Options getBitmapOptions(final Resources res, final int resId) { final BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); bitmapOptions.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, bitmapOptions); return bitmapOptions; } }
Activity_main.xml
<RelativeLayout 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" tools:context=".MainActivity" > <com.example.facebookstylepictureanimationtest.CustomImageView android:id="@+id/fullImageView" android:layout_width="0px" android:layout_height="0px" android:background="#33ff0000" android:scaleType="centerCrop" android:src="@drawable/test_image_res_id" android:visibility="invisible" /> <ImageView android:id="@+id/thumbnailImageView" android:layout_width="100dp" android:layout_height="100dp" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:scaleType="centerCrop" android:src="@drawable/test_image_res_id" /> </RelativeLayout>
CustomImageView.java
public class CustomImageView extends ImageView { public CustomImageView(final Context context) { super(context); } public CustomImageView(final Context context, final AttributeSet attrs) { super(context, attrs); } public CustomImageView(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); } public void setContentHeight(final int contentHeight) { final LayoutParams layoutParams = getLayoutParams(); layoutParams.height = contentHeight; setLayoutParams(layoutParams); } public void setContentWidth(final int contentWidth) { final LayoutParams layoutParams = getLayoutParams(); layoutParams.width = contentWidth; setLayoutParams(layoutParams); } public int getContentHeight() { return getLayoutParams().height; } public int getContentWidth() { return getLayoutParams().width; } public int getContentX() { return ((MarginLayoutParams) getLayoutParams()).leftMargin; } public void setContentX(final int contentX) { final MarginLayoutParams layoutParams = (MarginLayoutParams) getLayoutParams(); layoutParams.leftMargin = contentX; setLayoutParams(layoutParams); } public int getContentY() { return ((MarginLayoutParams) getLayoutParams()).topMargin; } public void setContentY(final int contentY) { final MarginLayoutParams layoutParams = (MarginLayoutParams) getLayoutParams(); layoutParams.topMargin = contentY; setLayoutParams(layoutParams); } }
Otra solución, si sólo quieres hacer una animación de una imagen de pequeña a grande, puedes probar ActivityOptions.makeThumbnailScaleUpAnimation o makeScaleUpAnimation y ver si les conviene.
http://developer.android.com/reference/android/app/ActivityOptions.html#makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int)
He encontrado una manera de obtener un efecto similar en un prototipo rápido. Puede que no sea adecuado para uso en producción (todavía estoy investigando), pero es rápido y fácil.
-
Utilice una transición de fade en su transición de actividad / fragmento (que comienza con ImageView exactamente en la misma posición). La versión del fragmento:
final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); ...etc
La versión de la actividad:
Intent intent = new Intent(context, MyDetailActivity.class); startActivity(intent); getActivity().overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
Esto da una transición suave sin parpadeo.
-
Ajuste los diseños de forma dinámica en el onStart () del nuevo fragmento (necesita guardar los campos de miembro en las partes apropiadas de su interfaz de usuario en onCreateView y agregar algunos indicadores para garantizar que este código sólo se llama una vez).
@Override public void onStart() { super.onStart(); // Remove the padding on any layouts that the image view is inside mMainLayout.setPadding(0, 0, 0, 0); // Get the screen size using a utility method, eg // http://stackoverflow.com/a/12082061/112705 // then work out your desired height, eg using the image aspect ratio. int desiredHeight = (int) (screenWidth * imgAspectRatio); // Resize the image to fill the whole screen width, removing // any layout margins that it might have (you may need to remove // padding too) LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(screenWidth, desiredHeight); layoutParams.setMargins(0, 0, 0, 0); mImageView.setLayoutParams(layoutParams); }
Creo que la forma más fácil es animar la altura del ImageView (una imagen normal, no es necesario una vista personalizada) mientras se mantiene el scaleType a centerCrop hasta altura completa, que se puede saber de antemano si se establece la altura de la imagen en wrap_content en su Y luego usar un ViewTreeObserver para saber cuando el diseño ha terminado, por lo que puede obtener la altura de ImageView y, a continuación, establecer el nuevo "colapso" de altura. No lo he probado, pero es así como lo haría.
También puede echar un vistazo a este post, hacen algo similar http://nerds.airbnb.com/host-experience-android/
No estoy seguro de por qué todo el mundo está hablando sobre el marco. El uso de código de otras personas puede ser grande a veces; Pero suena como lo que está buscando es el control preciso de la mirada. Al obtener acceso al contexto gráfico puede tener eso. La tarea es bastante simple en cualquier entorno que tenga un contexto gráfico. En android puedes obtenerlo reemplazando el método onDraw
y usando el Objeto Canvas. Tiene todo lo que necesita para dibujar una imagen en diferentes escalas, posiciones y recortes. Incluso puedes usar una matriz si estás familiarizado con ese tipo de cosas.
Pasos
-
Asegúrese de tener el control exacto de posicionamiento, escala y clip. Esto significa desactivar cualquier disposición o alineación automática que se pueda configurar dentro del contenedor de objetos.
-
Imagínate cuál será tu parámetro
t
para la interpolación lineal y cómo quieres que se relacione con el tiempo. ¿Qué tan rápido o lento, y habrá algún alivio.t
debe depender del tiempo. -
Después de que las miniaturas se almacenen en caché, cargue la imagen a escala completa en el fondo. Pero no lo muestres todavía.
-
Cuando se dispara el disparador de animación, muestre la imagen grande y conduzca su animación con el parámetro
t
utilizando la interpolación entre los estados de las propiedades iniciales en los estados de las propiedades finales. Haga esto para las tres propiedades, posición, escala y clip. Así que para todas las propiedades haga lo siguiente:Sinterpolated = Sinitial * (t-1) + Sfinal * t; // where // t is between 0.0 and 1.0 // and S is the states value // for every part of scale, position, and clip // // Sinitial is what you are going from // Sfinal is what you are going to // // t should change from 0.0->1.0 in // over time anywhere from 12/sec or 60/sec.
Si todas las propiedades están controladas por el mismo parámetro, la animación será suave. Como una ventaja añadida, aquí hay una sugerencia para la sincronización. Siempre y cuando se puede mantener su parámetro t
entre 0 y 1, la facilitación dentro o fuera puede ser hackeado con una línea de código:
// After your t is all setup t = t * t; // for easing in // or t = Math.sqrt(t); // for easing out
Puedes lograr esto a través de Transition
Api, y este es el gif resut:
Siguiente:
private void zoomIn() { ViewGroup.LayoutParams layoutParams = mImage.getLayoutParams(); int width = layoutParams.width; int height = layoutParams.height; layoutParams.width = (int) (width * 2); layoutParams.height = height * 2; mImage.setLayoutParams(layoutParams); mImage.setScaleType(ImageView.ScaleType.FIT_CENTER); TransitionSet transitionSet = new TransitionSet(); Transition bound = new ChangeBounds(); transitionSet.addTransition(bound); Transition changeImageTransform = new ChangeImageTransform(); transitionSet.addTransition(changeImageTransform); transitionSet.setDuration(1000); TransitionManager.beginDelayedTransition(mRootView, transitionSet); }
Ver la demo en github
Sdk version> = 21
- Android – Cómo pulsar el elemento ListView mediante programación
- Android AlertDialog con texto dinámicamente cambiante en cada solicitud