AnimationDrawable在实例化新视图时冻结



我有一个Fragment,我用它来显示很多从我的web服务器获取的异构内容。我使用AsyncTaskLoader来获取数据,一旦得到响应,我就执行一些逻辑来确定要显示的内容。当AsyncTaskLoader运行时,我有一个AnimationDrawable占用整个显示器。在我完成加载内容后,我在AnimationDrawable上调用stop(),将其设置为View.GONE,并将主LinearLayout(包含我的所有内容)设置为可见。

<?xml version="1.0" encoding="UTF-8"?>
<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:fillViewport="true">
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >
    <LinearLayout
        android:id="@+id/animation_layout"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:layout_gravity="center_vertical"
        android:gravity="center"
        android:orientation="vertical" >
        <ImageView
            android:id="@+id/progressDialog"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:scaleType="center" />
    </LinearLayout>
    <LinearLayout
        android:id="@+id/main_layout"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:visibility="gone"
        android:orientation="vertical" />
</LinearLayout>
</ScrollView>

下面是我如何初始化加载器和动画,并显示我的内容。

@Override
public void onStart(){
  super.onStart();
  LoaderManager lm = getLoaderManager();
  lm.initLoader(LOADER_PROFILE, getArguments(), this);
  loadingAnimation.start();
  animationLayout.setVisibility(LinearLayout.VISIBLE);
  mainLayout.setVisibility(ViewGroup.GONE);
}
@Override
public void onLoadFinished(Loader<Response> responseLoader, Response response) {
  LayoutInflater inflater = LayoutInflater.from(getActivity());
  for (Module m : response.modules) {
    switch (m.Type) {
      case Type1:
        mainLayout.addView(Type1Module.getView(inflater, m));
        break;
      case Type2:
        mainLayout.addView(Type2Module.getView(inflater, m));
        break;
      case Type3:
        mainLayout.addView(Type3Module.getView(inflater, m));
        break;
      case Type4:
        mainLayout.addView(Type4Module.getView(inflater, m));
        break;
      case Type5:
        mainLayout.addView(Type5Module.getView(inflater, m));
        break;
  }
  loadingAnimation.stop();
  animationLayout.setVisibility(LinearLayout.GONE);
  mainLayout.setVisibility(ViewGroup.VISIBLE);
}

在每个模块的静态getView()方法中,我要么膨胀布局并根据数据分配不同的TextViewsImageViews,要么创建我创建的几个自定义View中的一个实例(饼图,图表等),这需要一点数学知识。在我的测试中,这需要半秒到1.5秒。

我看到的是,当我的onLoadFinished启动时,一旦我开始创建视图并将它们添加到mainLayout, AnimationDrawable冻结在它所在的任何帧上。当我停止动画并切换布局时,它一直保持冻结状态,直到我到达方法的末尾。

现在,我知道这正在发生,因为我在UI线程上做所有的逻辑,因此阻塞了AnimationDrawable。问题是,我所做的大多数逻辑都是决定视图参数(文本,颜色,字体,是否显示),这必须在UI线程上完成。我的问题是,有没有人遇到过同样的问题,在UI线程阻塞动画上有太多的工作要做,你有没有想出一个解决方案?

注意:我最初将其实现为ListView和自定义BaseAdapter。我创建了一个TypeXModule对象数组(它们都实现了getView()),并根据需要在适配器中调用它们。内容几乎是瞬间加载的(游戏邦注:一次只有3或4个模块适合屏幕,只有3或4个模块调用了getView())。然而,使用ListView会在滚动时产生轻微的口吃,以至于你无法将整个列表放到顶部或底部。我决定尝试ScrollView,因为1)所有视图将立即实例化,因此没有口吃,并且2)模块的View s都不相似,以至于我无法真正利用View回收ListView s的性能。

试试这个,开关有4种情况:前两种不工作,情况2与单独的线程似乎是最快的

