Viewpager在recyclerview适配器里面

时间:2017-04-26 11:48:51

标签: android android-recyclerview android-viewpager android-adapter

我正在开发一个应用程序,其中有一个项目列表,每个项目都包含一个viewpager,在viewpager下面你有其他小部件(文本,按钮......等)。我必须从服务器加载图像的url并在我的viewpager中显示图像

这是我的Viewpager适配器:

android:windowSoftInputMode

}

以下是我的回收商视图项目的一部分:

public class ImageAdapter extends PagerAdapter {

private Context mContext;
private ArrayList<String> imagePaths;
private static final String TAG = ImageAdapter.class.getName();
// constructor
public FullScreenImageAdapter(Context context, ArrayList<String> imagePaths){
    this.mContext = context;
    this.imagePaths = imagePaths;
}

@Override
public int getCount() {
    return null == imagePaths ? 0 : this.imagePaths.size();
}

@Override
public Object instantiateItem(final ViewGroup container, final int position) {

    final LayoutInflater inflater = LayoutInflater.from(mContext);
    final View viewLayout = inflater.inflate(R.layout.full_screen_image, container, false);

    final ImageView image = (ImageView) viewLayout.findViewById(R.id.image);
    final ProgressBar progressBar = (ProgressBar)viewLayout.findViewById(R.id.pBar);

    String url = BuildConfig.URL_API_IMAGES;
    final SparseArray<Object> mBitmapMap = new SparseArray<>();

    Glide.with(mContext)
         .load(url + imagePaths.get(position)).asBitmap()
         .placeholder(R.drawable.car)
         .dontAnimate()
         .into(new BitmapImageViewTarget(image){
             @Override
             public void onLoadStarted(Drawable placeholder){
                 super.onLoadStarted(placeholder);
                 image.setImageDrawable(placeholder);
             }

             @Override
             public void onResourceReady(Bitmap resource,
                     GlideAnimation<? super Bitmap> glideAnimation){
                 super.onResourceReady(resource, glideAnimation);
                 mBitmapMap.put(position, resource);
                 progressBar.setVisibility(View.INVISIBLE);
                 image.setImageBitmap(resource);
             }

             @Override
             public void onLoadFailed(Exception e, Drawable errorDrawable){
                 super.onLoadFailed(e, errorDrawable);
                 progressBar.setVisibility(View.INVISIBLE);
             }
         });

    container.addView(viewLayout);
    return viewLayout;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    try{
        container.removeView((RelativeLayout) object);
        Glide.clear(((View) object).findViewById(R.id.image));
        unbindDrawables((View) object);
        object = null;
    }catch (Exception e) {
        Log.w(TAG, "destroyItem: failed to destroy item and clear it's used resources", e);
    }
}

protected void unbindDrawables(View view) {
    if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
    }
    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
        }
        ((ViewGroup) view).removeAllViews();
    }
}

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

这是我在Recyclerview适配器

中的(部分)视图类
<RelativeLayout
    android:id="@+id/rl_pictureLayout"
    android:layout_width="match_parent"
    android:layout_marginLeft="@dimen/dimen_5"
    android:layout_marginRight="@dimen/dimen_5"
    android:layout_marginTop="@dimen/dimen_5"
    android:layout_height="@dimen/sliderLayoutHeight">

    <ViewPager
        android:id="@+id/vp_photos"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

    <me.relex.circleindicator.CircleIndicator
        android:id="@+id/indicator"
        android:layout_width="match_parent"
        android:layout_marginBottom="50dp"
        android:layout_height="40dp"
        android:layout_alignParentBottom="true"
        />

    <ImageButton
        android:id="@+id/ib_bookmark"
        android:background="@color/transparent"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:layout_marginTop="@dimen/dimen_16"
        android:layout_marginRight="@dimen/dimen_16"
        android:layout_marginEnd="@dimen/dimen_16"
        android:src="@drawable/ic_bookmark_border_white_24dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />

    <TextView
        android:id="@+id/tv_picture_counter"
        android:textColor="@color/white"
        android:textAllCaps="true"
        android:fontFamily="sans-serif"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginLeft="@dimen/dimen_16"
        android:layout_marginStart="@dimen/dimen_16"
        android:layout_marginBottom="@dimen/dimen_24"
        />

    <TextView
        android:id="@+id/tv_damagedPhotos"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/white"
        android:fontFamily="sans-serif"
        android:layout_alignParentBottom="true"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_marginEnd="@dimen/dimen_16"
        android:layout_marginRight="@dimen/dimen_16"
        android:layout_marginBottom="@dimen/dimen_24"
        android:background="@color/transparent"
        android:drawableRight="@drawable/ic_photo_camera_red_24dp"
        android:drawableEnd="@drawable/ic_photo_camera_red_24dp"
        android:drawablePadding="@dimen/dimen_5"
        />
</RelativeLayout>

<RelativeLayout
    android:id="@+id/rl_timerLayout"
    android:layout_below="@id/rl_pictureLayout"
    android:layout_marginTop="@dimen/dimen_5"
    android:layout_alignLeft="@id/rl_pictureLayout"
    android:layout_alignStart="@id/rl_pictureLayout"
    android:layout_alignRight="@id/rl_pictureLayout"
    android:layout_alignEnd="@id/rl_pictureLayout"
    android:layout_marginBottom="@dimen/dimen_10"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/tv_timer"
        style="@style/text_item"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_centerVertical="true"
        android:layout_alignParentLeft="true"
        android:textColor="@color/ColorBlack"
        android:textStyle="bold"
        />

    <TextView
        android:id="@+id/tv_highest_bid"
        style="@style/text_item"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textStyle="bold"
        />

    <Button
        android:id="@+id/btn_bid"
        style="@style/button"
        android:layout_width="60dp"
        android:layout_height="30dp"
        android:textAllCaps="false"
        android:background="@drawable/curved_border_bgd_red"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:stateListAnimator="@null"
        android:text="@string/btn_text_bid"
        />
