Tim Kranen
Tim Kranen

Reputation: 4222

ListView items not being built correctly

I'm having some issues with my custom ArrayAdapter. I'm trying to build a view based on certain properties, however as soon as I start scrolling (and sometimes even before that), the views aren't build correctly and are shown incorrect. I know this has something to do with the fact that a ListView reuses items, but I can't figure out how to solve it. In theory this code should work.. At least that is what I'm thinking.

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View rowView = convertView;
    if (rowView == null) {
        LayoutInflater inflater = ((Activity) context).getLayoutInflater();
        rowView = inflater.inflate(R.layout.message_view_row, null);
        image = (ImageView) rowView.findViewById(R.id.message_row_image);
        messageText = (TextView) rowView
                .findViewById(R.id.message_row_message);
        senderName = (TextView) rowView
                .findViewById(R.id.message_row_senderName);
        dateText = (TextView) rowView.findViewById(R.id.message_row_date);
        mainLayout = (LinearLayout) rowView
                .findViewById(R.id.messaging_view_mainLayout);
        messageLayout = (LinearLayout) rowView
                .findViewById(R.id.message_row_messageLayout);
    } else {
        image = (ImageView) rowView.findViewById(R.id.message_row_image);
        messageText = (TextView) rowView
                .findViewById(R.id.message_row_message);
        senderName = (TextView) rowView
                .findViewById(R.id.message_row_senderName);
        dateText = (TextView) rowView.findViewById(R.id.message_row_date);
        mainLayout = (LinearLayout) rowView
                .findViewById(R.id.messaging_view_mainLayout);
        messageLayout = (LinearLayout) rowView
                .findViewById(R.id.message_row_messageLayout);
    }

    Message msg = messages.get(position);
    View imageFromMain = mainLayout.getChildAt(0);
    View messageLayoutFromMain = mainLayout.getChildAt(1);
    mainLayout.removeAllViews();
    // handle the view stance
    if (!msg.isOwnMessage()) {
        // switch the views
        mainLayout.addView(messageLayoutFromMain);
        mainLayout.addView(imageFromMain);
        // set the color for the message box
        messageLayout.setBackgroundColor(context.getResources().getColor(
                R.color.friend_message_color));
    } else {
        // switch the views
        mainLayout.addView(imageFromMain);
        mainLayout.addView(messageLayoutFromMain);
        messageLayout.setBackgroundColor(context.getResources().getColor(
                R.color.clanster_color));
    }

    messageText.setText(msg.getMessage());
    senderName.setText(msg.getSenderName());
    Date date = msg.getCreatedAt();
    dateText.setText(dateFormat.format(msg.getCreatedAt()));

    if (msg.getSenderImageUrl() != null
            && !msg.getSenderImageUrl().isEmpty()) {
        Picasso.with(context).load(msg.getSenderImageUrl())
                .transform(new RoundedTransformation()).into(image);
    } else {
        Picasso.with(context).load(R.drawable.defaultimage)
                .transform(new RoundedTransformation()).into(image);
    }

    return rowView;
}

This part where it isn't working is the part where I remove all views from a layout and re-add them in a certain order based on a certain property.

Upvotes: 1

Views: 56

Answers (3)

C0D3LIC1OU5
C0D3LIC1OU5

Reputation: 8680

This is a better way to handle your issue:

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

    LayoutInflater inflater = ((Activity) context).getLayoutInflater();

    if (convertView == null) {
        convertView = inflater.inflate(R.layout.message_view_row, null);
    }

    image = (ImageView) convertView.findViewById(R.id.message_row_image);
    messageText = (TextView) convertView.findViewById(R.id.message_row_message);
    senderName = (TextView) convertView.findViewById(R.id.message_row_senderName);
    dateText = (TextView) convertView.findViewById(R.id.message_row_date);
    mainLayout = (LinearLayout) convertView.findViewById(R.id.messaging_view_mainLayout);
    messageLayout = (LinearLayout) convertView.findViewById(R.id.message_row_messageLayout);

    Message msg = messages.get(position);

    //your problem area is here: 
    //**View imageFromMain = mainLayout.getChildAt(0);**
    //**View messageLayoutFromMain = mainLayout.getChildAt(1);**
    //when the view is recycled, you cannot assume 
    //that the views will be at the needed positions. instead, you should refactor
    //your code to make them separate views, and inflate them like this
    View imageFormMain = inflater.inflate(<your_refactored_view_name1>);
    View messageLayoutFromMain = inflater.inflate(<your_refactored_view_name2>);

    mainLayout.removeAllViews();
    // handle the view stance
    if (!msg.isOwnMessage()) {
        // switch the views
        mainLayout.addView(messageLayoutFromMain);
        mainLayout.addView(imageFromMain);
        // set the color for the message box
        messageLayout.setBackgroundColor(context.getResources().getColor(
                R.color.friend_message_color));
    } else {
        // switch the views
        mainLayout.addView(imageFromMain);
        mainLayout.addView(messageLayoutFromMain);
        messageLayout.setBackgroundColor(context.getResources().getColor(
                R.color.clanster_color));
    }

    messageText.setText(msg.getMessage());
    senderName.setText(msg.getSenderName());
    Date date = msg.getCreatedAt();
    dateText.setText(dateFormat.format(msg.getCreatedAt()));

    if (msg.getSenderImageUrl() != null && !msg.getSenderImageUrl().isEmpty()) {
        Picasso.with(context).load(msg.getSenderImageUrl())
                .transform(new RoundedTransformation()).into(image);
    } else {
        Picasso.with(context).load(R.drawable.defaultimage)
                .transform(new RoundedTransformation()).into(image);
    }

    return convertView;
}

Upvotes: 1

Ibrahim Yildirim
Ibrahim Yildirim

Reputation: 2771

Change the first part of your getView() to this. You are rewriting the code twice, it's not necessary and it looks messy.

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

    if (convertView == null) {
        LayoutInflater mInflator = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = mInflator.inflate(R.layout.message_view_row, parent, null);
    }

    image = (ImageView) convertView.findViewById(R.id.message_row_image);
    messageText = (TextView) convertView
            .findViewById(R.id.message_row_message);
    senderName = (TextView) convertView
            .findViewById(R.id.message_row_senderName);
    dateText = (TextView) convertView.findViewById(R.id.message_row_date);
    mainLayout = (LinearLayout) rowView
            .findViewById(R.id.messaging_view_mainLayout);
    messageLayout = (LinearLayout) convertView
            .findViewById(R.id.message_row_messageLayout);

...

return convertView;

Notice when you are inflating your convertView to pass parent as a parameter

Upvotes: 0

luiscosta
luiscosta

Reputation: 855

Try this:

LayoutInflater inflater = (LayoutInflater) activity
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(layout, parent, false);

Instead of:

View rowView = convertView;

Hope this helps.

Upvotes: 0

Related Questions