final AnimationDrawable dr = new AnimationDrawable();
dr.setBounds(0, 0, 100 ,100);
Drawable frame;
Resources res = getResources();
frame = res.getDrawable(R.drawable.t6);
dr.addFrame(frame, 200);
frame = res.getDrawable(R.drawable.t7);
dr.addFrame(frame, 200);
frame = res.getDrawable(R.drawable.t8);
dr.addFrame(frame, 200);
frame = res.getDrawable(R.drawable.t9);
dr.addFrame(frame, 200);
frame = res.getDrawable(R.drawable.t10);
dr.addFrame(frame, 200);
final TextView tv = new TextView(this);
tv.setTextSize(72);
tv.setTextColor(0xffeeeeee);
tv.setGravity(Gravity.CENTER);
tv.setBackgroundDrawable(dr);
FrameLayout fl = new FrameLayout(this);
fl.addView(tv);
fl.setBackgroundColor(0xffaaaaaa);
setContentView(fl);
final int STEPS = 200;
final Handler h = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        if (msg.what % 50 == 0) {
            Log.d(TAG, "handleMessage msg: " + msg);
        }
        tv.setText(Integer.toString(msg.what));
        if (msg.arg1 == 0) {
            take20msofCPU();
        }
        if (msg.what < STEPS) {
            obtainMessage(msg.what + 1).sendToTarget();
        }
    }
};
OnClickListener l = new OnClickListener() {
    @Override
    public void onClick(View v) {
        Log.d(TAG, "onClick ");
        h.removeCallbacksAndMessages(null);
        dr.stop();
        dr.start();
        int caseNumber = 2;
        switch (caseNumber) {
            case 0:
                Log.d(TAG, "onClick simple loop: no no");
                for (int i = 0; i < STEPS; i++) {
                    take20msofCPU();
                }
                break;
            case 1:
                Log.d(TAG, "onClick send all Messages at once: no no");
                for (int i = 0; i < STEPS; i++) {
                    h.obtainMessage(STEPS+i).sendToTarget();
                }
                break;
            case 2:
                Log.d(TAG, "onClick simple loop in a separate Thread + Handler notification");
                new Thread() {
                    public void run() {
                        for (int i = 0; i < STEPS; i++) {
                            take20msofCPU();
                            // we take 20 ms here so dont do that in the Handler
                            // alse msg.what is >= STEPS so it doesnt kick itself
                            h.obtainMessage(STEPS+i, 1, 0, null).sendToTarget();
                        }
                    };
                }.start();
                break;
            default:
                Log.d(TAG, "onClick send a Message one by one");
                h.obtainMessage(0).sendToTarget();
                break;
        }
    }
};
tv.setOnClickListener(l);
void take20msofCPU() {
    // simulate hard task: you can do some looping that do something or just sleep
    try {
        Thread.sleep(20);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

因为我无法完全消除动画冻结,所以我最终选择了一条稍微不同的路线。在我完成创建所有模块的Views后,我没有停止动画并切换布局,而是在加载第一个模块后停止动画并切换布局,然后增量加载每个后续模块。我使用了一个单独的HandlerThread,所以我没有触发所有主UI线程上的模块。

private ArrayList<Modules> modules;
private HandlerThread mThread;
private ModuleThreadHandler mThreadHandler;
@Override
public void onStart(){
  super.onStart();
  LoaderManager lm = getLoaderManager();
  lm.initLoader(LOADER_PROFILE, getArguments(), this);
  loadingAnimation.start();
  animationLayout.setVisibility(LinearLayout.VISIBLE);
  mainLayout.setVisibility(ViewGroup.GONE);
}
@Override
public void onLoadFinished(Loader<Response> responseLoader, Response response) {
  modules = response.modules;
  mThread = new HandlerThread("mThread");
  mThread.start();
  Looper mThreadLooper = mThread.getLooper();
  mThreadHandler = new ModuleThreadHandler(mThreadLooper);
  mThreadHandler.obtainMessage(0).sendToTarget();
}  
private class ModuleThreadHandler extends Handler {
  public ModuleThreadHandler(Looper looper) {
    super(looper);
  }
  @Override
  public void handleMessage(Message msg) {
    fragmentHandler.obtainMessage(msg.what + 1).sendToTarget();
  }
}
private Handler fragmentHandler = new Handler() {
  @Override
  public void handleMessage(Message msg) {
    if (msg.what < modules.size()) {
      Module m = modules.get(msg.what);
      switch (m.Type) {
        case Type1:
          mainLayout.addView(Type1Module.getView(inflater, m));
          break;
        case Type2:
          mainLayout.addView(Type2Module.getView(inflater, m));
          break;
        case Type3:
          mainLayout.addView(Type3Module.getView(inflater, m));
          break;
        case Type4:
          mainLayout.addView(Type4Module.getView(inflater, m));
          break;
        case Type5:
          mainLayout.addView(Type5Module.getView(inflater, m));
          break;
      }
    }
    if (msg.what == 0) {
      loadingAnimation.stop();
      animationLayout.setVisibility(LinearLayout.GONE);
      mainLayout.setVisibility(ViewGroup.VISIBLE);
    }
    if ((msg.what + 1) < modules.size()) {
      pThreadHandler.obtainMessage(msg.what).sendToTarget();
    } else {
      pThread.quit();
    }
  }
};

这具有"加载"效果的良好(IMO)副作用,因为模块连续快速加载。我发现这个加载速度和使用ListView一样快。这种方法的唯一缺点是,当模块正在加载时,滚动是不稳定的(因为在UI线程上正在完成工作)。然而,一旦所有模块都被加载,滚动就会非常平滑(这是我们的主要目标)。

最新更新