我创建了一个gridview,它显示来自服务器的视频。 GridItem具有视频拇指图像和视频持续时间。为了加载视频拇指,我正在使用UniversalImageloader并通过使用asynctask创建延迟加载来加载视频持续时间。但是如果有人随意滚动gridview,则视频时长会显示在错误的位置。用于创建延迟加载,我关注的是波纹管link

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    final TextView durationTextView;

    View view = null;
    if (convertView == null) {
        view = mInflater.inflate(R.layout.camera_roll_item, parent, false);

        MediaItemViewHolder mediaItemViewHolder = new MediaItemViewHolder();
        mediaItemViewHolder.highlightTagIcon = (ImageView) view.findViewById(R.id.iv_media_grid_item_highlight_tag);
        mediaItemViewHolder.mediaTypeIcon = (ImageView) view.findViewById(R.id.iv_media_grid_item_type);
        mediaItemViewHolder.mediaClipLength = (TextView) view.findViewById(R.id.tv_media_grid_item_length);
        mediaItemViewHolder.mediaThumbnail = (ImageView) view.findViewById(R.id.iv_media_grid_item_thumbnail);
        mediaItemViewHolder.cameraItemSelectedView = (RelativeLayout) view.findViewById(R.id.rl_item_selection_parent);
        mediaItemViewHolder.progressContainer = (RelativeLayout) view.findViewById(R.id.rl_grid_loader_parent);
        view.setTag(mediaItemViewHolder);
    } else {
        view = convertView;//(MediaItemViewHolder) convertView.getTag();
        //mediaItemViewHolder.mediaClipLength.setText("");
        Log.i(TAG, "set blank to ");
    }
    MediaItemViewHolder mediaItemViewHolder = (MediaItemViewHolder) convertView.getTag();
    durationTextView = mediaItemViewHolder.mediaClipLength;
    if (position >= mCameraMediaItems.size()) {
        Log.d(TAG, "Index out of Bound, position:" + position + " - mCameraMediaItems.size():" + mCameraMediaItems.size());
        return convertView;
    }
    MediaItemBean mediaItemBean = CameraMediaController.getInstance().getMediaItemByPosition(position);
    mediaItemViewHolder.mediaClipLength.setVisibility(View.VISIBLE);
    mediaItemViewHolder.highlightTagIcon.setVisibility(View.GONE);

    if (mediaItemBean != null && mediaItemBean.getCameraMedia() != null) {
        switch (mediaItemBean.getCameraMedia().getType()) {
            case AppConstant.MEDIA_TYPE_VIDEO:
                mediaItemViewHolder.mediaTypeIcon.setImageResource(R.drawable.icn_thumb_vid);
                //VideoInfoAsyncTask loads data in this list
                int videoDuration = mediaItemBean.getVideoDuration();
                //mediaItemViewHolder.mediaClipLength.setTag(CameraMediaUtil.convertSecondsTimeToMinutesString(videoDuration));
                Log.i(TAG, "VideoDuration " + videoDuration);

                String resId = mediaItemBean.getCreatedId()+"video_media_duration_com.gopro.buckhorn";
                Log.i(TAG, "RESID "+resId);
                downloadDuration(resId, durationTextView, mediaItemViewHolder.highlightTagIcon, mediaItemBean);
                break;
            case MULTI_PHOTO:
                String mulCount = String.valueOf(Controller.getInstance().getPhotoCountAtPosition(position));
                Log.i("MULTI_SHOT_SECTION", "MultiShot "+mulCount);
                mediaItemViewHolder.mediaTypeIcon.setImageResource(R.drawable.icn_thumb_burst);
                mediaItemViewHolder.mediaClipLength.setText(mulCount);
                break;

        }
        //Load image into image view from URL
        String imageUri = mediaItemBean.getThumbnailUri().toString();
        Log.i(TAG, "Thumb url :" + imageUri);
        mediaItemViewHolder.progressContainer.setVisibility(View.VISIBLE);

        DownloadImageUtil.getLoadImageInsatnce().downloadGridImage(imageUri,
                mediaItemViewHolder.mediaThumbnail, R.drawable.thumb_load, mediaItemViewHolder.progressContainer);
    }

    return convertView;
}


private void downloadDuration(String resId, TextView textView, ImageView highlightTagIcon, MediaItemBean mediaItemBean) {
    String duration = getVideoDurationFromCache(String.valueOf(resId));
    Log.i(TAG, "downloadDuration " + duration);
    if (duration == null) {
        loadVideoDuration(resId, textView, highlightTagIcon, mediaItemBean);
        textView.setText("");
    } else {
        cancelVideoDurationDownloaderTask(resId, textView);
        if(mediaItemBean.getCameraMedia().getType() == AppConstant.MEDIA_TYPE_VIDEO){
            textView.setText(duration);
            if (mediaItemBean.isIsHighLightTags()) {
                highlightTagIcon.setVisibility(View.VISIBLE);
            }
        }
    }
}


