Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing ViewHolder pattern on a ListView that includes ViewPager

I have a layout which includes a ViewPager among other, simpler Views. I am using this layout as a list item for a ListView and it works fine. But when I try to implement a ViewHolder pattern, the application crashes when I scroll down. The error occurs when I set the adapter to the view pager.

The adapter without the ViewHolder implementation:

public class FaceInSandAdapter extends ArrayAdapter<Gem> {
private Context mContext;

    public FaceInSandAdapter(Context c, ArrayList<Gem> gems) {
    super(c, 0, gems);
    mContext = c;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    final Gem gem = getItem(position);

    convertView = ((Activity) mContext).getLayoutInflater().inflate(
            R.layout.list_item_gem, null);
    final ViewPager pager = (ViewPager) convertView
            .findViewById(R.id.list_item_gem_photosViewPager);
    pager.setId(gem.getId());

    pager.setAdapter(new PhotoSliderPagerAdapter(((FragmentActivity) parent
        .getContext()).getSupportFragmentManager(), gem.getImageUrls()));

    TextView address = (TextView) convertView
        .findViewById(R.id.list_item_gem_addressTextView);

    address.setText(gem.getAddress());
    return convertView;

    }
}

How do I correctly implement a ViewHolder pattern for the adapter that inflates a layout which contains a ViewPager? The following is my attempt at implementing such adapter, which gives the above mentioned error:

public class FaceInSandAdapter extends ArrayAdapter<Gem> {
private Context mContext;

public FaceInSandAdapter(Context c, ArrayList<Gem> gems) {
    super(c, 0, ads);
    mContext = c;
}

static class ViewHolder {
    ViewPager pager;
    TextView address;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    final ViewHolder holder;

    final Gem gem = getItem(position);

    if (convertView == null) {
        convertView = ((Activity) mContext).getLayoutInflater().inflate(
                R.layout.list_item_gem, null);

        holder = new ViewHolder();
        holder.pager = (ViewPager) convertView
                .findViewById(R.id.list_item_gem_photosViewPager);


        holder.address = (TextView) convertView
                .findViewById(R.id.list_item_gem_addressTextView);

        convertView.setTag(holder);

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

    FragmentManager fm = ((FragmentActivity) convertView.getContext())
            .getSupportFragmentManager();
    holder.pager.setId(gem.getId());
    holder.pager.setAdapter(new PhotoSliderPagerAdapter(fm, gem
            .getImageUrls()));

    holder.address.setText(gem.getAddress());

    return convertView;

    }
}

EDIT

Stacktrace for the error I get with my implementation of ViewHolder pattern:

FATAL EXCEPTION: main
Process: xx.yyy.zzzzzz, PID: 5010
android.content.res.Resources$NotFoundException: Unable to find resource ID #0x3fe1
at android.content.res.Resources.getResourceName(Resources.java:1776)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:919)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1467)
at android.support.v4.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:472)
 at android.support.v4.app.FragmentStatePagerAdapter.finishUpdate(FragmentStatePagerAdapter.java:163)
at android.support.v4.view.ViewPager.populate(ViewPager.java:1068)
at android.support.v4.view.ViewPager.populate(ViewPager.java:914)
at android.support.v4.view.ViewPager.setAdapter(ViewPager.java:442)
at xxx.xxx.xxx.FaceInSandAdapter.getView(FaceInSandAdapter.java:89)
at android.widget.AbsListView.obtainView(AbsListView.java:2240)
at android.widget.ListView.makeAndAddView(ListView.java:1790)
at android.widget.ListView.fillDown(ListView.java:691)
at android.widget.ListView.fillGap(ListView.java:655)
at android.widget.AbsListView.trackMotionScroll(AbsListView.java:5136)
at android.widget.AbsListView$FlingRunnable.run(AbsListView.java:4247)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
at android.view.Choreographer.doCallbacks(Choreographer.java:574)
at android.view.Choreographer.doFrame(Choreographer.java:543)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5102)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
at dalvik.system.NativeStart.main(Native Method)

**EDIT 2 **

Now, as the viepager is working as a listview item, the problem that remains is that I am instantiating ImageViews, having a bare bones implementation of the PagerAdapter (no saving state, no limiting offscreen views), so the list ends up using big amount of memory.

like image 471
Sergo Pasoevi Avatar asked May 08 '26 11:05

Sergo Pasoevi


1 Answers

After trying a few alternatives, like a HorizontalScrollView or some external libraries, I still came back to how I was implementing this from the beginning. I solved this problem by extending the PagerAdapter directly instead of FragmentStatePagerAdapter, and overriding instantiateItem. The problem was with using the fragments, and it seems obvious now: Using FragmentStatePagerAdapter was really unneeded, as I was just trying to make a photo slider, so using fragments was unnecessary.

Here is my code for the new PhotoPagerAdapter:

public class PhotoPagerAdapter extends PagerAdapter {
private Context mContext;
private List<String> mUrls;

public PhotoPagerAdapter(Context context, List<String> urls) {
    mContext = context;
    mUrls = urls;
}

@Override
public int getCount() {
    return mUrls.size();
}

@Override
public boolean isViewFromObject(View view, Object object) {
    return view == ((RelativeLayout) object);
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
    ImageView imgDisplay;

    LayoutInflater inflater = (LayoutInflater) mContext
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View v = inflater.inflate(R.layout.fragment_gem_photo, container, false);

    imgDisplay = (ImageView) v
            .findViewById(R.id.fragment_gem_photo_imageView);
    Picasso.with(mContext).load(mUrls.get(position))
            .placeholder(R.drawable.wait_image).into(imgDisplay);
    ((ViewPager) container).addView(v);

    return v;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    ((ViewPager) container).removeView((RelativeLayout) object);

}
}

EDIT

I figured out the problem isn't with the incorrect implementation of the ViewHolder pattern at all. Instead, it is with reusing the already inflated convertView when it contains a viewpager with the FragmentStatePagerAdapter.

like image 177
Sergo Pasoevi Avatar answered May 10 '26 01:05

Sergo Pasoevi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!