RecyclerView内的ViewPager作为行项目 [英] ViewPager inside RecyclerView as row item

本文介绍了RecyclerView内的ViewPager作为行项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要在RecyclerView内有一个ViewPager(类似于水平画廊),以垂直显示列表. RecyclerView的每一行都有一个ViewPager,该ViewPager允许在某些图像之间滑动. ViewPager也将支持单击事件,这些事件将传播到父级RecyclerView.

I need to have an ViewPager (similar to an horizontal gallery) inside an RecyclerView which display an list vertically. Each row of the RecyclerView will have an ViewPager which will allow to swipe between some images. The ViewPager will also support click events which will be propagated to the parent RecyclerView.

目前,我具有以下实现方式:

Currently, I have the following implementation:

列表适配器:

@Override
public void onBindViewHolder(MyHolder holder, int position) {
    super.onBindViewHolder(holder, position);

    Item listItem = get(position);

    ...

    GalleryAdapter adapter =
                    new GalleryAdapter(getActivity().getSupportFragmentManager(),
                                                         item.mediaGallery);
    holder.imageGallery.setAdapter(adapter);

    ...
}

图库适配器:

public class GalleryAdapter extends FragmentStatePagerAdapter {

    private final List<Item.Gallery> mItems;
    @Bind(R.id.gallery_item)
    ImageView galleryView;

    public SearchResultsGalleryPagerAdapter(FragmentManager fm, @NonNull ArrayList<Item.Gallery> mediaGallery) {
        super(fm);

        mItems = mediaGallery;
    }

    @Override
    public Fragment getItem(int position) {
        GalleryFragment fragment = GalleryFragment.newInstance(mItems.get(position));
        ...
        return fragment;
    }

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

    @Override
    public int getItemPosition(Object object) {
        //return super.getItemPosition(object);
        return PagerAdapter.POSITION_NONE;
    }
}

图库片段:

public class GalleryFragment extends Fragment {

    private static final String GALLERY_ITEM_BUNDLE_KEY = "gallery_item_bundle_key";

    @Bind(R.id.gallery_item)
    ImageView mGalleryView;

    private Item.Gallery mGalleryItem;

    // Empty constructor, required as per Fragment docs
    public GalleryFragment() {}

    public static SearchResultsGalleryFragment newInstance(Item.Gallery galleryItem) {
        GalleryFragment fragment = new GalleryFragment();

        // Add the item in the bundle which will be set to the fragment
        Bundle bundle = new Bundle();
        bundle.putSerializable(GALLERY_ITEM_BUNDLE_KEY, galleryItem);
        fragment.setArguments(bundle);

        return fragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mGalleryItem = (Item.Gallery) getArguments().getSerializable(GALLERY_ITEM_BUNDLE_KEY);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_gallery_item, container, false);
        ButterKnife.bind(this, view);

        displayGalleryItem();

        return view;
    }

    private void displayGalleryItem() {
        if (null != mGalleryItem) {
            Glide.with(getContext()) // Bind it with the context of the actual view used
                 .load(mGalleryItem.getImageUrl()) // Load the image
                 .centerCrop() // scale type
                 .placeholder(R.drawable.default_product_400_land) // temporary holder displayed while the image loads
                 .crossFade()
                 .into(mGalleryView);
        }
    }
}

我遇到的问题是未创建并正确显示ViewPager的片段.有时它们会在手动滚动之后出现(但并非总是如此),在大多数情况下它们根本不会出现.

The issue I'm having is that the fragments of the ViewPager aren't created and displayed correctly. Sometimes they appear after an manual scroll (but not always), in most of the cases they don't appear at all.

有人对我实施的错误有任何想法吗?

Does anyone have an idea on what I've implemented wrong?

推荐答案

我设法通过直接使用PagerAdapter解决了这个问题.

I have managed to get around this issue by using PagerAdapter directly.

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.DecodeFormat;
import com.peoplepost.android.R;
import com.peoplepost.android.common.listener.ItemClickSupport;
import com.peoplepost.android.network.merv.model.Product;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * Custom pager adapter which will manually create the pages needed for showing an slide pages gallery.
 * </p>
 * Created by Ionut Negru on 13/06/16.
 */
public class GalleryAdapter extends PagerAdapter {

    private static final String TAG = "GalleryAdapter";

    private final List<Item> mItems;
    private final LayoutInflater mLayoutInflater;
    /**
     * The click event listener which will propagate click events to the parent or any other listener set
     */
    private ItemClickSupport.SimpleOnItemClickListener mOnItemClickListener;

    /**
     * Constructor for gallery adapter which will create and screen slide of images.
     *
     * @param context
     *         The context which will be used to inflate the layout for each page.
     * @param mediaGallery
     *         The list of items which need to be displayed as screen slide.
     */
    public GalleryAdapter(@NonNull Context context,
                                            @NonNull ArrayList<Item> mediaGallery) {
        super();

        // Inflater which will be used for creating all the necessary pages
        mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        // The items which will be displayed.
        mItems = mediaGallery;
    }

