【问题标题】:Implementing ViewHolder pattern on a ListView that includes ViewPager在包含 ViewPager 的 ListView 上实现 ViewHolder 模式
【发布时间】:2014-03-13 09:32:56
【问题描述】:

我有一个布局,其中包括一个 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;

    }
}

如何为包含 ViewPager 的布局膨胀的适配器正确实现 ViewHolder 模式?以下是我实现此类适配器的尝试,它给出了上述错误:

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 **

现在,由于 viepager 作为列表视图项工作,剩下的问题是我正在实例化 ImageViews,具有 PagerAdapter 的基本实现(没有保存状态,没有限制屏幕外视图),所以列表结束占用大量内存。

【问题讨论】:

  • 真的需要这个视图持有者模式吗?你的盖子里有多少东西?
  • 我最多有 10 个视图,为简洁起见,我在这里省略了。
  • 你能发布异常堆栈跟踪吗?
  • @fremmedehenvendelser 忘记持有人模式:SimpleCursorAdapter 不使用它,但我不会说它很慢
  • @pskink 请注意,我懒惰地从远程服务器加载列表视图的数据。

标签: android listview android-viewpager adapter android-adapter


【解决方案1】:

在尝试了一些替代方案(如 Horizo​​ntalScrollView 或一些外部库)之后,我仍然回到了我从一开始就实现它的方式。我通过直接扩展 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 模式的错误实现。相反,当它包含一个带有 FragmentStatePagerAdapter 的 viewpager 时,它是重用已经膨胀的 convertView。

【讨论】:

    【解决方案2】:

    阅读您发布的堆栈跟踪:

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

    您引用的 id 似乎不存在。这导致您的应用程序崩溃,而不是持有人模式。

    【讨论】:

    • 我如何解释我引用了一个不存在的 id?
    • 那么为什么没有 ViewHolder 我不会得到同样的错误呢?
    • 您介意解释否决票吗?我相信您的第一条评论的答案从我的回答中清晰可见。您似乎缺少持有者版本中的setId() 方法,这可能解释了为什么找不到资源。
    • 提到的 ViewPager 已经在 xml 中设置了一个 id。我在每个实例上再次对它执行setId(),以便正确地拥有它的多个实例。问题似乎出在其他地方,因为错误发生在我将适配器设置为 ViewPager 时,而不仅仅是在我引用它时。
    • 令人困惑。在非持有者版本中,您使用与特定 Gem 对应的特定参数显式调用 setId()。在持有人版本中,您不需要像在 XML 中设置的那样。那么为什么不在非持有者版本中也使用 XML 呢?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多