7

Tôi cần có một ViewPager (tương tự như một thư viện nằm ngang) bên trong RecyclerView hiển thị danh sách theo chiều dọc. Mỗi hàng của RecyclerView sẽ có một ViewPager mà sẽ cho phép để swipe giữa một số hình ảnh. ViewPager cũng sẽ hỗ trợ các sự kiện nhấp chuột sẽ được truyền đến RecyclerView gốc.ViewPager bên trong RecyclerView dưới dạng mục hàng

Hiện nay, tôi có thực hiện như sau:

Danh sách bộ chuyển đổi: chuyển đổi

@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); 

    ... 
} 

Thư viện ảnh: mảnh

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; 
    } 
} 

Thư viện ảnh:

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); 
     } 
    } 
} 

Vấn đề tôi gặp phải là các phần của ViewPager không được tạo và hiển thị chính xác. Đôi khi chúng xuất hiện sau khi cuộn theo cách thủ công (nhưng không phải lúc nào), trong hầu hết các trường hợp chúng không xuất hiện chút nào.

Có ai có ý tưởng về những gì tôi đã thực hiện sai không?

Cảm ơn bạn.

+0

Tôi đang gặp phải vấn đề tương tự: http://stackoverflow.com/questions/37801078/viewpager-inside-cardview-inside-recyclerview-android –

+0

Tôi đã cố gắng giải quyết vấn đề này bằng cách sử dụng 'PagerAdapter' trực tiếp. Tôi sẽ đăng giải pháp của tôi sau vài phút. –

Trả lời

11

Tôi đã cố gắng giải quyết vấn đề này bằng cách sử dụng trực tiếp PagerAdapter.

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

Và onBindViewHolder cập nhật() của RecyclerView mẹ:

@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); 

    ... 
} 

tôi nhận thấy một sự gia tăng nhỏ trong việc sử dụng bộ nhớ, nhưng giao diện người dùng rất chất lỏng. Tôi đoán một số tối ưu hóa hơn nữa có thể được thực hiện trên bao nhiêu trang được lưu giữ và cách chúng bị phá hủy và phục hồi.

Tôi hy vọng điều này sẽ giúp những người khác trong một tình huống tương tự.

+0

@lonut Negru, nếu tôi muốn sử dụng phân đoạn thay vì 'Xem' thì sao? Có cách nào không? –

+0

Nếu bạn đang đề cập đến 'FragmentPagerAdapter', hơn là có thể thực hiện được, vì bạn sẽ kiểm soát các máy nhắn tin phân đoạn theo chiều ngang và trong mỗi đoạn bạn sẽ có một danh sách. Nếu bạn muốn chèn các mảnh vào bộ chuyển đổi của chế độ xem tái chế thì tôi không biết, nhưng tôi nghĩ nó sẽ có tác động lớn đến hiệu năng khi các mảnh vỡ có vòng đời của riêng chúng. Trân trọng. –

Các vấn đề liên quan