0

I managed to implement ViewHolder in my adapter but since I am rather new to this, I am not sure if the following implementation is correct. I have 10 elements in my array at the moment but I spent around 3-4 minutes debugging and I'm not sure I understand the workflow of the View Holder. I know this is not necessarily a question, but I would like to have this checked in case it's written badly.

@Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        COLOR_GREEN = R.color.ck_in_category_green;
        View rowView = convertView;
        ViewHolder holder;

        int type = getItemViewType(position);
        if (convertView == null) {
            holder = new ViewHolder();
            switch (type) {
                case TYPE_MINE_OR_ACCEPTED:
                    convertView = View.inflate(context, R.layout.item_notif_mine_or_accepted, null);
                    rowView = handleViewForMineOrAccepted(holder, convertView, checkIns.get(position));
                    break;
                case TYPE_TAGGING:
                    convertView = View.inflate(context, R.layout.item_notif_tagged_or_invited, null);
                    rowView = handleViewForTaggingOrInvitation(holder, convertView, checkIns.get(position), true);
                    break;
                case TYPE_INVITATION:
                    convertView = View.inflate(context, R.layout.item_notif_tagged_or_invited, null);
                    rowView = handleViewForTaggingOrInvitation(holder, convertView, checkIns.get(position), false);
                    break;
            }
            rowView.setTag(holder);
        } else {
            holder = (ViewHolder) rowView.getTag();
            switch (type) {
                case TYPE_MINE_OR_ACCEPTED:
                    rowView = handleViewForMineOrAccepted(holder, convertView, checkIns.get(position));
                    break;
                case TYPE_TAGGING:
                    rowView = handleViewForTaggingOrInvitation(holder, convertView, checkIns.get(position), true);
                    break;
                case TYPE_INVITATION:
                    rowView = handleViewForTaggingOrInvitation(holder, convertView, checkIns.get(position), false);
                    break;
            }
        }
        return rowView;
    }

One of the handleView methods :

private View handleViewForMineOrAccepted(final ViewHolder holder, View view, final Checkin checkin) {
        holder.checkInPicture = (ImageView) view.findViewById(R.id.mine_or_accepted_check_in_picture);
        holder.locationSmall = (TextView) view.findViewById(R.id.check_in_location_small);
        holder.locationBig = (TextView) view.findViewById(R.id.check_in_location_big);
        holder.creatorFrame = (RelativeLayout) view.findViewById(R.id.check_in_creator_frame);

        holder.tagged1Layout = (RelativeLayout) view.findViewById(R.id.notif_check_in_tagged_1);
        holder.tagged2Layout = (RelativeLayout) view.findViewById(R.id.notif_check_in_tagged_2);
        holder.tagged3Layout = (RelativeLayout) view.findViewById(R.id.notif_check_in_tagged_3);

        holder.time = (TextView) view.findViewById(R.id.join_request_check_in_time);
        holder.calendar = (ImageView) view.findViewById(R.id.join_request_time_icon);

        holder.creator = (ImageView) view.findViewById(R.id.confirmed_friend_1);
        holder.tagged1 = (ImageView) view.findViewById(R.id.confirmed_friend_2);
        holder.tagged2 = (ImageView) view.findViewById(R.id.confirmed_friend_3);
        holder.tagged3 = (ImageView) view.findViewById(R.id.confirmed_friend_4);
        holder.tagged1Layout.setVisibility(View.GONE);
        holder.tagged2Layout.setVisibility(View.GONE);
        holder.tagged3Layout.setVisibility(View.GONE);

followed by some logic, click events, etc.

Alex
  • 108
  • 1
  • 11
  • do you really need to use view holder pattern? note that none of google's adapters (used by an AdapterView) use it – pskink May 05 '15 at 10:44
  • I guess I do have to use it, since I need 3 layouts with a lot of data to be displayed – Alex May 05 '15 at 10:47
  • if you have problems implementing them, just forget, view holder doesn't give any visible performance speedup, its a myth – pskink May 05 '15 at 10:51
  • the viewholder is definitly not a myth - you use it to restore the inflated XML - because creating the views takes time and it speeds up the scrolling performance a LOT! your cood looks fine - but you don't need to call findViewById everytime - in general we use 2 methods: newView(int viewtype) which generates the viewholder and calls the findViewById's bindView(viewHolder, object) which actually sets the values to the object... – Lukas Olsen May 05 '15 at 11:05
  • The newView method, I assume that's different from adapter to adapter :) I use a BaseAdapter in this case @LukasOlsen – Alex May 05 '15 at 11:09
  • i answered for better code highlighting and stuff like this – Lukas Olsen May 05 '15 at 11:15
  • @LukasOlsen view holder and reusing the item views ("because creating the views takes time and it speeds up the scrolling performance a LOT") are **COMPLETLY** two different things: see `convertView` parameter in `getView` method, also: none of google's adapters use view holder pattern, so it means that it is not really a speed daemon, see: http://daniel-codes.blogspot.com/2013/11/is-findviewbyid-slow.html – pskink May 05 '15 at 11:27
  • sorry misread your first comment - ofc findViewById is not that big of a deal but it's not that big of an hassle so why don't do it. while working with recyclerview it's even nicer if you do it in the constructor of the viewHolder. – Lukas Olsen May 05 '15 at 11:30

2 Answers2

0

From my experience its the best to extend BaseAdapter to generate clean methods for binding Views to ViewHolder - thats a BaseAdapter how i use it everywhere (where i'm still using listview instead of recyclerview)

public abstract class MyBaseAdapter extends BaseAdapter {

protected LayoutInflater inflater;
protected Context context;

public TikBaseAdapter(Context context) {
    this.context = context;
    this.inflater = (LayoutInflater) context
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

public final View getView(int position, View convertView, ViewGroup parent) {
    int type = getItemViewType(position);
    if (convertView == null) {
        convertView = newView(type, parent);
    }
    bindView(position, type, convertView);
    return convertView;
}

/** Create a new instance of a view for the specified {@code type}. */
public abstract View newView(int type, ViewGroup parent);

/** Bind the data for the specified {@code position} to the {@code view}. */
public abstract void bindView(int position, int type, View view);



}

your code looks good as far as i can see - but you're calling findView everytime when "binding" the view - that's unneccessary.

with my BaseAdapter you set the viewHolder to the convertView in newView() and already call findViewById to reference all the views. in bindView() you just get the viewHolder via convertView.getTag() and set things on the views e.g.: holder.title.setText(item.getTitle())

Lukas Olsen
  • 5,294
  • 7
  • 22
  • 28
0

Also consider using RecyclerView. It was introduced in 5.0 (available in the support library). RecyclerView uses the view holder pattern as part of it's implementation and allows more flexibilty than ListView did.
You can read the guide on using it for various scenarios on Android Developers

Alex.F
  • 5,648
  • 3
  • 39
  • 63