private String getVideoDurationFromCache(String key) {
    // First try the hard reference cache
    synchronized (mMemoryCache) {
        final String duration = mMemoryCache.get(key);
        if (duration != null) {
            // Bitmap found in hard cache
            // Move element to first position, so that it is removed last
            mMemoryCache.remove(key);
            mMemoryCache.put(key, duration);
            return duration;
        }
    }
    return null;
}


private static class MediaItemViewHolder {
    ImageView highlightTagIcon, mediaTypeIcon, mediaThumbnail;
    TextView mediaClipLength;
    RelativeLayout cameraItemSelectedView;
    /* ProgressBar innerProgressBar;
     ProgressBar outerProgressBar;*/
    RelativeLayout progressContainer;
}

public class VideoDurationDownloaderTask extends AsyncTask<String, Void, String> {
    private final WeakReference<TextView> videoDurationReference;
    private final WeakReference<ImageView> hiliteTagImageViewWeakReference;
    private String data = "";
    private MediaItemBean mediaItemBean;

    public VideoDurationDownloaderTask(TextView textView, ImageView hiliteTagIcon, MediaItemBean mediaItemBean) {
        this.mediaItemBean = mediaItemBean;
        videoDurationReference = new WeakReference<>(textView);
        hiliteTagImageViewWeakReference = new WeakReference<>(hiliteTagIcon);
    }

    @Override
    protected String doInBackground(String... params) {
        data = params[0];
        Log.i(TAG, "data in task "+data);
        return downloadVideoDuration(mediaItemBean);
    }

    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(result);
        if (isCancelled()) {
            Log.i(TAG, "isCancelled " + result);
            result = "";
        }
        addDurationToMemoryCache(data, result);
        //noinspection ConstantConditions
        if (videoDurationReference != null) {
            TextView videoDuration = videoDurationReference.get();
            Log.i(TAG, "videoDuration " + videoDuration);
            VideoDurationDownloaderTask videoDurationDownloaderTask =
                    getTextViewDerationWorkerTask(videoDuration);
            Log.i(TAG, "videoDurationDownloaderTask " + videoDurationDownloaderTask);
            if (videoDuration != null) {
                if (this == videoDurationDownloaderTask) {
                    if(mediaItemBean.getCameraMedia().getType() == AppConstant.MEDIA_TYPE_VIDEO) {
                        Log.i(TAG, "TAG VAL "+videoDuration.getTag());
                        videoDuration.setText(result);
                        videoDuration.setTag(new TextView(context));
                        if (mediaItemBean.isIsHighLightTags()) {
                            ImageView highlightTagIcon = hiliteTagImageViewWeakReference.get();
                            if (highlightTagIcon != null)
                                highlightTagIcon.setVisibility(View.VISIBLE);
                        }
                    }
                }
            }
        }
    }
}

private String downloadVideoDuration(MediaItemBean mediaItemBean) {
    try {
        if (media != null && mediaItemBean.getMedia() != null) {
            int videoDuration = mediaItemBean.getVideoDuration();
            Log.i(TAG, "Video has duration = " + videoDuration);
            if (videoDuration == -1) {
                CommandResult<Integer> duration =
                        media.getVideoDuration(mediaItemBean.getCameraMedia().getFilePath());
                videoDuration = duration.getData();
                mediaItemBean.setVideoDuration(videoDuration);
                Log.i(TAG, "set Video Duration " + videoDuration);
            }
            return MediaUtil.convertSecondsTimeToMinutesString(videoDuration);
        }
    } catch (Exception e) {
        Log.e(TAG, "Exception Occure while Getting video info:" + e.getMessage());
    }
    Log.i(TAG, "not fetch duration ");
    return "";
}


public void loadVideoDuration(String resId, TextView textView, ImageView hiliteTagIcon, MediaItemBean mediaItemBean) {
    if (cancelVideoDurationDownloaderTask(resId, textView)) {
        final VideoDurationDownloaderTask task = new VideoDurationDownloaderTask(textView, hiliteTagIcon, mediaItemBean);
        AsyncTextView asyncTextView = new AsyncTextView(context, task);
        textView.setTag(asyncTextView);
        task.execute(resId);
    }
}

