【问题标题】:Android: async loaded bitmaps randomly don't appear in ImageView after ImageView.setBitmapDrawable(..)Android:在 ImageView.setBitmapDrawable(..) 之后,异步加载的位图随机不会出现在 ImageView 中
【发布时间】:2014-01-24 10:32:29
【问题描述】:

我有一个 ListView,其元素是我需要从远程 API 加载的照片,我使用 loopj 来访问它。

loopj下载照片数据后,我使用AsyncTask将数据处理成缩放位图。

不过,这通常效果很好..

问题

在我的 AsyncTask 的 onPostExecute 方法中,我在 ImageView 上调用 setImageBitmap。有时,调用 ImageView.setImageBitmap 后,位图根本不会出现在 ImageView 中。我已经通过日志和调试器验证了 ImageView.setImageBitmap 确实在有问题的 ImageView 上被调用。

如果我稍微滚动列表,列表元素会重新绘制,并且图像会从内存缓存中加载并正确显示在 ImageView 上。

这是我的代码:

public void setPersonImage(final ImageView personImageView, final Person p) {
    personImageView.setImageResource(R.drawable.empty_image);

    //kills unecessary requests for the imageview's previous occupant photo
    if(personImageView.getTag() != null) {
        AsyncHttpClient client = mPhotoRequestsMap.get(p.getEmployeeId());
        if(client != null) {
            client.cancelRequests(getActivity().getApplicationContext(), true);
            Log.d(TAG, "Cancled photo request for " + String.valueOf(p.getEmployeeId()));
        }
    }

    personImageView.setTag(p.getEmployeeId());

    //first attempt to grab photo from cache
    Bitmap cachedBitmap = mMemoryCache.get(p.getPhotoCacheKey());
    if(cachedBitmap != null) {
        personImageView.setImageBitmap(cachedBitmap);
        Log.d(TAG, "bitmap for " + String.valueOf(p.getEmployeeId()) + " retrieved from memory cache");
    } else {

        AsyncHttpClient client = ApiWrapper.getPersonImage(180,mUserSession, 
            getActivity().getApplicationContext(), p.getEmployeeId(),
            new MyAsyncHttpResponseHandler(this) {
               @Override
               public void onSuccess(String response) {
                    if(personImageView.getTag().equals(p.getEmployeeId())) {
                        // use async task to process the bitmap off the UI thread
                        AsyncTask<String,Integer,Bitmap> task = new AsyncTask<String,Integer,Bitmap>() {
                            @Override
                            protected Bitmap doInBackground(String... params) {
                                String response = params[0];
                                byte[] bitmapBytes = ApiWrapper.processImageResponse(response);
                                Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length);
                                Bitmap bmpScaled = ThumbnailUtils.extractThumbnail(bitmap, 180, 180);
                                return bmpScaled;
                            }

                            protected void onPostExecute(Bitmap bitmap) {
                                //set on imageview if tags agree 
                                if(personImageView.getTag().equals(p.getEmployeeId())) {
                                    Log.d(TAG, "setting image view for " + p.getEmployeeId());
                                    personImageView.setImageBitmap(bitmap);
                                    mMemoryCache.put(p.getPhotoCacheKey(), bitmap);

                                }
                                else {
                                    Log.d(TAG, "tags don't match after decode bitmap, discarding...");
                                }
                            }

                        };

                        task.execute(response);
                    }
                    else {
                        Log.d(TAG, "tags don't match BEFORE decode bitmap employee id: " + p.getEmployeeId() + " tag: " + personImageView.getTag());
                    }
               }


           }
        );

        //create entry in PhotoRequestsMap table
        mPhotoRequestsMap.put(p.getEmployeeId(), client);

    } // end if not in cache
}

我尝试在 ImageView 上设置位图后调用 invalidate(),但遗憾的是这并没有解决它..

非常感谢任何帮助或指点。

更新

来自我的适配器的 getView 方法:

public View getView(int position, View convertView, ViewGroup parent) {

            // If we weren't given a view, inflate one
            if (convertView == null) {
               convertView = getActivity().getLayoutInflater().inflate(R.layout.list_item_person, null);
            }

            Person p = getItem(position);

            TextView nameTextView = (TextView)convertView.findViewById(R.id.person_ListItemNameTextView);
            nameTextView.setText(p.getFirstName() + " " + p.getLastName());

            TextView idTextView = (TextView)convertView.findViewById(R.id.person_ListItemIdTextView);
            idTextView.setText(String.valueOf(p.getEmployeeId()));

            ImageView personImageView = (ImageView)convertView.findViewById(R.id.person_ListItemImageView);
            setPersonImage(personImageView, p);

            return convertView;
        }

【问题讨论】:

  • personImageView 被多次调用,在第一个 AsyncTask 完成之前,第二个覆盖它。尝试制作对象数组。
  • 我猜您正在为不同的列表行重新使用相同的 Imageview?是否可以从您的适配器中看到getView(...) 方法?
  • @ErnirErlingsson 我认为默认情况下android会自行回收imageViews。虽然我正在缓存位图。我用我的 getView() 方法更新了这个问题。谢谢!

标签: android android-listview android-asynctask android-imageview loopj


【解决方案1】:

我认为.setImageBitmap(Bitmap) 方法工作正常,你只是用null 设置它。

onPostExecute(Bitmap bitmap) 中检查bitmap == null 的大小写。这也符合您观察到图像在您滚动时出现的情况,因为当时 Android 再次调用 getView(..) 以获得可见行。

【讨论】:

  • 设置的位图不为空。我已经通过调试器验证了这一点,现在还像你描述的那样添加了一个空检查。我看到了同样的结果.. 谢谢你的尝试:)
  • 您是否尝试在每次调用 getView(..) 时都尝试扩充一个新视图?尝试删除if (convertView == null) 并检查问题是否仍然存在(不是我推荐它,只是检查问题是否在于视图的重用)。
  • 我删除了 if (convertView == null),它似乎正在工作!我正试图弄清楚我现在应该做什么,因为每次都夸大视图可能不是一种可持续的方法?
  • 正确!那太慢了。我怀疑问题与取消代码有关。将if (convertView == null) 放回原处,这次尝试删除client.cancelRequest.. 代码。
  • 看来你是对的。我删除了取消代码,它现在按预期工作。取消代码处理的问题是,如果用户翻阅一长串需要加载的图像,应用程序将发出大量不必要的请求。这个问题通常是如何处理的?
猜你喜欢
  • 2014-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-21
  • 1970-01-01
  • 1970-01-01
  • 2011-06-23
相关资源
最近更新 更多