AutoCompleteTextView更新ArrayAdapter后无法显示结果

7
我正在尝试使AutoCompleteTextView(ACTV)显示从网络资源获取的结果。我已将完成阈值设置为2,并且当我输入两个字符时,我可以看到请求被触发。
我得到的结果是正确的。假设我写了“ca”,并且我得到了“car”作为自动完成的结果。我有一个回调函数,它从AsyncTask接收结果并将结果放入ArrayAdapter中。然后我在ACTV上调用.showDropDown(),但是会显示一个空的下拉列表(只有正常元素大小的一半)。然后,如果我输入最后一个字母“r”,ACTV就会显示“car”,下拉列表就会显示,并且结果突然出现在列表中。
如果我已经输入了两个字符(返回有效结果),并且删除了最后一个字母,则会发生相同的情况。当字母被删除时,“car”会显示为自动完成值。
是否有人遇到过这个问题?看起来适配器填充了结果,但结果不会显示,直到我进行下一个操作。我还尝试在将结果添加到适配器后运行.notifyDataSetChanged(),但那应该不需要,对吗?

如果您添加您的代码可能会有所帮助。 - Joe
2个回答

16

没有看到你的代码,很难说出现了什么问题。但我首先想到的是你的网络请求是在不同的线程中进行,因此你的 performFiltering() 可能会过早地返回一个空结果集。此时,publishResults() 返回一个空结果,下拉框也为空。稍后,你的AsyncTask将获得其结果,并将结果添加到适配器的列表中,但由于某种原因,它尚未显示。

我认为你可能对需要 AsyncTask 的需求有所误解。过滤器对象已经在做类似 AsyncTask 的事情: performFiltering() 在后台线程中完成,publishResults() 在 performFiltering() 完成后从 UI 线程调用。因此,你可以直接在 performFiltering() 中执行网络请求,并将结果设置到 FilterResults 对象中,这样就不必担心网络请求过慢导致 UI 出现问题了。

另一种解决方案,略微复杂,但这是我在我的 Filter 对象中所做的(由于现有架构在后台执行 API 调用,使用异步回调而不是执行 performFiltering() 所需的阻塞/同步步骤),是使用一个带有 wait()/notify() 的同步对象进行跨线程监视,以便效果与直接在 performFiltering() 中执行网络请求相同,但实际上是在多个线程中进行:

// in Filter class..
protected FilterResults performFiltering(CharSequence constraint) {

    APIResult response = synchronizer.waitForAPI(constraint);
    // ...
}

// callback invoked after the API call finishes:
public void onAPIComplete(APIResult results) {
    synchronizer.notifyAPIDone(results);
}

private class Synchronizer {
    APIResult result;

    synchronized APIResult waitForAPI(CharSequence constraint) {
        someAPIObject.startAsyncNetworkRequest(constraint);
        // At this point, control returns here, and the network request is in-progress in a different thread.
        try {
            // wait() is a Java IPC technique that will block execution until another
            // thread calls the same object's notify() method.
            wait();
            // When we get here, we know that someone else has just called notify()
            // on this object, and therefore this.result should be set.
        } catch(InterruptedException e) { }
        return this.result;
    }

    synchronized void notifyAPIDone(APIResult result) {
        this.result = result;
        // API result is received on a different thread, via the API callback.
        // notify() will wake up the other calling thread, allowing it to continue
        // execution in the performFiltering() method, as usual.
        notify();
    }
}

不过,我认为您可能会发现最简单的解决方案是直接在performFiltering()方法中同步执行您的网络请求。上面的代码示例只是一种可能性,如果您已经有了异步/回调驱动的API调用架构,并且不想改变该行为以获取performFiltering()方法中的同步结果,则可以使用该方法。


干得好,乔!我认为你关于异步任务在执行过滤之后返回数据存在竞争条件的观点是正确的! - reidisaki
1
感谢您指出performFiltering()在后台线程中运行。 - Davor Zlotrg
这似乎太复杂了。我的问题在于建议是从Firebase中提取的。 因此,为了避免除Firebase DB侦听器之外的另一个aysnc,我在适配器确保数据被获取后再次调用settext。 只需在结束时触发整个watcher ==> performFilter循环即可。 我验证了额外的调用不会损坏任何东西(视图足够聪明,以便在第一个过滤器已经OK的情况下注意到没有变化)。 也许它会帮助某人... - Croc

1

我认为Joe的回答是可行的。然而,我认为你应该使用CountDownLatch而不是wait/notify。

原因是,使用wait/notify时,如果你的API在开始“wait()”之前实际上返回超级快,你会面临竞争条件的风险...在这种情况下,notify将没有效果,wait()将无限等待。 使用Latch,代码将如下所示(从Joe复制并修改):

// in Filter class..
protected FilterResults performFiltering(CharSequence constraint) {
  APIResult response = synchronizer.waitForAPI(constraint);
  // ...
}

// callback invoked after the API call finishes:
public void onAPIComplete(APIResult results) {
  synchronizer.notifyAPIDone(results);
}

private class Synchronizer {
  APIResult result;
  CountDownLatch latch;

  synchronized APIResult waitForAPI(CharSequence constraint) {
      latch = new CountDownLatch(1);
      someAPIObject.startAsyncNetworkRequest(constraint);
      // At this point, control returns here, and the network request is in-progress in a different thread.
      try {
        // Will wait till the count is 0...
        // If the count is already 0, it'll return immediately. 
        latch.await();
        // When we get here, we know that someone else has just called notify()
        // on this object, and therefore this.result should be set.
    } catch(InterruptedException e) { }
    return this.result;
  }

  synchronized void notifyAPIDone(APIResult result) {
    this.result = result;
    // API result is received on a different thread, via the API callback.
    // countDown() will wake up the other calling thread, allowing it to continue
    // execution in the performFiltering() method, as usual.
    latch.countDown();
  }
}

最后,我没有足够的信用来发表评论,否则我就会...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接