private boolean cancelVideoDurationDownloaderTask(String data, TextView textView) {
    final VideoDurationDownloaderTask durationWorkerTask = getTextViewDerationWorkerTask(textView);

    if (durationWorkerTask != null) {
        final String textViewData = durationWorkerTask.data;
        Log.i(TAG, textViewData + " textViewDataData, data " + data);
        if (data != null && !textViewData.equalsIgnoreCase(data)) {
            // Cancel previous task
            Log.i(TAG, "Cancel previous task " + data);
            durationWorkerTask.cancel(true);
        } else {
            // The same work is already in progress
            Log.i(TAG, "same work is already in progress " + false);
            return false;
        }
    }
    // No task associated with the ImageView, or an existing task was
    // cancelled
    Log.i(TAG, "cancelVideoDurationDownloaderTask true");
    return true;
}

static class AsyncTextView extends TextView {
    private final WeakReference<VideoDurationDownloaderTask> textviewWorkerTaskReference;

    public AsyncTextView(Context context, VideoDurationDownloaderTask textviewWorkerTask) {
        super(context);
        textviewWorkerTaskReference = new WeakReference<>(textviewWorkerTask);
    }

    public VideoDurationDownloaderTask getTextViewWorkerTask() {
        return textviewWorkerTaskReference.get();
    }
}

private static VideoDurationDownloaderTask getTextViewDerationWorkerTask(TextView textView) {
    if(textView.getTag() != null){
        Log.i(TAG, " textView.getTag() " + textView.getTag());
        if (textView.getTag() instanceof AsyncTextView) {
            Log.i(TAG, " Return Textview task");
            final AsyncTextView asyncTextView = (AsyncTextView) textView.getTag();
            return asyncTextView.getTextViewWorkerTask();
        }
    }
    return null;
}

public void addDurationToMemoryCache(String key, String duration) {
    if (getBitmapFromMemCache(key) == null) {
        mMemoryCache.put(key, duration);
    }
}

最佳答案

从方法接收时,摆脱view并使用convertView。更改为以下内容

//final is optional but if you need to use in thread
final MediaItemViewHolder mediaItemViewHolder = null;
if (convertView == null) {
    convertView = mInflater.inflate(R.layout.camera_roll_item, parent, false);

    mediaItemViewHolder = new MediaItemViewHolder();
    mediaItemViewHolder.highlightTagIcon = (ImageView) convertView.findViewById(R.id.iv_media_grid_item_highlight_tag);
    mediaItemViewHolder.mediaTypeIcon = (ImageView) convertView.findViewById(R.id.iv_media_grid_item_type);
    mediaItemViewHolder.mediaClipLength = (TextView) convertView.findViewById(R.id.tv_media_grid_item_length);
    mediaItemViewHolder.mediaThumbnail = (ImageView) convertView.findViewById(R.id.iv_media_grid_item_thumbnail);
    mediaItemViewHolder.cameraItemSelectedView = (RelativeLayout) convertView.findViewById(R.id.rl_item_selection_parent);
    mediaItemViewHolder.progressContainer = (RelativeLayout) convertView.findViewById(R.id.rl_grid_loader_parent);
    convertView.setTag(mediaItemViewHolder);
} else {
    mediaItemViewHolder = (MediaItemViewHolder)convertView.getTag();
    Log.i(TAG, "set blank to ");
}

现在使用mediaItemViewHolder.durationTextView.setText(....)
更新1:我已经知道了问题所在。 为什么在延迟的Gridview位置上延迟加载更新视频的持续时间。 如果需要,请丢弃以上内容
downloadDuration(resId, durationTextView, mediaItemViewHolder.highlightTagIcon, mediaItemBean);

是罪魁祸首。 downloadDuration在异步模式下运行,并且事先具有durationTextView的引用。假设downloadDuration尚未完成,并且用户滚动ListView。滚动ListView downloadDuration后,此durationTextView将使用值更新,但是ListView项的位置错误,而不是downloadDuration中传递的位置。将final TextView durationTextView;设置为final也不会解决问题。
解决方案可能是显示持续时间的某种空指示符,并让downloadDuration完成它的异步工作。删除durationTextView作为参数,并添加position作为参数。异步作业完成后,您可以为该位置更新MediaItemBean类型的bean列表。现在通知适配器某些值已更改,并且ListView会相应地进行更新。 FYI RecyclerView项更新比ListView更优化。

更新2 :您可以预先获取项目并将其映射到bean。仅一次异步将运行。

更新3 :同时,您可以检查bean是否包含特定的ListView项(如果不为0)。如果为0,则在durationTextView.setText("00:00:00")中显示,否则调用downloadDuration,让它完成并更新bean中的持续时间值。但是仍然需要notify来更新项目。

10-08 17:50