Spinning progress bar in every listview item

后端 未结 6 519
日久生厌
日久生厌 2021-02-01 08:30

I\'ve been scratching my head over this for a long time now and searched for an answer without any luck! It seems to be trivial, but as far as I know, it isn\'t.

I use a

相关标签:
6条回答
  • 2021-02-01 08:52

    Digging through the source code I see that the spinning image is a drawable that is spinning because of a private Animation. There is no way to obtain the view or the animation, thus discarding any idea I had of reusing one animation on all the views. I'd say your only alternative is what Jug6ernaut suggested, even though it is not the prettiest of solutions.

    0 讨论(0)
  • 2021-02-01 08:55

    I'm not sure this is possible given that each row in your ListView is recycled when it moves off screen. When a new row appears on the screen the ProgressBar views will all have to be re-synchronized with each other. Keeping all of the rows in sync is going to be a big PITA.

    Have you considered loading all of the list data in one request and caching that data to local storage? It would be much more efficient and result in a better user experience. It's not very useful to have a ListView that loads immediately with empty rows. I'd rather wait for the list to populate entirely.

    0 讨论(0)
  • 2021-02-01 08:58

    Here's how to make it work. Use just one AnimationDrawable, multiplex the callback. ProgressBar doesn't really add anything, you might as well stick with View. Make a subclass of View that bypasses the Drawable management.

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

    Then make your activity keep track of all the whirlies somehow.

    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 讨论(0)
  • 2021-02-01 09:07

    In your adapter getView() disable your progressViwe's, and then for each progressView

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

    What this will do is enable all your progressViews at the same time. This might work, i have not tested. If this does not work then you may want to add the progressView dynamically(not included in the layout, but add via addView()). But do so in the runnable, the key here is so they all get added at the same time.

    0 讨论(0)
  • 2021-02-01 09:10

    1> Create Layout for list row

    <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> Then in getView method

    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);
                }
    
    0 讨论(0)
  • 2021-02-01 09:14

    EDIT: Now works perfectly! - inserted animation listener call setStartOffset() back to 0 after the first repeat so that it doesn't keep "jumping" randomly.


    I found a working solution for this issue, which works by timing the animation to the current system milliseconds. It's a bit of a hack, as it uses reflection to get the mAnimation field in ProgressBar. That being said, this field has remained in place in the Android sources since it was created (works up to 4.2).

    Create the android.widget.SyncedProgressBar class, and use it instead of ProgressBar in your .xml files. It essentially makes the animation start at the beginning of the animation duration. You can also play around with setDuration(500) to verify that it works (the progress wheel will spin really quickly). Hope this helps!

    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 讨论(0)
提交回复
热议问题