AnimationDrawable在实例化新视图时冻结

时间:2014-03-27 21:14:26

标签: android android-listview android-view android-scrollview

我有Fragment用于显示从我的网络服务器获取的大量异构内容。我正在使用AsyncTaskLoader来获取数据,一旦我得到响应,我会执行一些逻辑来确定要显示的内容。当AsyncTaskLoader正在运行时,我有AnimationDrawable占用整个显示。加载完内容后,我在stop()上致电AnimationDrawable,将其设为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,要么创建几个自定义{之一的实例{1}}我已创建(饼图,图表等),需要一点点数学。在我的测试中,这需要半秒到1.5秒。

我看到的情况是,当我的View开始时,一旦我开始创建视图并将其添加到onLoadFinishedmainLayout就会冻结它所在的任何帧。当我停止动画并切换布局时,它会一直冻结,直到我到达方法的末尾。

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

注意:我最初使用自定义AnimationDrawable将其实现为ListView。我创建了一个TypeXModule对象的数组(都实现了BaseAdapter),并根据需要在适配器中调用它们。内容将几乎瞬间加载(一次只有3或4个模块适合屏幕,只有3或4个模块调用它们getView())。但是,使用getView()在滚动时会产生轻微卡顿的副作用,直到您无法将整个列表拖到顶部或底部。我决定尝试一个ListView,因为1)所有视图都会立即被实例化,因此没有口吃,2)模块'ScrollView都不同,我无法真正利用View回收View执行。

2 个答案:

答案 0 :(得分:0)

试试这个,开关有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();
    }
}

答案 1 :(得分:0)

由于我无法完全消除动画冻结,我最终走的路线略有不同。在我完成所有模块之后,不是停止动画并切换布局。 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)副作用,即#34; loading&#34;当模块连续快速加载时效果。我发现加载速度与使用ListView一样快。这种方法的唯一缺点是,在加载模块时,滚动是不稳定的(因为在UI线程上正在进行工作)。但是,一旦加载了所有模块,滚动就非常顺利(这首先是主要目标)。