    @Override
    public int getCount() {
        // Just to be safe, check also if we have an valid list of items - never return invalid size.
        return null == mItems ? 0 : mItems.size();
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        // The object returned by instantiateItem() is a key/identifier. This method checks whether
        // the View passed to it (representing the page) is associated with that key or not.
        // It is required by a PagerAdapter to function properly.
        return view == object;
    }

    @Override
    public Object instantiateItem(ViewGroup container, final int position) {
        // This method should create the page for the given position passed to it as an argument.
        // In our case, we inflate() our layout resource to create the hierarchy of view objects and then
        // set resource for the ImageView in it.
        // Finally, the inflated view is added to the container (which should be the ViewPager) and return it as well.

        // inflate our layout resource
        View itemView = mLayoutInflater.inflate(R.layout.fragment_gallery_item, container, false);

        // Display the resource on the view
        displayGalleryItem((ImageView) itemView.findViewById(R.id.gallery_item), mItems.get(position));

        // Add our inflated view to the container
        container.addView(itemView);

        // Detect the click events and pass them to any listeners
        itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (null != mOnItemClickListener) {
                    mOnItemClickListener.onItemClicked(position);
                }
            }
        });

        // Return our view
        return itemView;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        // Removes the page from the container for the given position. We simply removed object using removeView()
        // but could’ve also used removeViewAt() by passing it the position.
        try {
            // Remove the view from the container
            container.removeView((View) object);

            // Try to clear resources used for displaying this view
            Glide.clear(((View) object).findViewById(R.id.gallery_item));
            // Remove any resources used by this view
            unbindDrawables((View) object);
            // Invalidate the object
            object = null;
        } catch (Exception e) {
            Log.w(TAG, "destroyItem: failed to destroy item and clear it's used resources", e);
        }
    }

    /**
     * Recursively unbind any resources from the provided view. This method will clear the resources of all the
     * children of the view before invalidating the provided view itself.
     *
     * @param view
     *         The view for which to unbind resource.
     */
    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();
        }
    }

    /**
     * Set an listener which will notify of any click events that are detected on the pages of the view pager.
     *
     * @param onItemClickListener
     *         The listener. If {@code null} it will disable any events from being sent.
     */
    public void setOnItemClickListener(ItemClickSupport.SimpleOnItemClickListener onItemClickListener) {
        mOnItemClickListener = onItemClickListener;
    }

    /**
     * Display the gallery image into the image view provided.
     *
     * @param galleryView
     *         The view which will display the image.
     * @param galleryItem
     *         The item from which to get the image.
     */
    private void displayGalleryItem(ImageView galleryView, Item galleryItem) {
        if (null != galleryItem) {
            Glide.with(galleryView.getContext()) // Bind it with the context of the actual view used
                 .load(galleryItem.getImageUrl()) // Load the image
                 .asBitmap() // All our images are static, we want to display them as bitmaps
                 .format(DecodeFormat.PREFER_RGB_565) // the decode format - this will not use alpha at all
                 .centerCrop() // scale type
                 .placeholder(R.drawable.default_product_400_land) // temporary holder displayed while the image loads
                 .animate(R.anim.fade_in) // need to manually set the animation as bitmap cannot use cross fade
                 .thumbnail(0.2f) // make use of the thumbnail which can display a down-sized version of the image
                 .into(galleryView); // Voilla - the target view
        }
    }
}

以及父级RecyclerView的更新后的onBindViewHolder():

And the updated onBindViewHolder() of the parent RecyclerView:

@Override
public void onBindViewHolder(MyHolder holder, int position) {
    super.onBindViewHolder(holder, position);

    Item listItem = get(position);

    ...

    GalleryAdapter adapter =
                    new GalleryAdapter(getActivity(), product.mediaGallery);
    // Set the custom click listener on the adapter directly
    adapter.setOnItemClickListener(new ItemClickSupport.SimpleOnItemClickListener() {
        @Override
        public void onItemClicked(int position) {
            // inner view pager page was clicked
        }
    });
    // Set the adapter on the view pager
    holder.imageGallery.setAdapter(adapter);

    ...
}

我注意到内存使用量有所增加,但是用户界面非常流畅.我猜想可以对保留的页面数以及如何销毁和还原进行一些进一步的优化.

I noticed a little increase in memory usage, but the UI is very fluid. I guess some further optimizations can be made on how many pages are kept and how they are destroyed and restored.

我希望这可以帮助处于类似情况的其他人.

I hope this helps others in a similar situation.

这篇关于RecyclerView内的ViewPager作为行项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