Android RecyclerView内CardView内的ViewPager [英] ViewPager inside CardView inside RecyclerView Android

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

问题描述

我正在尝试从材料设计网站.

因此,基本上,我有一个CardView列表,每个CardView都有多个视图,用户可以在它们之间滑动. (图片上的第一张卡片)

So basically, I have a list of CardViews that each of them has multiple views that user can swipe between them. (First card on the image)

我首先实现了一个简单的包含CardView的recyclerview,对于每个Cardview我实现了一个FragmentStatePagerAdapter,其中包含多个片段,用户可以通过它们滑动并且可以使用(某种). 我面临的问题与该问题非常相似片段再次查看片段时,使用FragmentPagerAdapter的ViewPager中的图片为空白,这些片段要么无法加载,要么在您上下滚动时消失.我已经尝试了人们提出的所有可能的解决方案,但仍然无法正常工作.

I started by implementing a simple recyclerview that contains CardView and for each cardview I implemented a FragmentStatePagerAdapter that contains multiple fragments that user can swipe through and it works (kind of). The issue that I'm facing is very similar to this issue Fragment in ViewPager using FragmentPagerAdapter is blank the second time it is viewed where fragments either won't load, or they disappear when you scroll up and down. I've tried every possible fix that people were suggesting and I still can't get this to work.

我想知道是否有更好的方法.

I was wondering if there is a better way of doing it.

