2012-06-20 9 views
11

¡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.Barra de progreso de hilado en cada elemento de la vista de lista

Puedo utilizar una vista de lista en mi aplicación Android en el que cada elemento (ver) muestra una ProgressBar de girar antes de que se cargue el contenido y se presenta (el contenido se recupera a través de llamadas HTTP y JSON, lo que puede tardar un tiempo para procesar) . El problema es que las barras de progreso giratorias giran independientemente una de otra, creando así el efecto de un caos giratorio en lugar de una fila sincronizada de marcadores de carga atractivos.

He intentado todo lo que podía llegar a ... no reciclar el ProgressBar en getView() ... tienen sólo una instancia de la misma ProgressBar ... restablecer el androide ProgressBars: el progreso cada vez que los elementos de la lista se vuelve visible (mediante onScroll() en la actividad) ... etc, pero como empiezan 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.

enter image description here

Cualquier sugerencia es bienvenida!

+1

Muy buena pregunta! Tengo curiosidad por mí mismo. – st0le

+1

sí ... Excelente ... –

+0

(Asunción simple) Posible, debido a la lista de elementos dibujados uno por uno en la pantalla. Primero la barra de progreso de listitem comenzó primero. – user370305

Respuesta

2

EDIT: ¡Ahora funciona perfectamente! - la escucha de animación insertada establece setStartOffset() vuelve a 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 midiendo el tiempo de la animación de los milisegundos del sistema actual. Es un poco complicado, ya que utiliza la reflexión para obtener el campo 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).

Crea la clase android.widget.SyncedProgressBar y úsala en lugar de ProgressBar en tus 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(); 
    } 

} 
0

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

handler.postAtTime(new Runnable(){ 

public void run(){ 
    progressView.setEnabled(true); 
}}, someTimeInTheFuture 
); 

Lo que esto hará es permitir que todos 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.

1

Así es 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. Crea una subclase de vista que elude la administración 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 giros 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); 
    } 
} 
+0

He estado trabajando en su código por un tiempo y estoy progresando, al menos parece ser la dirección correcta :) Sin embargo, el problema es que tengo dificultades para implementar este código en mi adaptador. En su ejemplo, cada "whirly" ya está definido en algún recurso, y todos están configurados para usar el mismo AnimationDrawable. Quiero tener solo un whirly definido, que cada elemento de la lista en mi lista de lista compartirá. – maglar

+0

En su ListView, cada elemento debe tener un diseño completo con sus propias vistas. Para cada gráfico animado necesitarás una vista. Un Whirly es solo una envoltura muy delgada alrededor de la vista menos costosa (Vista). El XML del diseño del elemento simplemente tendría un LinearLayout horizontal, un TextView para "Tues 1" y un Whirly. Cuando se hace un ítem, whirly.setImageDrawable (null) y lo quita de la lista de devolución de llamada. onResume sería a.start(), onSuspend sería a.stop(), cuando el recuento de Whirlys en la lista de devolución de llamada va a cero a.stop(); – Renate

0

de excavación a través del código fuente veo que la imagen spinning es un estirable que está girando a causa de una Animation privado. 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.

+0

getIndeterminateDrawable funciona bien en cualquier caso. [link] (http://developer.android.com/reference/android/widget/ProgressBar.html#getIndeterminateDrawable%28%29) – Renate

0

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 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.

+0

No mucho de un PITA, mira mi respuesta –

0

1> Crear disposición para la línea de lista de

<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> A continuación, 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); 
      } 
Cuestiones relacionadas