2

私はMaterial Design websiteからこのデザインに非常に類似したものを設計しようとしています。RecyclerView内のCardView内のViewPager

ViewPager inside cardview

だから、基本的に、私はそれらのそれぞれは、ユーザがそれらの間でスワイプすることができ、複数のビューを持っていることをCardViewsのリストを持っています。

私はCardViewを含む単純なリサイクルビューを実装することから始めました。そして、各カードビューに対して、ユーザーがスワイプして動作させる複数のフラグメントを含むFragmentStatePagerAdapterを実装しました。 私が直面している問題は、この問題と非常によく似ています。フラグメントがロードされないか、上下にスクロールすると消えます。私は人々が示唆していたすべての可能な修正を試みましたが、私はまだこれを動作させることができません。

もっと良い方法があるのだろうかと思っていました。

ここに私のコード(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 
} 
} 

デモです:

Demo

注:私はその可能確信している、両方のiOS用Google Analytics app以来アンドロイドは私が探しているのとまったく同じデザインを持っています。 Google analytics app for Android

私は、私はあなたが私のものにまねた問題を持って理解して何から感謝

答えて

3

を任意の助けをいただければ幸いです。私はPagerAdapterに切り替えることで問題を解決することができました.PagerAdapterは、作成されて表示されたページをより詳細に制御できるようにするためです。

これは私が思いついたのソリューションです:

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

と更新されonBindViewHolder()親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); 

    ... 
} 

私は少し気づきましたメモリ使用量は増加しますが、UIは非常に流動的です。どのくらいのページを保持し、どのように破壊して復元するかについて、さらに最適化を行うことができます。

要件は少し異なりますが、その背後にあるロジックは同じである必要があります。これがあなたの問題を解決することを願っています。

関連する問題