我正在开发一个小应用,我需要在其中显示产品列表。从web api获取产品(在后台),然后将其添加到产品列表中,适配器负责在视图中显示它们 -
ProductActivity -
runOnUiThread(new Runnable() {
@Override
public void run() {
if (mAdapter == null) {
mAdapter = new ProductAdapter(filteredProductList, R.layout.list_activity_product, ProductActivity.this);
mRecyclerView.setAdapter(mAdapter);
} else {
int addItemAtIndex = 0;
mAdapter.notifyItemInserted(addItemAtIndex);
// scroll to top
mRecyclerView.smoothScrollToPosition(0);
}
}
});
您可以注意到,我将Activity上下文传递给上面的适配器。现在,由于产品对象的一个属性是url(产品图像),我试图在绑定viewHolder时尝试获取它(再次在后台) -
public void onBindViewHolder(ViewHolder viewHolder, int i) {
_vwHolder = viewHolder;
ProductDetail prod = products.get(i);
...
// setting image in background thread
new setImageBitmap().execute(prod.ProductImageUrl);
}
class setImageBitmap extends AsyncTask<String, String, String> {
@Override
protected String doInBackground(String... params) {
try{
Log.d(TAG, "image to be fetched: " + params[0]);
URL url = new URL(params[0]);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream inp = connection.getInputStream();
Bitmap btmp = BitmapFactory.decodeStream(inp);
btmp = Bitmap.createScaledBitmap(btmp, _width, _height, true);
Log.d(TAG,"width: " + Integer.toString(btmp.getWidth()));
Log.d(TAG,"height" + Integer.toString(btmp.getHeight()));
_vwHolder.ProductImageUrl.setImageBitmap(btmp); // throws error
}
catch (MalformedURLException e) {
Log.d(TAG, "MalformedURLException");
} catch (IOException e) {
Log.d(TAG, "IOException");
}
return null;
}
}
现在,当我这样做时,我收到一条消息(上面代码中的'// throws error'行) -
Only the original thread that created a view hierarchy can touch its views.
我知道这意味着我需要获取调用它然后在该线程上运行的活动上下文的引用,但我不确定如何从适配器代码访问它。任何人都可以帮忙吗?
答案 0 :(得分:1)
你发现问题出在
_vwHolder.ProductImageUrl.setImageBitmap(btmp); // throws error
您需要将该段代码放入AsyncTask的onPostExecute(T result)方法中。您目前还没有,所以您需要首先覆盖该方法
答案 1 :(得分:1)
根据你的问题,我认为你可能误解了你的错误,但我马上就会明白这一点。
调用时,您已将Activity实例传递给适配器构造函数:
mAdapter = new ProductAdapter(filteredProductList, R.layout.list_activity_product, ProductActivity.this);
但是你可能正在做的是在你的ProductAdapter中,这是作为一个Context传递的。由于ProductActivity继承自Activity,而Activity继承自Context,因此您可以将Activity实例作为Context传递。要恢复您的活动,您只需将上下文转换回ProductActivity。
ProductActivity acti = (ProductActivity) context;
现在讨论你的错误。当您从行中的后台线程修改UI时,您收到错误:
mAdapter = new ProductAdapter(filteredProductList, R.layout.list_activity_product, ProductActivity.this);
你应该只在UI线程中修改它。所以我建议你简单地从AsyncTask类的onPostExecute()方法进行更新。在这种情况下,您的AsyncTask看起来像
class setImageBitmap extends AsyncTask<String, String, Bitmap> {
@Override
protected Bitmap doInBackground(String... params) {
try{
Log.d(TAG, "image to be fetched: " + params[0]);
URL url = new URL(params[0]);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream inp = connection.getInputStream();
Bitmap btmp = BitmapFactory.decodeStream(inp);
btmp = Bitmap.createScaledBitmap(btmp, _width, _height, true);
Log.d(TAG,"width: " + Integer.toString(btmp.getWidth()));
Log.d(TAG,"height" + Integer.toString(btmp.getHeight()));
}
catch (MalformedURLException e) {
Log.d(TAG, "MalformedURLException");
} catch (IOException e) {
Log.d(TAG, "IOException");
}
return btmp;
}
// This runs on the UI thread
@Override
protected void on PostExecuted(Bitmap btmp) {
_vwHolder.ProductImageUrl.setImageBitmap(btmp);
}
}
只要从UI线程启动AsyncTask,这就应该有效。
答案 2 :(得分:1)
我需要做的是对传递给Activity
的上下文进行类型转换,然后移动我绑定到OnPostExecute
的代码 -
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
((Activity)mContext).runOnUiThread(new Runnable() { // here is the change
@Override
public void run() {
_vwHolder.ProductImageUrl.setImageBitmap(_btmp);
}
});
}
尽管苏尼尔到底有点说同样的话,但他明白这个问题是错的。我从另一篇文章中得到了这个解决方案,所以在我的代码中发布我现在编码的内容。
我的应用现在正常运作。感谢。
答案 3 :(得分:0)
更好用:
@Override
protected void onPostExecute(Bitmap btmp ) {
if (isCancelled()) {
}
if (btmp !=null) context.asyncRefresh(btmp);
}
然后在适配器活动中使用:
_vwHolder.ProductImageUrl.setImageBitmap(btmp);
就像这样:
public void asyncRefresh(Bitmap btmp ) {
_vwHolder.ProductImageUrl.setImageBitmap(btmp);
}