studio property intent custom animations animate activity android android-listview android-animation

property - custom animation android



AnimaciĆ³n de desplazamiento del elemento ListView(similar a "UIKit Dynamics") (7)

Intento animar los elementos ListView cuando ocurre un desplazamiento. Más específicamente, estoy tratando de emular las animaciones de desplazamiento de la aplicación iMessage en iOS 7. Encontré un ejemplo similar en online :

Para aclarar, estoy tratando de lograr el efecto de movimiento "fluido" en los elementos cuando el usuario se desplaza, no la animación cuando se agrega un nuevo elemento. BaseAdapter modificar las Vistas en mi BaseAdapter y busqué en el AbsListView fuente de AbsListView para ver si podía adjuntar de alguna manera un AccelerateInterpolator algún lugar que pudiera ajustar las coordenadas de dibujo enviadas a las Vistas de los niños (si así es como está diseñado AbsListView ) No he podido avanzar hasta ahora.

¿Alguien tiene alguna idea de cómo replicar este comportamiento?

Para que el registro ayude con Google: esto se llama "UIKit Dynamics" en iOS.

Cómo replicar mensajes rebotando burbujas en iOS 7

Está incorporado a las últimas versiones de iOS. Sin embargo, todavía es algo difícil de usar. (2014) Esta es la publicación que todo el mundo copia: artículo ampliamente copiado Sorprendentemente, UIKit Dynamics solo está disponible en la "vista de colección" de Apple, no en la "vista de tabla" de Apple, por lo que todos los debs iOS tienen que convertir cosas de la vista de tabla "vista de colección"

La biblioteca que todo el mundo está utilizando como punto de partida es BPXLFlowLayout , ya que esa persona prácticamente descifró copiar la sensación de la aplicación de mensajes de texto de iphone. De hecho, si tuvieras que portarlo a Android, creo que podrías usar los parámetros para obtener la misma sensación. FYI noté en mi colección de Android Fone, teléfonos HTC tienen este efecto, en su interfaz de usuario. Espero eso ayude. ¡Rocas de Android!


Como queremos que los elementos aparezcan cada vez que aparecen en la parte superior o inferior de nuestra lista, el mejor lugar para hacerlo es el método getView () del adaptador:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { animatePostHc(position, v); } else { animatePreHc(position, v); }


Esta implementación funciona bastante bien. Sin embargo, hay algunos parpadeos, probablemente debido a los índices alterados cuando el adaptador agrega nuevas vistas a la parte superior o inferior ... Eso podría resolverse posiblemente observando los cambios en el árbol y cambiando los índices sobre la marcha.

