1

I am newbie to Android, I am working on a ListView with customlistitems containing Images and some text, I found a problem when i am scrolling up/down the listview the positions of listItems are changing. So can any one help me in this please? I am posting my adapter here for your information. Hope you will help me to figure it out.

OfferAdapter

public class OfferAdapter extends BaseAdapter {
    private Context mContext;
    private ArrayList<Offer> OfferList;


    public OfferAdapter(Context c, ArrayList<Offer> OfferList) {
        mContext = c;
        this.OfferList = OfferList;
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return OfferList.size();
    }

    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        View grid;
        LayoutInflater inflater = (LayoutInflater) mContext
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        if (convertView == null) {

            grid = new View(mContext);
            grid = inflater.inflate(R.layout.raw_offer, null);
            TextView tv_ofr_hdr = (TextView) grid.findViewById(R.id.tv_ofr_hdr);
            ImageView iv_pic = (ImageView) grid.findViewById(R.id.iv_pic);
            TextView tv_ofr_desc = (TextView) grid.findViewById(R.id.tv_ofr_desc);
            TextView tv_date = (TextView) grid.findViewById(R.id.tv_date);
            tv_ofr_desc.setText(OfferList.get(position).getDescription());
            tv_ofr_hdr.setText(OfferList.get(position).getHeadline());
            Date from = new Date();
            Date to = new Date();
            SimpleDateFormat input = new SimpleDateFormat("yyyy-MM-dd");
            SimpleDateFormat output = new SimpleDateFormat("dd/MM/yyyy");
            try {
                from = input.parse(OfferList.get(position).getStart_date());
                to = input.parse(OfferList.get(position).getEnd_date());      // parse input

            } catch (ParseException e) {
                e.printStackTrace();
            }

            tv_date.setText(output.format(from) + " TO " + output.format(to));

            Picasso.with(mContext)
                    .load(OfferList.get(position).getPhoto().replaceAll(" ", "%20"))
                    .placeholder(R.drawable.ic_no_img)
                    .error(R.drawable.ic_no_img)
                    .into(iv_pic);
        } else {
            grid = (View) convertView;
        }

        return grid;
    }
}
Artem Mostyaev
  • 3,874
  • 10
  • 53
  • 60

4 Answers4

1

There are multiple problems:

1. Instead of

@Override
public Object getItem(int position) {
    return null;
}

you should write

@Override
public Object getItem(int position) {
    return OfferList.get(position);
}

2. Instead of

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View grid;
    LayoutInflater inflater = (LayoutInflater) mContext
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    if (convertView == null) {

        grid = new View(mContext);
        grid = inflater.inflate(R.layout.raw_offer, null);

        // [all that initializing stuff]

    } else {
        grid = (View) convertView;
    }

    return grid;
}

you should write

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    LayoutInflater inflater = LayoutInflater.from(mContext);

    View grid;
    if (convertView == null) {
        grid = inflater.inflate(R.layout.raw_offer, parent, false);
    } else {
        grid = convertView;
    }

    // [all that initializing stuff]

    return grid;
}

You can also have a look at the ViewHolder concept to improve the performance of your list:

Headcracker
  • 522
  • 4
  • 19
0

You need to modify your getView() method as follows-

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    // TODO Auto-generated method stub
    View grid = convertView;
    LayoutInflater inflater = (LayoutInflater) mContext
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    if (grid == null) {
        grid = inflater.inflate(R.layout.raw_offer, parent, false);
    }
    TextView tv_ofr_hdr = (TextView) grid.findViewById(R.id.tv_ofr_hdr);
    ImageView iv_pic = (ImageView) grid.findViewById(R.id.iv_pic);
    TextView tv_ofr_desc = (TextView) grid.findViewById(R.id.tv_ofr_desc);
    TextView tv_date = (TextView) grid.findViewById(R.id.tv_date);
    tv_ofr_desc.setText(OfferList.get(position).getDescription());
    tv_ofr_hdr.setText(OfferList.get(position).getHeadline());
    Date from = new Date();
    Date to = new Date();
    SimpleDateFormat input = new SimpleDateFormat("yyyy-MM-dd");
    SimpleDateFormat output = new SimpleDateFormat("dd/MM/yyyy");
    try {
            from = input.parse(OfferList.get(position).getStart_date());
            to = input.parse(OfferList.get(position).getEnd_date());      // parse input

    } catch (ParseException e) {
            e.printStackTrace();
    }

    tv_date.setText(output.format(from) + " TO " + output.format(to));

    Picasso.with(mContext)
                .load(OfferList.get(position).getPhoto().replaceAll(" ", "%20"))
                .placeholder(R.drawable.ic_no_img)
                .error(R.drawable.ic_no_img)
                .into(iv_pic);    
    return grid;
}

and also you need to override these two methods -

@Override
public int getViewTypeCount() {

   return getCount();
}

