在每个listview项目中旋转进度条

我已经很长时间以来一直在摸不着头脑并且没有任何运气就寻找答案! 这似乎是微不足道的,但据我所知,事实并非如此。

我在我的Android应用程序中使用listview,其中每个项目(视图)在加载和显示内容之前显示旋转的ProgressBar(内容通过http调用和json检索,因此可能需要一段时间才能处理)。 问题在于旋转进度条彼此独立地旋转,从而产生旋转混乱的效果,而不是同步的一排漂亮的装载标记。

我已经尝试了所有我能想出来的东西……没有在getView()中循环使用ProgressBar …只有一个相同ProgressBar的实例…重置ProgressBars android:每当列表项可见时进度(通过onScroll ()在活动中)…等,但由于它们在创建时开始旋转(当在适配器中调用getView时),它们将永远不会具有相同的循环同步。

在此处输入图像描述

欢迎任何建议!

编辑:现在完美无缺! – 插入的animation侦听器在第一次重复后调用setStartOffset()返回0,以便它不会随机“跳跃”。


我find了一个解决这个问题的工作解决方案,它通过将animation计时到当前系统毫秒来工作。 这有点像黑客,因为它使用reflection来获取ProgressBarmAnimation字段。 话虽这么说,自创建以来,这个领域一直存在于Android资源中(最高可达4.2)。

创建android.widget.SyncedProgressBar类,并在.xml文件中使用它而不是ProgressBar 。 它实质上使animation在animation持续时间的开始处开始。 你也可以使用setDuration(500)来validation它的工作原理(进度轮会很快旋转)。 希望这可以帮助!

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

以下是如何使其工作。 只使用一个AnimationDrawable,复用回调。 ProgressBar并没有真正添加任何内容,您可以坚持使用View。 创建绕过Drawable管理的View子类。

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

在你的适配器getView()中禁用progressViwe,然后为每个progressView

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

这样做可以同时启用所有progressView。 这可能有用,我还没有测试过。 如果这不起作用,那么您可能想要动态添加progressView(不包括在布局中,但通过addView()添加)。 但是在runnable中这样做,关键在于它们都可以同时添加。

通过源代码挖掘,我发现由于私有Animation ,旋转图像是一个可以旋转的可绘制图像。 无法获取视图或animation,因此放弃了在所有视图上重复使用一个animation的任何想法。 我会说你唯一的选择是Jug6ernaut建议的,即使它不是最漂亮的解决方案。

我不确定这是否可行,因为ListView中的每一行都在离开屏幕时被回收。 当屏幕上出现新行时, ProgressBar视图将必须彼此重新同步。 保持所有行同步将成为一个大PITA。

您是否考虑过在一个请求中加载所有列表数据并将该数据缓存到本地存储? 它会更高效,并带来更好的用户体验。 让ListView立即加载空行并不是很有用。 我宁愿等待列表完全填充。

1>为列表行创建布局

      

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