public class ElasticListView extends GridView implements AbsListView.OnScrollListener, View.OnTouchListener { private static int SCROLLING_UP = 1; private static int SCROLLING_DOWN = 2; private int mScrollState; private int mScrollDirection; private int mTouchedIndex; private View mTouchedView; private int mScrollOffset; private int mStartScrollOffset; private boolean mAnimate; private HashMap<View, ViewPropertyAnimator> animatedItems; public ElasticListView(Context context) { super(context); init(); } public ElasticListView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ElasticListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { mScrollState = SCROLL_STATE_IDLE; mScrollDirection = 0; mStartScrollOffset = -1; mTouchedIndex = Integer.MAX_VALUE; mAnimate = true; animatedItems = new HashMap<>(); this.setOnTouchListener(this); this.setOnScrollListener(this); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (mScrollState != scrollState) { mScrollState = scrollState; mAnimate = true; } if (scrollState == SCROLL_STATE_IDLE) { mStartScrollOffset = Integer.MAX_VALUE; mAnimate = true; startAnimations(); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (mScrollState == SCROLL_STATE_TOUCH_SCROLL) { if (mStartScrollOffset == Integer.MAX_VALUE) { mTouchedView = getChildAt(mTouchedIndex - getPositionForView(getChildAt(0))); if (mTouchedView == null) return; mStartScrollOffset = mTouchedView.getTop(); } else if (mTouchedView == null) return; mScrollOffset = mTouchedView.getTop() - mStartScrollOffset; int tmpScrollDirection; if (mScrollOffset > 0) { tmpScrollDirection = SCROLLING_UP; } else { tmpScrollDirection = SCROLLING_DOWN; } if (mScrollDirection != tmpScrollDirection) { startAnimations(); mScrollDirection = tmpScrollDirection; } if (Math.abs(mScrollOffset) > 200) { mAnimate = false; startAnimations(); } Log.d("test", "direction:" + (mScrollDirection == SCROLLING_UP ? "up" : "down") + ", scrollOffset:" + mScrollOffset + ", toucheId:" + mTouchedIndex + ", fvisible:" + firstVisibleItem + ", " + "visibleItemCount:" + visibleItemCount + ", " + "totalCount:" + totalItemCount); int indexOfLastAnimatedItem = mScrollDirection == SCROLLING_DOWN ? getPositionForView(getChildAt(0)) + getChildCount() : getPositionForView(getChildAt(0)); //check for bounds if (indexOfLastAnimatedItem >= getChildCount()) { indexOfLastAnimatedItem = getChildCount() - 1; } else if (indexOfLastAnimatedItem < 0) { indexOfLastAnimatedItem = 0; } if (mScrollDirection == SCROLLING_DOWN) { setAnimationForScrollingDown(mTouchedIndex - getPositionForView(getChildAt(0)), indexOfLastAnimatedItem, firstVisibleItem); } else { setAnimationForScrollingUp(mTouchedIndex - getPositionForView(getChildAt(0)), indexOfLastAnimatedItem, firstVisibleItem); } if (Math.abs(mScrollOffset) > 200) { mAnimate = false; startAnimations(); mTouchedView = null; mScrollDirection = 0; mStartScrollOffset = -1; mTouchedIndex = Integer.MAX_VALUE; mAnimate = true; } } } private void startAnimations() { for (ViewPropertyAnimator animator : animatedItems.values()) { animator.start(); } animatedItems.clear(); } private void setAnimationForScrollingDown(int indexOfTouchedChild, int indexOflastAnimatedChild, int firstVisibleIndex) { for (int i = indexOfTouchedChild + 1; i <= indexOflastAnimatedChild; i++) { View v = getChildAt(i); v.setTranslationY((-1f * mScrollOffset)); if (!animatedItems.containsKey(v)) { animatedItems.put(v, v.animate().translationY(0).setDuration(300).setStartDelay(50 * i)); } } } private void setAnimationForScrollingUp(int indexOfTouchedChild, int indexOflastAnimatedChild, int firstVisibleIndex) { for (int i = indexOfTouchedChild - 1; i > 0; i--) { View v = getChildAt(i); v.setTranslationY((-1 * mScrollOffset)); if (!animatedItems.containsKey(v)) { animatedItems.put(v, v.animate().translationY(0).setDuration(300).setStartDelay(50 * (indexOfTouchedChild - i))); } } } @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: Rect rect = new Rect(); int childCount = getChildCount(); int[] listViewCoords = new int[2]; getLocationOnScreen(listViewCoords); int x = (int)event.getRawX() - listViewCoords[0]; int y = (int)event.getRawY() - listViewCoords[1]; View child; for (int i = 0; i < childCount; i++) { child = getChildAt(i); child.getHitRect(rect); if (rect.contains(x, y)) { mTouchedIndex = getPositionForView(child); break; } } return false; } return false; } }


Honestamente, va a ser mucho trabajo y matemáticamente intenso, pero hubiera pensado que podría hacer que los diseños de los elementos de la lista tengan un relleno superior e inferior y que pueda ajustar ese relleno para cada elemento para que los elementos individuales se vuelvan más o menos Espaciado. Cómo harías un seguimiento de cuánto y cómo sabrías la velocidad a la que se desplazan los artículos, esa sería la parte difícil.


Me tomó solo unos minutos explorar esto y parece que se puede hacer bastante fácilmente con API 12 y superior (con suerte no me falta algo ...). Para obtener el efecto de tarjeta muy básico, basta con un par de líneas de código al final de getView () en su Adaptador justo antes de devolverlo a la lista. Aquí está el adaptador completo:

public class MyAdapter extends ArrayAdapter<String>{ private int mLastPosition; public MyAdapter(Context context, ArrayList<String> objects) { super(context, 0, objects); } private class ViewHolder{ public TextView mTextView; } @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { holder = new ViewHolder(); convertView = LayoutInflater.from(getContext()).inflate(R.layout.grid_item, parent, false); holder.mTextView = (TextView) convertView.findViewById(R.id.checkbox); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.mTextView.setText(getItem(position)); // This tells the view where to start based on the direction of the scroll. // If the last position to be loaded is <= the current position, we want // the views to start below their ending point (500f further down). // Otherwise, we start above the ending point. float initialTranslation = (mLastPosition <= position ? 500f : -500f); convertView.setTranslationY(initialTranslation); convertView.animate() .setInterpolator(new DecelerateInterpolator(1.0f)) .translationY(0f) .setDuration(300l) .setListener(null); // Keep track of the last position we loaded mLastPosition = position; return convertView; } }

Tenga en cuenta que estoy realizando un seguimiento de la última posición que se va a cargar (mLastPosition) para determinar si animar las vistas desde la parte inferior (si se desplaza hacia abajo) o hacia abajo desde la parte superior (si estamos desplazándose hacia arriba).

Lo maravilloso es que puede hacer mucho más simplemente modificando las propiedades iniciales de convertView (por ejemplo, convertView.setScaleX (escala flotante)) y la cadena convertView.animate () (por ejemplo, .scaleX (float)).


Por lo que entiendo, lo que estás buscando es un efecto de paralaje.

Esta respuesta es realmente completa y creo que eso puede ayudarte mucho.


Pruebe esto poniendo esto en su método getView () justo antes de devolver su convertView:

Animation animationY = new TranslateAnimation(0, 0, holder.llParent.getHeight()/4, 0); animationY.setDuration(1000); Yourconvertview.startAnimation(animationY); animationY = null;

Donde llParent = RootLayout que consiste en su artículo de fila personalizado.