</RelativeLayout>

在onBindViewHolder中,这就是我所拥有的(就viewpager而言):

static class ViewHolder extends RecyclerView.ViewHolder
{
    @BindView(R.id.indicator)
    CircularIndicator mCircleIndicator;
    @BindView(R.id.vp_photos)
    ViewPager mViewPagerPhotoView;

    public ViewHolder(View itemView)
    {
        super(itemView);
        ButterKnife.bind(this, itemView);
    }
}

现在我的reclerview有10个项目,我从服务器加载。事情是,我注意到,当我向下滚动说到列表中的第7项时,一切似乎都很好但是当我向上滚动以查看我的回收列表的第一项时,在第一项的viewpager中找到的图像倾向于消失并被(暂时)替换为占位符图像,即viewpager完全清空,我暂时占有占位符;如果该项目在我的屏幕上仍然可见,则viewpager a的图像将再次从服务器加载。我选择了Glide,因为它的缓存功能众所周知。此外,viewpager最多可以有20个图像 !! ...

有没有人知道如何解决这个问题? 我做错了什么? 任何帮助将不胜感激.....

3 个答案:

答案 0 :(得分:2)

&#34;问题&#34;是Recyclerview的项目将被回收。您永远不会保存加载的图像。如果向下和向上滚动,将调用onBindViewHolder并创建一个新的适配器实例,这样每次调用onBindViewHolder时都会反复加载图像。 解决方案是保存适配器对象(在map - &gt;位置,适配器中)并将其绑定到onBindViewHolder内。

ImageAdapter adapter = adaptermap.get(postion);
int adapterPosition = 0;
if(adapter == null) {
  adapter = new ImageAdapter(mContext, item.getUrlPhotos());
  adaptermap.put(position, adapter);
  adapterPosition = viewPageStates.get(position);
}

holder.mViewPagerPhotoView.setAdapter(adapter);
holder.mViewPagerPhotoView.setCurrentItem(adapterPosition);

您还必须保存viewpager的位置。

将此作为成员:

// you also can use a SparseIntArray for better performance
// if the itemcount is less than a few hundret
public HashMap<Integer, Integer> viewPageStates = new HashMap<>();

还要在您的recyclerview适配器中添加:

@Override
public void onViewRecycled(YourViewHolderClass holder) {
  int position = holder.getAdapterPosition();
  viewPageStates.put(position, holder.mViewPagerPhotoView.getCurrentItem());
  super.onViewRecycled(holder);
}

重要:

我的解决方案只有在您不添加/删除/排序项目时才有效,因为我们保存了适配器位置。如果你还需要它,你必须修改添加/删除/排序等地图

答案 1 :(得分:1)

尝试在ViewHolder中保留ImageAdapter的实例:

static class ViewHolder extends RecyclerView.ViewHolder
{
    @BindView(R.id.indicator)
    CircularIndicator mCircleIndicator;
    @BindView(R.id.vp_photos)
    ViewPager mViewPagerPhotoView;
    ImageAdapter mAdapter;

    public ViewHolder(View itemView)
    {
        super(itemView);
        ButterKnife.bind(this, itemView);

        mAdapter = new ImageAdapter();
        mViewPagerPhotoView.setAdapter(mAdapter);
    }

    //Add bind() method which will be called in onBindViewHolder()
    public void bind(ArrayList<String> imagePaths) {
        //... maybe some other bindings

        mAdapter.init(imagePaths);
        mViewPagerPhotoView.setOffscreenPageLimit(mAdapter.getCount() - 1);
        mCircleIndicator.setViewPager(mViewPagerPhotoView);
    }
}

在适配器中创建init(ArrayList<String> imagePaths)方法:

void init(ArrayList<String> paths) {
      this.imagePaths = paths;
      notifyDataSetChanged();
}

应该有所帮助。如果没有帮助,那么你使用Glide会出现问题。

答案 2 :(得分:1)

问题在于图像。你说“一个viewpager最多可以有20个图像”,我不知道你的意思是什么,最多20个.ViewPager设计为无限项,除非你把所有项目加载到一个没有意义的项目。

正如你所描述的那样,我想问题是你的图像正在回收,为什么当你向上滚动时你不会再看到你的图像,直到Glide加载图像并再次渲染它。

所以解决方案并不是很简单,但首先让我解释一下image和viewpager。假设您在recyclerview适配器中有10个项目,并且recyclerview根据需要仅加载了5个项目。所以现在需要在内存中的最小总图像是5 x 2(每个viewpager需要至少2个图像)总共等于10个图像。这是巨大的,它取决于你的形象,如果你的图像非常大,你将耗尽内存,当Glide将破坏一些图像,为新的来临腾出空间。希望你能看到正在加载图像的影响。

现在我的建议是:

  1. 确保您的Glide具有diskcache配置,这样只要磁盘缓存仍有可用空间,您的映像就不会从加载的服务器加载。
  2. 缩小图片以适合您的视野。原始图像的示例为1440x320,您的视图仅为1080x240,因此最好将1080x240加载到内存中。或者你甚至可以使它比1080x240小一点。
  3. 让Glide使用builder.setMemoryCache(new LruResourceCache(yourSizeInBytes))控制缓存
  4. 但是,您可以使用RecyclerView.ViewCacheExtension来管理您自己的视图回收器,但我认为它不会有所帮助。此外,您还可以为Pager适配器实现缓存视图,ViewPager deos不回收任何视图,因此当ViewPager需要View时,它只需调用instantiateItem并创建View。