这是我的代码(C#-Xamarin)

Here is my code (C# - Xamarin)

using System;
using Android.Support.V7.Widget;
using System.Collections.Generic;
using Android.Views;
using Android.Widget;
using System.Security.Cryptography;
using Android.Support.V4.View;
using Android.Support.V4.App;
using Android.Runtime;
using Android.OS;
using Android.Content;
using Android.App;
using Android.Support.V7.App;
using JavaString = Java.Lang.String;
using Android.Util;
using Android.Animation;
using System.ComponentModel.Design.Serialization;

namespace Answers.PortalAppXamarin.Droid
{
public enum MyTestAdapterItemType {
    Type1,
    Type2,
    Type3
};

public class MyTestDataObj
{
    public MyTestAdapterItemType type { get; set; }
    public string data { get; set; }
}





public class PagerFragmentAdapter : Android.Support.V4.App.FragmentStatePagerAdapter {

    public List<MyTestPageFragmentContainer> Tabs { get; set; }

    public PagerFragmentAdapter(Android.Support.V4.App.FragmentManager fm) : base(fm) {
    }


    public override Android.Support.V4.App.Fragment GetItem(int position) {
        return Tabs [position].Fragment;
    }

    public override Java.Lang.ICharSequence GetPageTitleFormatted(int position)
    {
        return new JavaString(Tabs [position].Title);
    }

    public override int GetItemPosition (Java.Lang.Object objectValue) {

        for (int i = 0; i < Tabs.Count; i++) {
            if (Tabs[i].Fragment.Equals (objectValue) ) {
                return i;
            }
        }

        return PositionNone;
    }

    public override int Count {
        get {
            return Tabs.Count;
        }
    }
}



public class MyTestPageFragmentContainer {

    public string Title { get; set; }
    public Android.Support.V4.App.Fragment Fragment { get; set; }

    public MyTestPageFragmentContainer(string title, Android.Support.V4.App.Fragment fragment)
    {
        this.Title = title;
        this.Fragment = fragment;
    }
}


public class BaseFragment : Android.Support.V4.App.Fragment
{
    public static Android.Support.V4.App.Fragment newInstance(int position) { 

            BaseFragment f = new BaseFragment ();
            // Supply num input as an argument.
            Bundle args = new Bundle ();
            args.PutInt ("num", position);
            f.Arguments = (args);

            return f;
    }
}

public class PageFragment1 : BaseFragment {

    View RootView;

    public override void OnCreate (Bundle savedInstanceState)
    {
        base.OnCreate (savedInstanceState);
    }

    public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        RootView = inflater.Inflate (Resource.Layout.LoadingIndicatorOverlay, container, false);
        return RootView;
    }

    public override void OnResume ()
    {
        base.OnResume ();

        ValueAnimator _animator = ValueAnimator.OfFloat(0, 1);
        _animator.SetDuration(2000);
        _animator.Update += (object sender, ValueAnimator.AnimatorUpdateEventArgs e) => {
            if (RootView != null) {
                RootView.Alpha = (float)e.Animation.AnimatedValue;
            }
        };
    }
}

public class PageFragment2 : BaseFragment {

    View RootView;

    public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        RootView = inflater.Inflate(Resource.Layout.MeasureListItem, container, false);
        return RootView;
    }

    public override void OnResume ()
    {
        base.OnResume ();

        ValueAnimator _animator = ValueAnimator.OfFloat(0, 1);
        _animator.SetDuration(2000);
        _animator.Update += (object sender, ValueAnimator.AnimatorUpdateEventArgs e) => {
            if (RootView != null) {
                RootView.Alpha = (float)e.Animation.AnimatedValue;
            }
        };
    }

}


public abstract class BaseCardViewHolder : RecyclerView.ViewHolder
{
    public ViewGroup Header;
    public ViewGroup Footer;

    public TextView TitleTextView;
    public Button MoreInfoBtn;

    public ViewPager ContentViewPager;
    public PagerFragmentAdapter Adapter;

    public List<MyTestPageFragmentContainer> Tabs;

    public Android.Support.V4.App.FragmentManager fm;

    public BaseCardViewHolder(View rootView, Android.Support.V4.App.FragmentManager fm) : base(rootView)
    {
        Header = rootView.FindViewById<ViewGroup> (Resource.Id.card_view_header);
        Footer = rootView.FindViewById<ViewGroup> (Resource.Id.card_view_footer);
        ContentViewPager = rootView.FindViewById<ViewPager> (Resource.Id.card_view_content_viewPager);
        TitleTextView = Header.FindViewById<TextView> (Resource.Id.card_view_title);
        MoreInfoBtn = Footer.FindViewById<Button> (Resource.Id.card_view_moreInfoBtn);

        this.fm = fm;

        Tabs = setupTabs ();

        if (Tabs != null && fm != null) {
            Adapter = new PagerFragmentAdapter (fm);
            Adapter.Tabs = Tabs;
            ContentViewPager.Adapter = Adapter;
        }

    }

    public void RefreshViewPager()
    {
        ContentViewPager.Adapter.NotifyDataSetChanged ();
    }

    public abstract List<MyTestPageFragmentContainer> setupTabs ();
}



public class Type1ViewHolder : BaseCardViewHolder
{
    public Type1ViewHolder(View v, Android.Support.V4.App.FragmentManager fm) : base(v, fm)
    {
    }

    public override List<MyTestPageFragmentContainer> setupTabs()
    {
        return new List<MyTestPageFragmentContainer> {
            new MyTestPageFragmentContainer ("Tab1", new PageFragment1 ()),
            new MyTestPageFragmentContainer ("Tab2", new PageFragment1 ()) 
        };
    }
}



public class Type2ViewHolder : BaseCardViewHolder
{
    public Type2ViewHolder(View v, Android.Support.V4.App.FragmentManager fm) : base(v, fm)
    {
    }

    public override List<MyTestPageFragmentContainer> setupTabs()
    {
        return new List<MyTestPageFragmentContainer> {
            new MyTestPageFragmentContainer ("Tab1", new PageFragment2 ()),
            new MyTestPageFragmentContainer ("Tab2", new PageFragment2 ()),
            new MyTestPageFragmentContainer ("Tab1", new PageFragment1 ()),
            new MyTestPageFragmentContainer ("Tab2", new PageFragment1 ()),
        };
    }
}


public class Type3ViewHolder : BaseCardViewHolder
{
    public Type3ViewHolder(View v, Android.Support.V4.App.FragmentManager fm) : base(v, fm)
    {
    }

    public override List<MyTestPageFragmentContainer> setupTabs()
    {
        return new List<MyTestPageFragmentContainer> {
            new MyTestPageFragmentContainer ("Tab1", new PageFragment2 ()),
            new MyTestPageFragmentContainer ("Tab2", new PageFragment1 ()),
            new MyTestPageFragmentContainer ("Tab3", new PageFragment2 ()),
            new MyTestPageFragmentContainer ("Tab4", new PageFragment1 ())
        };
    }
}


public class MyTestRecyclerAdapter : RecyclerView.Adapter
{

    public Android.Support.V4.App.FragmentManager FM;

    readonly List<MyTestDataObj> myTestDataList = new List<MyTestDataObj>{
        new MyTestDataObj {type = MyTestAdapterItemType.Type1, data = "Item 1"}, 
        new MyTestDataObj {type = MyTestAdapterItemType.Type1, data = "Item 2"}, 
        new MyTestDataObj {type = MyTestAdapterItemType.Type2, data = "Item 3"},
        new MyTestDataObj {type = MyTestAdapterItemType.Type3, data = "Item 4"}, 
        new MyTestDataObj {type = MyTestAdapterItemType.Type1, data = "Item 5"}, 
        new MyTestDataObj {type = MyTestAdapterItemType.Type2, data = "Item 6"}, 
        new MyTestDataObj {type = MyTestAdapterItemType.Type1, data = "Item 7"}, 
        new MyTestDataObj {type = MyTestAdapterItemType.Type1, data = "Item 8"}, 
        new MyTestDataObj {type = MyTestAdapterItemType.Type2, data = "Item 9"},
        new MyTestDataObj {type = MyTestAdapterItemType.Type3, data = "Item 10"}, 
        new MyTestDataObj {type = MyTestAdapterItemType.Type1, data = "Item 11"}, 
        new MyTestDataObj {type = MyTestAdapterItemType.Type2, data = "Item 12"}, 
        new MyTestDataObj {type = MyTestAdapterItemType.Type1, data = "Item 13"}, 
        new MyTestDataObj {type = MyTestAdapterItemType.Type1, data = "Item 14"}, 
        new MyTestDataObj {type = MyTestAdapterItemType.Type2, data = "Item 15"},
        new MyTestDataObj {type = MyTestAdapterItemType.Type3, data = "Item 16"}, 
        new MyTestDataObj {type = MyTestAdapterItemType.Type1, data = "Item 17"}, 
        new MyTestDataObj {type = MyTestAdapterItemType.Type2, data = "Item 18"}, 
        new MyTestDataObj {type = MyTestAdapterItemType.Type1, data = "Item 19"}, 
        new MyTestDataObj {type = MyTestAdapterItemType.Type1, data = "Item 20"}, 
        new MyTestDataObj {type = MyTestAdapterItemType.Type2, data = "Item 21"},
        new MyTestDataObj {type = MyTestAdapterItemType.Type3, data = "Item 22"}, 
        new MyTestDataObj {type = MyTestAdapterItemType.Type1, data = "Item 23"}, 
        new MyTestDataObj {type = MyTestAdapterItemType.Type2, data = "Item 24"}, 
    };

    #region implemented abstract members of Adapter

    public override void OnBindViewHolder (RecyclerView.ViewHolder holder, int position)
    {
        MyTestDataObj item = myTestDataList [position];

        switch (GetItemViewType (position)) {
            case (int) MyTestAdapterItemType.Type1:
            {
                ((Type1ViewHolder)holder).TitleTextView.Text = item.data;
                ((Type1ViewHolder)holder).RefreshViewPager ();
                break;
            }
            case (int) MyTestAdapterItemType.Type2:
            {
                ((Type2ViewHolder)holder).TitleTextView.Text = item.data;
                ((Type2ViewHolder)holder).RefreshViewPager ();
                break;
            }
            case (int) MyTestAdapterItemType.Type3:
            {
                ((Type3ViewHolder)holder).TitleTextView.Text = item.data;
                ((Type3ViewHolder)holder).RefreshViewPager ();
                break;
            }
        }
    }

    public override RecyclerView.ViewHolder OnCreateViewHolder (Android.Views.ViewGroup parent, int viewType)
    {
        LayoutInflater layoutInflater = LayoutInflater.From (parent.Context);
        switch (viewType) {
            case (int) MyTestAdapterItemType.Type1:
            {
                return new Type1ViewHolder (layoutInflater.Inflate (Resource.Layout.MyTestCardViewLayout, parent, false), FM);
            }
            case (int) MyTestAdapterItemType.Type2:
            {
                return new Type2ViewHolder (layoutInflater.Inflate (Resource.Layout.MyTestCardViewLayout, parent, false), FM);
            }
            case (int) MyTestAdapterItemType.Type3:
            {
                return new Type3ViewHolder (layoutInflater.Inflate (Resource.Layout.MyTestCardViewLayout, parent, false), FM);
            } 
        }

        return null;
    }

    public override int ItemCount {
        get {
            return myTestDataList.Count;
        }
    }

    public override int GetItemViewType (int position)
    {
        return (int)myTestDataList [position].type;
    }

    #endregion
}
}

这是演示:

注意:由于

我将不胜感激,谢谢

推荐答案

据我了解,您有一个类似的问题要挖掘.我已设法切换到PagerAdapter来解决此问题,因为它使您可以更好地控制创建和显示的页面.

From what I understand you have an simular issue to mine. I have managed to resolve the issue by switching to PagerAdapter as it gives you more control over the pages created and displayed.

这是我想出的解决方案:

This is the solution I came up to:

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

以及父级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.

您的要求有所不同,但是其背后的逻辑应该相同.我希望这能解决您的问题.

Your requirements are a little different, but the logic behind it should be the same. I hope this solves your issue.

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

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