Полоса хода поворота в каждом элементе списка

Я долго пробовал эту тему и искал ответ без всякой удачи! Это кажется тривиальным, но, насколько я знаю, это не так.

Я использую listview в своем приложении для Android, где каждый элемент (представление) отображает вращающийся ProgressBar до содержимое загружается и отображается (содержимое извлекается через HTTP-вызовы и json, поэтому для обработки может потребоваться некоторое время). Проблема в том, что полосы хода вращающегося вращения вращаются независимо друг от друга, тем самым создавая эффект завихрения хаос, а не синхронизированный ряд замечательных маркеров загрузки.

Я пробовал все, что мог придумать... не перерабатывать ProgressBar в getView()... есть только один экземпляр того же ProgressBar... сброс ProgressBars android: прогресс, когда элементы списка становятся видимыми (через onScroll() в активности)... и т.д. но поскольку они начинают вращаться во время создания (когда getView вызывается в адаптере), они никогда не будут иметь одинаковую синхронизацию цикла.

enter image description here

Любые предложения приветствуются!

Ответ 1

EDIT: теперь отлично работает! - вставил вызов прослушивателя анимации setStartOffset() обратно в 0 после первого повтора, чтобы он не "случайно прыгал".


Я нашел рабочее решение для этой проблемы, которое работает, привязывая анимацию к текущей системе миллисекунд. Это немного взломать, поскольку он использует отражение, чтобы получить поле mAnimation в ProgressBar. При этом это поле сохранилось в источниках Android с момента его создания (работает до 4.2).

Создайте класс android.widget.SyncedProgressBar и используйте его вместо ProgressBar в ваших XML файлах. По сути, анимация начинается с начала анимации. Вы также можете поиграть с setDuration(500), чтобы убедиться, что он работает (колесо хода вращается очень быстро). Надеюсь, это поможет!

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

}

Ответ 2

Вот как это сделать. Используйте только один AnimationDrawable, мультиплексируйте обратный вызов. ProgressBar ничего не добавляет, вы можете также придерживаться View. Создайте подкласс View, который обходит управление 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();
   }
}

Затем сделайте свою деятельность каким-то образом отслеживайте все вихри.

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

Ответ 3

В вашем адаптере getView() отключите ваш progressViwe, а затем для каждого progressView

handler.postAtTime(new Runnable(){

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

Что это будет делать, так это одновременно включить все ваши progressViews. Это может сработать, я не тестировал. Если это не сработает, вы можете добавить динамический динамический прогресс (не включен в макет, но добавить через addView()). Но делайте это в runnable, ключ здесь, так что все они добавляются одновременно.

Ответ 4

Копаясь в исходном коде, я вижу, что вращающееся изображение является поворотным, которое вращается из-за частного Animation. Невозможно получить представление или анимацию, отбросив любую идею, которую я использовал для повторного использования одной анимации во всех представлениях. Я бы сказал, что ваша единственная альтернатива - это то, что предложил Jug6ernaut, хотя это не самые красивые решения.

Ответ 5

Я не уверен, что это возможно, учитывая, что каждая строка вашего ListView перерабатывается, когда она перемещается за пределы экрана. Когда на экране появляется новая строка, все представления ProgressBar должны быть повторно синхронизированы друг с другом. Сохранение всех синхронизируемых строк будет большой PITA.

Рассматривали ли вы загрузку всех данных списка в один запрос и кэширование этих данных на локальное хранилище? Это было бы намного более эффективно и приводило бы к лучшему опыту пользователя. Не очень полезно иметь ListView, который немедленно загружает пустые строки. Я предпочел бы, чтобы список полностью заполнил.

Ответ 6

1 > Создать макет для строки списка

<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 > Затем в методе 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);
            }