2

Ich versuche, etwas sehr ähnliches zu diesem Design aus der Material Design website zu entwerfen.ViewPager innerhalb CardView innerhalb RecyclerView Android

ViewPager inside cardview

Also im Grunde, ich habe eine Liste von CardViews dass jeder von ihnen mehrere Ansichten hat, dass der Benutzer zwischen ihnen klauen kann. (Erste Karte auf dem Bild)

Ich begann mit der Implementierung einer einfachen Recyclerview, die CardView enthält und für jede Kartenansicht habe ich einen FragmentStatePagerAdapter implementiert, der mehrere Fragmente enthält, die der Benutzer durchwischen kann und es funktioniert (irgendwie). Das Problem, mit dem ich konfrontiert bin, ist diesem Problem sehr ähnlich Fragment in ViewPager using FragmentPagerAdapter is blank the second time it is viewed wo Fragmente entweder nicht geladen werden, oder sie verschwinden, wenn Sie nach oben und unten scrollen. Ich habe alles versucht, was die Leute vorgeschlagen haben, und ich kann es immer noch nicht zur Arbeit bringen.

Ich fragte mich, ob es einen besseren Weg gibt, es zu tun.

Hier ist mein 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 
} 
} 

Hier ist die Demo ist:

Demo

Hinweis: Ich bin sicher, dass seine möglich, da die Google Analytics app sowohl für iOS und Android hat genau das gleiche Design, nach dem ich suche. Google analytics app for Android

ich Hilfe schätzen würde, Dank

Antwort

3

Von dem, was ich verstehe Sie hat ein simular Problem zu mir. Ich habe es geschafft, das Problem zu lösen, indem ich zu PagerAdapter wechselte, da es Ihnen mehr Kontrolle über die erstellten und angezeigten Seiten gibt.

Dies ist die Lösung, die ich kam zu:

Die PagerAdapter Klasse:

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

und der aktualisierte onBindViewHolder() der Mutter 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); 

    ... 
} 

bemerkte ich ein wenig Erhöhung der Speichernutzung, aber die Benutzeroberfläche ist sehr flüssig. Ich denke, es gibt noch weitere Optimierungen, wie viele Seiten behalten und wie sie zerstört und wiederhergestellt werden.

Ihre Anforderungen sind ein wenig anders, aber die Logik dahinter sollte die gleiche sein. Ich hoffe, das löst dein Problem.

Verwandte Themen