1

我正在开发一个 Android 应用程序,并且我有一个 Activity,我在其上显示两个 TableLayout 的顶部。第一个 TableLayout 包含一个比赛的记分牌(类型 1),第二个是另一个比赛的记分牌(类型 2)。

我从同一个 XML 布局文件中扩充了这两个 TableLayout。我用来自 doInBackground() (AsyncTask) 的数据填充它们。但是,填充部分非常繁重,因此我将其放入自定义同步方法(在 AsyncTask 类中)并将膨胀的 TableLayout 对象作为参数传递给该方法。

我使用 ExecutorService 对象从 do​​InBackground() 方法调用所述方法。这个 ExecutorService 对象是用 Executors.newSingleThreadExecutor() 创建的。我将第一个方法调用放在一个 Runnable 中,将第二个方法调用放在另一个 Runnable 中,并在这些可运行对象上触发 ExecutorService.execute()。

在该方法中,我通过在传入的 TableLayout 对象参数上调用 findViewById() 并在这些视图上调用 TextView.setText() 等方法来访问 UI 元素。这在大多数情况下都可以正常工作,但我开始看到一些例外情况,例如

ERROR/AndroidRuntime(409): FATAL EXCEPTION: pool-9-thread-1
    android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

ERROR/AndroidRuntime(378): FATAL EXCEPTION: pool-2-thread-1
    java.lang.NullPointerException

这让我相信我必须以难以置信的倒退来做这件事。我将在下面包含一个简化的代码片段,以帮助描述我目前正在做的事情:

class AddRacesTask extends AsyncTask<Void, View, Void> {
    synchronized void populateTable(ArrayList<Race> data, TableLayout table) {
       TextView headerText = table.findViewById(R.id...);
       headerText.setText("Header");

       for(Race race : data) {
          TableRow row = getLayoutInflater().inflate(R.layout....);

          TextView pos = row.findViewById(R.id....);
          pos.setText(race.getPos());

          TextView person = row.findViewById(R.id....);
          person.setText(race.getPerson());

          // etc.
          // etc..
          // etc...

          table.addView(row);

       }

       publishProgress(table);

    }

    @Override
    private Void doInBackground(Void... voids) {
       ExecutorService executor = Executors.newSingleThreadExecutor();

       executor.execute(new Runnable() {
          @Override
          public void run() {
             TableLayout raceType1Layout = getLayoutInflater().inflate(R.layout...);
             populateTable(data, raceType1Layout);
          }
       });

       executor.execute(new Runnable() {
          @Override
          public void run() {
             TableLayout raceType2Layout = getLayoutInflater().inflate(R.layout...);
             populateTable(data, raceType1Layout);
          }
       });
    }

    @Override
    protected void onProgressUpdate(View... values) {
       if(values[0] instanceof TableLayout) {
          container.addView(values[0]);
       }
    }
}

我欢迎任何反馈。此外,如果您需要更多内容,我很乐意发布我的实际源代码,但我认为简化和干净的版本会更容易。

4

2 回答 2

1

您需要实现一个自定义适配器,用 Race 对象填充您的自定义视图。您在后台使用自定义线程或 AsyncTask 接收数据,并分别通过 Handler 或 onPostExecute 方法传递给 UI 线程。然后,您可以调用 Adapter.clear 方法和之后

for(Race r: ArrayList<Race>)
Adapter.add(r) 
于 2012-08-04T15:12:24.493 回答
0

在您完成 AsyncTask 中的工作后,可能会建议您通过 Handler 更新您的 UI。这是 AsyncTask/Thread 之间的消息应如何与 UI 线程通信的标准。

这样做的原因是因为不允许您在不是UI 线程的线程中访问 UI 元素。

您可以使用的另一种方法runOnUiThread是 Activity 类中可用的方法。然而; 我建议使用处理程序。

于 2012-08-04T14:24:37.203 回答