Dynamically added bitmaps to Android listview on scrolling down make list jumping
Asked Answered
W

0

0

I use custom ArrayAdapter to populate listview.
I use Picasso to load images, before loading image I calculate height and width for each image. Thus dynamic ImageView has different height and width. When I scroll up the listview, everything is smooth. But when I scroll the listview down, list starts to jump, when it comes to the rows with images.
I think, it caused by listview elements recycle, it forgets dynamic imageviews heights and produce this jumping effect when it recalculating imageviews again. I attach my dynamic imageview to Holder, but it doesn't help.
Part of my adapter looks like this:

@Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ChatMessageElement el = list.get(position);
        ViewHolder holder = null;
        NewMessagesLabelHolder labelHolder = null;

        if (convertView == null) {
            convertView = el.getView(inflater, parent);

            if (el.isMessage()) {

                holder = new ViewHolder();
                holder.messageLayout = (RelativeLayout) convertView.findViewById(R.id.message_container);
                holder.messageContent = (LinearLayout) convertView.findViewById(R.id.message_content);
                holder.bottomIndicator = (LinearLayout) convertView.findViewById(R.id.bottom_indicators);
                holder.dateTextView = (TextView) convertView.findViewById(R.id.message_date);
                holder.timeAgo = (TextView) convertView.findViewById(R.id.time_ago);
                holder.nameTextView = (TextView) convertView.findViewById(R.id.user_name);
                convertView.setTag(holder);
            }

        } else {
            if (el.isMessage()) {
                holder = (ViewHolder) convertView.getTag();
            }

        }

        if (el.isMessage()) {
            Message currentMessage = (Message) el;
            drawMessage(holder, currentMessage, position);
        }

        return convertView;
    }


private void drawMessage(ViewHolder holder, Message message, int position) {
        String date = message.getCreatedAt();
        String formattedDate = Helper.getInstance().formatDate("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "HH:mm", date);

        String userName = message.getUserName();

        holder.likesLabelImageView.setVisibility(View.GONE);
        holder.likesCountTextView.setVisibility(View.GONE);
        holder.nameTextView.setText(userName);
        holder.dateTextView.setText(formattedDate);

        if (message.isLiked()) {
            holder.likesCountTextView.setText(Integer.toString(message.getLikesCount()));
            holder.likesCountTextView.setVisibility(View.VISIBLE);
            holder.likesLabelImageView.setVisibility(View.VISIBLE);
        }

        List<MessageComponent> messageComponentList;
        messageComponentList = message.getMessageComponents();

        drawMessageContent(holder, messageComponentList, message);

        holder.nameTextView.setTag(position);
        holder.avatarImageView.setTag(position);
        holder.nameTextView.setOnClickListener(userClickListener);
        holder.avatarImageView.setOnClickListener(userClickListener);

        // hang empty onLingClickListener to display context menu when
        // long click on whole message
        holder.nameTextView.setOnLongClickListener(longClickListener);
        holder.avatarImageView.setOnLongClickListener(longClickListener);
    }


private void drawMessageContent(ViewHolder holder, final List<MessageComponent> messageComponentList, final Message msg) {

        holder.messageContent.removeAllViewsInLayout();
        int messageComponentListSize = messageComponentList.size();

        for (final MessageComponent messageComponent : messageComponentList) {
            messageComponentListSize--;
            if (messageComponentListSize == 0)
                iAmLast = true;
            final String type = messageComponent.getType();

            if (type.equals(MessageComponent.MESSAGE_COMPONENT_TEXT_TYPE)) {

                TextView textView = new TextView(context);
                textView.setText(messageComponent.getText());
                setViewBackground(textView, msg);
                //reset margins for texts, caused by margin changes for images
                RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) holder.bottomIndicator.getLayoutParams();
                params.setMargins(45, -82, 0, 0);
                holder.bottomIndicator.setLayoutParams(params);

                holder.messageContent.addView(textView);
            }

            if (type.equals(MessageComponent.MESSAGE_COMPONENT_IMAGE_TYPE)) {


                ViewGroup.LayoutParams params = new ActionBar.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        thumbHeight
                );

                final RoundedImageView imageView = new RoundedImageView(context);
                imageView.setPadding(20, 0, 20, 20);
                imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
                imageView.setCornerRadius(15.0f);

                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        drawPreview(messageComponent, imageView);
                    }
                });
                imageView.setLayoutParams(params);
                // hang empty onLingClickListener to display context menu when
                // long click on whole message
                imageView.setOnLongClickListener(longClickListener);

                final RelativeLayout mediaContainer = new RelativeLayout(context);

                mediaContainer.addView(imageView);


         }
    }
}