@Override
public int getItemViewType(int position) {

   return position;
}
Amit Kumar
  • 1,428
  • 2
  • 12
  • 20
  • I got an error "Error:(62, 42) error: variable grid might not have been initialized" – sulphuric Acid Oct 11 '17 at 06:30
  • Now list is coming,but as i am scrolling it crashed with below eeror.java.lang.NullPointerException at com.abc.allaboutcity.adapter.OfferAdapter.getView(OfferAdapter.java:62) at android.widget.AbsListView.obtainView(AbsListView.java:2240) at android.widget.ListView.makeAndAddView(ListView.java:1790) – sulphuric Acid Oct 11 '17 at 06:35
  • can u help me? for this – sulphuric Acid Oct 11 '17 at 06:35
  • Overriding getViewTypeCount() and getItemViewType() that way is wrong. Simple lists that contain only one kind of items usually have only one view type. Then getViewTypeCount() should return 1 and getItemViewType(int position) should return 0; – Headcracker Oct 11 '17 at 07:01
  • It is giving null because I forgot to initialise grid. Check the update to answer. – Amit Kumar Oct 11 '17 at 07:37
0

Change some of your adapter method like -

@Override
    public Offer getItem(int position) {
        // TODO Auto-generated method stub
        return OfferList.get(position);
    }

    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return position;
    }

So your adapter would be -

public class OfferAdapter extends BaseAdapter {
    private Context mContext;
    private ArrayList<Offer> OfferList;


    public OfferAdapter(Context c, ArrayList<Offer> OfferList) {
        mContext = c;
        this.OfferList = OfferList;
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return OfferList.size();
    }

    @Override
    public Offer getItem(int position) {
        // TODO Auto-generated method stub
        return OfferList.get(position);
    }

    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        View grid;
        LayoutInflater inflater = (LayoutInflater) mContext
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        if (convertView == null) {

            grid = new View(mContext);
            grid = inflater.inflate(R.layout.raw_offer, null);
            TextView tv_ofr_hdr = (TextView) grid.findViewById(R.id.tv_ofr_hdr);
            ImageView iv_pic = (ImageView) grid.findViewById(R.id.iv_pic);
            TextView tv_ofr_desc = (TextView) grid.findViewById(R.id.tv_ofr_desc);
            TextView tv_date = (TextView) grid.findViewById(R.id.tv_date);
            tv_ofr_desc.setText(OfferList.get(position).getDescription());
            tv_ofr_hdr.setText(OfferList.get(position).getHeadline());
            Date from = new Date();
            Date to = new Date();
            SimpleDateFormat input = new SimpleDateFormat("yyyy-MM-dd");
            SimpleDateFormat output = new SimpleDateFormat("dd/MM/yyyy");
            try {
                from = input.parse(OfferList.get(position).getStart_date());
                to = input.parse(OfferList.get(position).getEnd_date());      // parse input

            } catch (ParseException e) {
                e.printStackTrace();
            }

            tv_date.setText(output.format(from) + " TO " + output.format(to));

            Picasso.with(mContext)
                    .load(OfferList.get(position).getPhoto().replaceAll(" ", "%20"))
                    .placeholder(R.drawable.ic_no_img)
                    .error(R.drawable.ic_no_img)
                    .into(iv_pic);
        } else {
            grid = (View) convertView;
        }

        return grid;
    }
}
AGM Tazim
  • 2,213
  • 3
  • 16
  • 25
0

Your problem is in your getView() implementation, and comes from a misunderstanding of what the method parameters do and how this method populates the ListView. This is perfectly understandable; ListView is somewhat complex.

The important piece to understand is that ListView will build up a "pool" of views, and then recycle them so that they don't need to be recreated every time you scroll the list. A view that you previously created and returned from getView() can be passed in as the convertView argument; all you need to do in these cases is update the UI of that view.

With that in mind, you could rewrite your getView() as follows:

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

    if (convertView == null) {
        LayoutInflater inflater = (LayoutInflater) mContext
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        grid = inflater.inflate(R.layout.raw_offer, parent, false);
    } else {
        grid = convertView;
    }

    TextView tv_ofr_hdr = (TextView) grid.findViewById(R.id.tv_ofr_hdr);
    ImageView iv_pic = (ImageView) grid.findViewById(R.id.iv_pic);
    TextView tv_ofr_desc = (TextView) grid.findViewById(R.id.tv_ofr_desc);
    TextView tv_date = (TextView) grid.findViewById(R.id.tv_date);
    tv_ofr_desc.setText(OfferList.get(position).getDescription());
    tv_ofr_hdr.setText(OfferList.get(position).getHeadline());
    Date from = new Date();
    Date to = new Date();
    SimpleDateFormat input = new SimpleDateFormat("yyyy-MM-dd");
    SimpleDateFormat output = new SimpleDateFormat("dd/MM/yyyy");
    try {
        from = input.parse(OfferList.get(position).getStart_date());
        to = input.parse(OfferList.get(position).getEnd_date());      // parse input
    } catch (ParseException e) {
        e.printStackTrace();
    }

    tv_date.setText(output.format(from) + " TO " + output.format(to));

    Picasso.with(mContext)
            .load(OfferList.get(position).getPhoto().replaceAll(" ", "%20"))
            .placeholder(R.drawable.ic_no_img)
            .error(R.drawable.ic_no_img)
            .into(iv_pic);

    return grid;
}

There are other optimizations you could make as well. The simplest is to get rid of the View grid variable; you can just use convertView directly. More complex would be to use the "view holder" pattern to cache the result of your various findViewById() calls.

Ben P.
  • 52,661
  • 6
  • 95
  • 123
  • Further reading regarding the view holder pattern: https://developer.android.com/training/improving-layouts/smooth-scrolling.html#ViewHolder – Ben P. Oct 11 '17 at 07:20