Search code examples
androidandroid-layoutlistviewcustom-adaptercustom-lists

Android, dynamic Listview layout changing on scroll


I have a conversation mode in my application where I wish to load one layout for one user and another layout for the other. It need not always be alternating hence I cannot use a simple "%2" to achieve it. Based on a flag I am assigning a dynamic layout, which works. My problem is that as I scroll the layouts get distorted as in, conversation of user_1 will get layout_2 or conversation of user_2 will get layout_1, absolutely random.

I did something similar to an answer I saw here: https://stackoverflow.com/a/16774696/4810718

There were a few posts about randomized data. That is not my issue, the order of the list items does not change however the layout get's randomly applied. I read that the items in view + 1 are kept in temporary memory, regarding this another thing I noticed was: as I keep adding items such that the scrollbar comes into picture when I add a second item outside the visibility it tends to get the layout of the topmost item (first item) visible. Scrolling would later give me seemingly randomized results.

public class ConversationAdapter extends BaseAdapter
{
    private LayoutInflater inflater;
    private ArrayList<ConversationContent> objects;
    ImageView user;
    static int ind = 0;

    private class ViewHolder
    {
        TextView textView1;
        TextView textView2;
        TextView textView3;
    }

    public ConversationAdapter(Context context, ArrayList<ConversationContent> objects)
    {
        inflater = LayoutInflater.from(context);
        this.objects = objects;
    }

    public int getCount()
    {
        return objects.size();
    }

    public ConversationContent getItem(int position)
    {
        return objects.get(position);
    }

    public long getItemId(int position)
    {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent)
    {
        ViewHolder holder = null;
        if(convertView == null)
        {
            holder = new ViewHolder();
            if (Main_Page3.convFlag == 1)
            {
                convertView = inflater.inflate(R.layout.conversation_item_1, null);
            }
            else
            {
                convertView = inflater.inflate(R.layout.conversation_item_2, null);
            }

            holder.textView1 = (TextView) convertView.findViewById(R.id.trans);
            holder.textView1.setTypeface(Main_Activity.fontC);
            holder.textView2 = (TextView) convertView.findViewById(R.id.lang);
            holder.textView2.setTypeface(Main_Activity.fontC);
            holder.textView3 = (TextView) convertView.findViewById(R.id.user);
            holder.textView3.setTypeface(Main_Activity.fontC);
            convertView.setTag(holder);
        } 
        else
        {
            holder = (ViewHolder) convertView.getTag();     
        }

        holder.textView1.setText(objects.get(position).getTranslatedText());
        holder.textView2.setText(objects.get(position).getTranslationString());
        SpannableString originalTextString = new SpannableString("\n" + objects.get(position).getOriginalText());
        originalTextString.setSpan(new RelativeSizeSpan(0.5f), 0, originalTextString.length(), 0);
        holder.textView1.append(originalTextString);
        holder.textView3.setText(objects.get(position).getUser());

        return convertView;
    }
}

So, that's the code I've written. A possible solution I thought of was if I used an array of views and loaded them accordingly, it may work? I'm really not really sure how I should be going about doing this - I'm still pretty new to Android. I've searched a bit but could not get a helpful solution. Please direct me to a helpful solution you find or, a working answer would be most appreciable. Thank you.


Solution

  • I think the best way to achieve what you want is to put the flag to determine which layout to use on your ConversationContent object, then override getViewTypeCount() and getItemViewType(int position) something like this:

    @Override
    int getViewTypeCount() {
        return 2;
    }
    
    @Override
    int getItemViewType(int position) {
        if (objects.get(position).isReply()) {  //isReply can be whatever you want to determine whether to change layout
            return 1;
        }
        return 0;
    }
    
    
    @Override
    public View getView(int position, View convertView, ViewGroup parent)
    {
        ViewHolder holder = null;
        if(convertView == null)
        {
            holder = new ViewHolder();
            if (getItemViewType(position) == 1)
            {
                convertView = inflater.inflate(R.layout.conversation_item_1, null);
            }
            else
            {
                convertView = inflater.inflate(R.layout.conversation_item_2, null);
            }
    
            holder.textView1 = (TextView) convertView.findViewById(R.id.trans);
            holder.textView1.setTypeface(Main_Activity.fontC);
            holder.textView2 = (TextView) convertView.findViewById(R.id.lang);
            holder.textView2.setTypeface(Main_Activity.fontC);
            holder.textView3 = (TextView) convertView.findViewById(R.id.user);
            holder.textView3.setTypeface(Main_Activity.fontC);
            convertView.setTag(holder);
        } 
        else
        {
            holder = (ViewHolder) convertView.getTag();     
        }
    
        holder.textView1.setText(objects.get(position).getTranslatedText());
        holder.textView2.setText(objects.get(position).getTranslationString());
        SpannableString originalTextString = new SpannableString("\n" + objects.get(position).getOriginalText());
        originalTextString.setSpan(new RelativeSizeSpan(0.5f), 0, originalTextString.length(), 0);
        holder.textView1.append(originalTextString);
        holder.textView3.setText(objects.get(position).getUser());
    
        return convertView;
    }