// Calculates restricted dimensions with a maximum of $goal_width by $goal_height
    private ImageSize resize_dimensions(float goal_width, float goal_height, float width, float height) {

        float ratio = Math.min(goal_width/width, goal_height/height);
        int nwidth = Math.round(width*ratio);
        int nheight = Math.round(height*ratio);

        if(nwidth>nheight*2)
            nheight = 400;
        if(nheight>nwidth*2)
            nwidth = 600;

        ImageSize imageSize = new ImageSize(nwidth, nheight);
        return imageSize;

    }


    private void drawPreview(MessageComponent messageComponent, final ImageView imageView) {
        String type = messageComponent.getType();
        String mediaPath = messageComponent.getMediaPath();
        String thumbPath = messageComponent.getThumbPath();
        String thumbUrl = messageComponent.getThumbUrl();
        String videoThumbPath = messageComponent.getVideoThumbPath();
        Uri uri = null;

        if (type.equals(MessageComponent.MESSAGE_COMPONENT_IMAGE_TYPE)) {
            if (!TextUtils.isEmpty(mediaPath)) {
                uri = Uri.parse("file://" + mediaPath);
                File file = new File(uri.getPath());
                if (file.exists()) {
                    resizeAndLoadThumbnail(uri, imageView);
                    return;
                }
            }

            if (!TextUtils.isEmpty(thumbPath)) {
                uri = Uri.parse("file://" + mediaPath);
                File file = new File(uri.getPath());
                if (file.exists()) {
                    resizeAndLoadThumbnail(uri, imageView);
                    return;
                }
            }


            if (thumbUrl != null) {
                uri = Uri.parse(thumbUrl);
            }

            if (uri != null) {
                resizeAndLoadThumbnail(uri, imageView);
                return;
            }
        }


    }

    private void resizeAndLoadThumbnail(Uri uri, final ImageView imageView) {
        Picasso.with(context).load(uri).into(new Target() {
            @Override
            public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                int width = bitmap.getWidth();
                int height = bitmap.getHeight();
                ImageSize imgSize = resize_dimensions(900, 900, width, height);

                RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
                        imgSize.width,
                        imgSize.height
                );

                imageView.setLayoutParams(params);
                imageView.setImageBitmap(bitmap);
            }

            @Override
            public void onBitmapFailed(Drawable errorDrawable) {

            }

            @Override
            public void onPrepareLoad(Drawable placeHolderDrawable) {

            }

        });
    }
Womanhater answered 23/12, 2014 at 8:23 Comment(8)
Does it really have to be measured in code? You make the measurements and redraw image all the time. If so, maybe try to implement some caching on level of choosing whether or not measure and load image? Set the flag for each item from getItem(int) and if returning true, skip all the measurements. You have to also refresh visible items using getLastVisibleItem() or something similar.Machination
Why I should refresh visible items?Womanhater
I might be wrong but I always thought that happens when you swipe the list.Machination
Erase that, it was stupid. In that case, event one element can be that troublesome. Maybe try to get rid of measuring at least?Machination
Yes, I thought I better measure thumbnail size at first time, and store sizes locally and get them from there, to prevent remeasure each time.Womanhater
Well, the problem is that while your view is outside of bounds it gets cropped to visible part. I know developer.android.com/reference/android/support/v4/widget/… is changing its measurements dynamically and while it certainly has image in it, it does not freeze. Maybe this will be helpful?Machination
Thank you dominik4142, your comments helped me to solve the problem. I stored sizes in local db. Thus I didn't have to recalculate image sizes each time. Now my scroll works smoothly.Womanhater
@RafaelMuhamedzyanov, please, say, how have you solved it? I also try to calculate an ImageView height and store it to a ArrayList. Then when I scroll ListView up, I get this height and assign to ImageView. Anyway, it jumps.Lucillelucina

© 2022 - 2024 — McMap. All rights reserved.