在包含ViewPager的ListView上实现ViewHolder模式

3

我是一名能够提供翻译的助手。

我有一个布局,其中包括一个 ViewPager 和其他简单的视图。我将此布局用作 ListView 的列表项,它可以正常工作。但是当我尝试实现 ViewHolder 模式时,当我向下滚动时应用程序会崩溃。错误发生在将适配器设置为视图页时。

适配器没有使用 ViewHolder 实现:

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;

    }
}

如何正确实现ViewHolder模式,用于适配器(adapter)中的ViewPager所填充的布局?以下是我尝试实现这样一个适配器的代码,但会产生上述提到的错误:

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;

    }
}

编辑

我在实现ViewHolder模式时遇到错误,以下是错误的堆栈跟踪信息:

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)

**编辑2**

现在,由于viewpager作为listview的一个item正在工作,剩下的问题是我正在实例化ImageViews,并且PagerAdapter只有最基本的实现(没有保存状态,没有限制离屏视图),因此列表最终使用了大量的内存。


1
你能发布异常堆栈跟踪吗? - Tadej
@pskink,CursorAdapter是针对数据库的,而我通过Web服务调用加载列表视图的行。 - Sergo Pasoevi
@fremmedehenvendelser 为什么你想要实现 Holder 模式? - pskink
@pskink 用于使滚动更加流畅。 - Sergo Pasoevi
@fremmedehenvendelser 我不知道,也许可以使用加载器? - pskink
显示剩余8条评论
2个回答

2

在尝试了一些替代方案,比如 HorizontalScrollView 或一些外部库后,我最终回到了一开始的实现方式。我通过直接扩展 PagerAdapter 而不是 FragmentStatePagerAdapter,并覆盖 instantiateItem 来解决了这个问题。问题在于使用了片段,现在看来似乎很明显:使用 FragmentStatePagerAdapter 完全没有必要,因为我只是想制作一个照片幻灯片,所以使用片段是不必要的。

以下是我新的 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);

}
}

编辑

我发现问题并不在于 ViewHolder 模式的实现不正确,而是当已经充气的 convertView 包含一个带有 FragmentStatePagerAdapter 的 viewpager 时重复使用它。


0

请阅读您发布的堆栈跟踪:

android.content.res.Resources$NotFoundException: Unable to find resource ID #0x3fe1

似乎您正在引用一个不存在的 ID。这导致您的应用程序崩溃,而不是 holder 模式。


我该如何解释我正在引用一个不存在的ID? - Sergo Pasoevi
为什么没有 ViewHolder 我就不会得到同样的错误呢? - Sergo Pasoevi
你介意解释一下为什么要点踩吗?我认为你第一个评论的答案在我的回答中已经很清楚了。你好像漏掉了holder版本中的setId()方法,这可能解释了为什么找不到资源。 - Tadej
ViewPager已经在xml中设置了id。我在每个实例上都执行setId(),以便正确地拥有多个实例。问题似乎出在其他地方,因为错误发生在我将适配器设置到ViewPager时,而不仅仅是引用它时。 - Sergo Pasoevi
混淆。在非 holder 版本中,您明确调用 setId() 并使用特定参数对应于特定的 Gem。在 holder 版本中,您不需要这样做,因为它被认为是在 XML 中设置的。那么为什么在非 holder 版本中不使用 XML 呢? - Tadej
抱歉,在将代码复制到浏览器窗口时,我可能错过了它。我也会在 holder 版本上执行 setId() - Sergo Pasoevi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接