studio library bar android listview asynchronous progress-bar cycle

studio - progress bar library android



Girar barra de progreso en cada elemento de la vista de lista (6)

¡Hace tiempo que me estoy rascando la cabeza por esto y busqué una respuesta sin suerte! Parece ser trivial, pero hasta donde yo sé, no lo es.

Utilizo una vista de lista en mi aplicación Android donde cada elemento (vista) muestra una ProgressBar giratoria antes de cargar y mostrar el contenido (el contenido se recupera mediante llamadas http y json, por lo que puede llevar un tiempo procesar). El problema es que las barras de progreso giratorias giran independientemente unas de otras, creando así el efecto de un caos giratorio en lugar de una fila sincronizada de marcadores de carga atractivos.

He intentado todo lo que pude imaginar ... no reciclar ProgressBar en getView () ... tener solo una instancia de la misma ProgressBar ... reiniciar ProgressBars android: progresar siempre que los elementos de la lista se vuelvan visibles (a través deScroll () en la actividad) ... etc, pero como comienzan a girar en el tiempo de creación (cuando se llama a getView en el adaptador) nunca tendrán la misma sincronización de ciclo.

Cualquier sugerencia es bienvenida!


En su adaptador getView () deshabilita su progressViwe, y luego para cada progressView

handler.postAtTime(new Runnable(){ public void run(){ progressView.setEnabled(true); }}, someTimeInTheFuture );

Lo que hará es habilitar todas sus progressViews al mismo tiempo. Esto podría funcionar, no lo he probado. Si esto no funciona, puede agregar progressView dinámicamente (no incluido en el diseño, pero agregar a través de addView ()). Pero hágalo en el ejecutable, la clave aquí es para que todos se agreguen al mismo tiempo.


Excavando a través del código fuente veo que la imagen que gira es dibujable y gira debido a una Animation privada. No hay forma de obtener la vista o la animación, descartando así cualquier idea que tenga de reutilizar una animación en todas las vistas. Diría que su única alternativa es lo que sugirió Jug6ernaut, a pesar de que no es la solución más bonita.


He aquí cómo hacerlo funcionar. Use solo una AnimaciónDibujada, multiplexe la devolución de llamada. ProgressBar realmente no agrega nada, es mejor que se quede con View. Cree una subclase de Vista que evite la administración de Drawable.

public class Whirly extends View { Drawable image = null; public Whirly(Context context, AttributeSet attr) { super(context, attr); } @Override public void draw(Canvas canvas) { if (image!=null) image.draw(canvas); } public void setImageDrawable(Drawable image) { this.image = image; invalidate(); } }

Luego haga que su actividad haga un seguimiento de todos los torbellinos de alguna manera.

public class Demo extends Activity implements Drawable.Callback { private Handler h; private AnimationDrawable a; private Whirly w0; private Whirly w1; private Whirly w2; private Whirly w3; public void onCreate(Bundle bundle) { ... h = new Handler(); a = (AnimationDrawable) getResources().getDrawable(R.drawable.mywhirly); int width = a.getIntrinsicWidth(); int height = a.getIntrinsicHeight(); a.setBounds(0, 0, width, height); w0 = (Whirly) findViewById(R.id.whirly0); w1 = (Whirly) findViewById(R.id.whirly1); w2 = (Whirly) findViewById(R.id.whirly2); w3 = (Whirly) findViewById(R.id.whirly3); w0.setImageDrawable(a); w1.setImageDrawable(a); w2.setImageDrawable(a); w3.setImageDrawable(a); a.setCallback(this); a.start(); } public void invalidateDrawable (Drawable who) { w0.invalidate(); w1.invalidate(); w2.invalidate(); w3.invalidate(); } public void scheduleDrawable (Drawable who, Runnable what, long when) { h.postAtTime(what, who, when); } public void unscheduleDrawable (Drawable who, Runnable what) { h.removeCallbacks(what, who); } }


No estoy seguro de que esto sea posible dado que cada fila en su ListView se recicla cuando se mueve fuera de la pantalla. Cuando aparece una nueva fila en la pantalla, todas las vistas de ProgressBar deben volver a sincronizarse entre sí. Mantener sincronizadas todas las filas va a ser un gran PITA.

¿Ha considerado cargar todos los datos de la lista en una sola solicitud y almacenar esos datos en caché en el almacenamiento local? Sería mucho más eficiente y daría como resultado una mejor experiencia de usuario. No es muy útil tener un ListView que se carga inmediatamente con filas vacías. Prefiero esperar a que la lista se complete por completo.


EDITAR: ¡Ahora funciona a la perfección! - Se insertó la llamada de escucha de animación setStartOffset () en 0 después de la primera repetición para que no siga "saltando" aleatoriamente.

Encontré una solución de trabajo para este problema, que funciona sincronizando la animación con los milisegundos del sistema actual. Es un poco complicado, ya que utiliza la reflexión para obtener el campo de mAnimation en ProgressBar . Dicho esto, este campo se ha mantenido en su lugar en las fuentes de Android desde que se creó (funciona hasta 4.2).

Cree la clase android.widget.SyncedProgressBar y utilícela en lugar de ProgressBar en sus archivos .xml. En esencia, hace que la animación comience al comienzo de la duración de la animación. También puedes jugar con setDuration(500) para verificar que funcione (la rueda de progreso girará muy rápido). ¡Espero que esto ayude!

package android.widget; import android.annotation.SuppressLint; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import java.lang.reflect.Field; /** * @author Oleg Vaskevich * */ public class SyncedProgressBar extends ProgressBar { public SyncedProgressBar(Context context) { super(context); modifyAnimation(); } public SyncedProgressBar(Context context, AttributeSet attrs) { super(context, attrs); modifyAnimation(); } public SyncedProgressBar(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); modifyAnimation(); } @Override public void setVisibility(int v) { super.setVisibility(v); modifyAnimation(); } @SuppressLint("NewApi") @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); modifyAnimation(); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); modifyAnimation(); } @Override public synchronized void setIndeterminate(boolean indeterminate) { super.setIndeterminate(indeterminate); modifyAnimation(); } public void modifyAnimation() { Field mAnim; try { mAnim = ProgressBar.class.getDeclaredField("mAnimation"); mAnim.setAccessible(true); AlphaAnimation anim = (AlphaAnimation) mAnim.get(this); if (anim == null) return; // set offset to that animations start at same time long duration = anim.getDuration(); long timeOffset = System.currentTimeMillis() % duration; anim.setDuration(duration); anim.setStartOffset(-timeOffset); anim.setAnimationListener(new AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { animation.setStartOffset(0); } @Override public void onAnimationEnd(Animation animation) { } }); } catch (Exception e) { Log.d("SPB", "that didn''t work out...", e); return; } postInvalidate(); } }


1> Crear diseño para la fila de la lista

<RelativeLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:padding="10dip" > <TextView android:id="@+id/word_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_centerVertical="true" android:text="Word 1" /> <ProgressBar android:id="@+id/row_progress" style="@style/Widget.Sherlock.Light.ProgressBar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:indeterminate="true" /> <ImageView android:id="@+id/speaker" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:src="@drawable/speaker_icon" android:visibility="gone"/> </RelativeLayout>

2> Luego en el método getView

if(word.getIsWordSynchedLocally() == 1){ convertView.findViewById(R.id.row_progress).setVisibility(View.GONE); convertView.findViewById(R.id.speaker).setVisibility(View.VISIBLE); }else{ convertView.findViewById(R.id.row_progress).setVisibility(View.VISIBLE); convertView.findViewById(R.id.speaker).setVisibility(View.GONE); }