Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Textview Lazyloading, setText at wrong position in gridview?

I have created a gridview, which shows videos from server. GridItem has video thumb image, and video duration.for loading video's thumb I am using UniversalImageloader and loading video duration by creating lazyloading using asynctask.Lazyloading works fine. but if someone scrolls gridview freequently,then video duration shows at wrong position. for creating Lazyloading I am following bellow 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);
    }
}
like image 993
Sandeep Tiwari Avatar asked Oct 18 '22 17:10

Sandeep Tiwari


1 Answers

Get rid of view and use convertView as you receive from method. Change to the following

//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 ");
}

Now use mediaItemViewHolder.durationTextView.setText(....)

Update 1: I have figured out the problem. Why lazyloading update video duration at wrong gridview position. Discard the above if you want

downloadDuration(resId, durationTextView, mediaItemViewHolder.highlightTagIcon, mediaItemBean);

is the culprit. downloadDuration runs in async mode and it has the reference of durationTextView in before hand. Suppose the downloadDuration is not finished and user scrolls the ListView. After scrolling the ListView downloadDuration is finished, then this durationTextView will be updated with the value but for the wrong ListView item position and not for which position this was passed in downloadDuration. Making final TextView durationTextView; it as final will also not solve the problem. Solution could be to show some kind of empty indicatior for duration and let the downloadDuration finishes it's async job. Remove durationTextView as a parameter and add position as a parameter. After async job is done, you can update the bean list of type MediaItemBean for that position. Now notify the adapter that some value has been changed and the ListView will update itself accordingly. FYI RecyclerView item update is more optimised than ListView.

Update 2: You can fetch the items in advance and just map it to bean. Only one time async will run.

Update 3: In the meantime you can check bean for the particular ListView item if it is 0 or not. If it is 0 show in the durationTextView.setText("00:00:00") else call downloadDuration and let it finish and update the duration value in bean. But still you need to notify for updating item.

like image 106
Anurag Singh Avatar answered Oct 21 '22 04:10

Anurag Singh