wbk727
wbk727

Reputation: 8408

How to show CardViews (with different content types) in RecyclerView

I'm trying to show an additional CardView that contains a different layout but have got lost with my code. I'm also unsure of how to to show the array of strings within a GridView for the CardView itself. Does anyone know where I may have gone wrong & what can be done so that the following can be achieved?:

  1. Place the CardView containing the GridView wherever I want within the RecyclerView.
  2. Show the array of strings in a GridView within a CardView

What I want to add to the RecyclerView (above the Item A CardView)

enter image description here

RecyclerView current contents

enter image description here

Fragment class

public class MyFragment extends android.support.v4.app.Fragment {

    private MonRecyclerAdapterWithGrid adapterG;

    static final String[] frenchVowels = new String[]{
            "a", "e", "i", "o", "u", "y"
    };


    public MyFragment() {}

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        return inflater.inflate(R.layout.fragment_rv, container, false);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        View v = getView();
        assert v != null;

        recyclerView = v.findViewById(R.id.my_recyclerview);
        linearLayoutManager = new LinearLayoutManager(getActivity());

        MyRecyclerAdapter adapter = new MyRecyclerAdapter(getContext(), getHeader(), getListItemsG(), getListItemsT());
        recyclerView.setLayoutManager(linearLayoutManager);
        recyclerView.setAdapter(adapter);

        super.onActivityCreated(savedInstanceState);
    }

    RecyclerView recyclerView;
    LinearLayoutManager linearLayoutManager;

    public RecyclerViewHeader getHeader()
    {
        return new RecyclerViewHeader();
    }

    public List<RecyclerViewItemGV> getListItemsG() {
        List<RecyclerViewItemGV> rvItemsG = new ArrayList<>();

        RecyclerViewItemGV itemG = new RecyclerViewItemGV();
        itemG.setTitleGV("Item A");
        itemG.setVowelsGV(adapterG);
        for (String fVowels : frenchVowels) {
            // ?????? Still not working :-(
            adapterG.addAdapterItem(new MyFragment.AdapterItem(frenchVowels));
        }
        rvItemsG.add(itemG);

        return rvItemsG;
    }

        public List<RecyclerViewItemTV> getListItemsT()
    {
        List<RecyclerViewItemTV> rvItemsT = new ArrayList<>();

        RecyclerViewItemTV itemA = new RecyclerViewItemTV();
        itemA.setTitleTV("Item A");
        itemA.setDescriptionTV("Feature A1");
        rvItemsT.add(itemA);

        RecyclerViewItemTV itemB = new RecyclerViewItemTV();
        itemB.setTitleTV("Item B");
        itemB.setDescriptionTV("Feature B1\nFeature B2");
        rvItemsT.add(itemB);

        RecyclerViewItemTV itemC = new RecyclerViewItemTV();
        itemC.setTitleTV("Item C");
        itemC.setDescriptionTV("Feature C1\nFeature C2\nFeature C3");
        rvItemsT.add(itemC);

        return rvItemsT;
    }
}

RecyclerView adapter class

public class MyRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private static final int TYPE_HEADER = 0;
    private static final int TYPE_ITEMG = 1;
    private static final int TYPE_ITEMT = 2;

    private Context mContext;

    RecyclerViewHeader header;
    List<RecyclerViewItemGV> listItemsG;
    List<RecyclerViewItemTV> listItemsT;
    ValueAnimator mAnimator;



    public MyRecyclerAdapter(Context context, RecyclerViewHeader header, List<RecyclerViewItemGV> listItemsG, List<RecyclerViewItemTV> listItemsT)
    {
        this.mContext = context;
        this.header = header;
        this.listItemsG = listItemsG;
        this.listItemsT = listItemsT;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if(viewType == TYPE_HEADER)
        {
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_header_expandcollapsebuttons, parent, false);
            return new MyRecyclerAdapter.VHHeader(v);
        }
        else if(viewType == TYPE_ITEMG)
        {
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item_gv, parent, false);
            return new MyRecyclerAdapter.VHItemG(v);
        }
        else if(viewType == TYPE_ITEMT)
        {
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item_tv, parent, false);
            return new MyRecyclerAdapter.VHItemT(v);
        }
        throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly");
    }

    private RecyclerViewItemGV getItemG(int position)
    {
        return listItemsG.get(position);
    }

    private RecyclerViewItemTV getItemT(int position)
    {
        return listItemsT.get(position);
    }


    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        final Typeface iconFont = FontManager.getTypeface(mContext, FontManager.FONTAWESOME);

        if (holder instanceof MyRecyclerAdapter.VHHeader)
        {
            final MyRecyclerAdapter.VHHeader vhHeader = (MyRecyclerAdapter.VHHeader)holder;
        }
        else if (holder instanceof MyRecyclerAdapter.VHItemG){
            RecyclerViewItemGV currentItemG = getItemG(position-1);
            final MonRecyclerAdapterWithGrid.VHItemG vhItemG = (MyRecyclerAdapter.VHItemG)holder;

            vhItemG.txtAG.setText(currentItemG.getTitleGV());

            vhItemG.mGridViewG.setVisibility(View.GONE);


            vhItemG.txtExpandCollapseG.setText(R.string.fa_icon_chevron_down);
            vhItemG.txtExpandCollapseG.setTypeface(iconFont);

            //Add onPreDrawListener
            vhItemG.mGridViewG.getViewTreeObserver().addOnPreDrawListener(
                    new ViewTreeObserver.OnPreDrawListener() {

                        @Override
                        public boolean onPreDraw() {
                            vhItemG.mGridViewG.getViewTreeObserver().removeOnPreDrawListener(this);
                            vhItemG.mGridViewG.setVisibility(View.GONE);

                            final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
                            final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
                            vhItemG.mGridViewG.measure(widthSpec, heightSpec);

                            vhItemG.mGridViewHeight = vhItemG.mGridViewG.getMeasuredHeight();

                            return true;
                        }
                    });

            vhItemG.mCardViewG.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(vhItemG.mGridViewG.getVisibility() == View.GONE){
                        vhItemG.expandG();
                    } else {
                        vhItemG.collapseG();
                    }
                }
            });
        }
        else if (holder instanceof MyRecyclerAdapter.VHItemT)
        {
            RecyclerViewItemTV currentItem = getItemT(position-2);
            final MyRecyclerAdapter.VHItemT vhItemT = (MyRecyclerAdapter.VHItemT)holder;

            vhItemT.txtA.setText(currentItem.getTitleTV());
            vhItemT.txtB.setText(currentItem.getDescriptionTV());

            vhItemT.txtB.setVisibility(View.GONE);

            vhItemT.txtExpandCollapse.setText(R.string.fa_icon_chevron_down);
            vhItemT.txtExpandCollapse.setTypeface(iconFont);

            //Add onPreDrawListener
            vhItemT.txtB.getViewTreeObserver().addOnPreDrawListener(
                    new ViewTreeObserver.OnPreDrawListener() {

                        @Override
                        public boolean onPreDraw() {
                            vhItemT.txtB.getViewTreeObserver().removeOnPreDrawListener(this);
                            vhItemT.txtB.setVisibility(View.GONE);

                            final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
                            final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
                            vhItemT.txtB.measure(widthSpec, heightSpec);

                            vhItemT.textBHeight = vhItemT.txtB.getMeasuredHeight();

                            return true;
                        }
                    });

            vhItemT.cardView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(vhItemT.txtB.getVisibility() == View.GONE){
                        vhItemT.expandT();
                    } else {
                        vhItemT.collapseT();
                    }
                }
            });
        }
    }

    // need to override this method
    @Override
    public int getItemViewType(int position) {
        if(isPositionHeader(position))
        return TYPE_HEADER;
        return TYPE_ITEMG;
        return TYPE_ITEMT;
    }

    private boolean isPositionHeader(int position)
    {
        return position == 0;
    }

    @Override
    public int getItemCount() {
        return listItemsG.size()+1;
        return listItemsT.size()+1;
    }

    class VHHeader extends RecyclerView.ViewHolder{
        Button btnCollapseAll, btnExpandAll;

        public VHHeader(View headerView) {
            super(headerView);

            this.btnCollapseAll = headerView.findViewById(R.id.btn_collapseall);
            this.btnExpandAll = headerView.findViewById(R.id.btn_expandall);
        }
    }

    public class VHItemG extends RecyclerView.ViewHolder{
        CardView mCardViewG;
        LinearLayout mLinearLayoutG;
        RelativeLayout mRelativeLayoutG;
        RecyclerView mRecyclerViewG;
        GridView mGridViewG;
        TextView txtExpandCollapseG, txtAG;
        public int mGridViewHeight;


        public VHItemG(View itemView) {
            super(itemView);

            this.mCardViewG = itemView.findViewById(R.id.cv_gv);
            this.mLinearLayoutG = itemView.findViewById(R.id.linearlayout_gv_titlerow);
            this.mRelativeLayoutG = itemView.findViewById(R.id.relativelayout_gv);
            this.mRecyclerViewG = itemView.findViewById(R.id.my_recyclerview);
            this.txtAG = itemView.findViewById(R.id.tv_gv_A);
            this.txtExpandCollapseG = itemView.findViewById(R.id.tv_gv_expandcollapse);
            this.mGridViewG = itemView.findViewById(R.id.gv_a);
        }

        private void expandG() {
            // change visibility to 'VISIBLE'
            mGridViewG.setVisibility(View.VISIBLE);

            // change direction of chevron to 'up'
            txtExpandCollapseG.setText(R.string.fa_icon_chevron_up);

            // apply animation to the height of 'txtB'
            mAnimator = slideAnimator(0, mGridViewHeight);

            // start the animation
            mAnimator.start();
        }

        private void collapseG() {
            // change direction of chevron to 'down'
            txtExpandCollapseG.setText(R.string.fa_icon_chevron_down);

            int finalHeight = mGridViewG.getHeight();

            ValueAnimator mAnimator = slideAnimator(finalHeight, 0);

            mAnimator.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationEnd(Animator animator) {
                    // Height will be 0, but set visibility to 'GONE'
                    mGridViewG.setVisibility(View.GONE);
                }

                @Override
                public void onAnimationStart(Animator animator) {
                }

                @Override
                public void onAnimationCancel(Animator animator) {
                }

                @Override
                public void onAnimationRepeat(Animator animator) {
                }
            });
            mAnimator.start();
        }


        public ValueAnimator slideAnimator(int start, int end) {

            ValueAnimator animator = ValueAnimator.ofInt(start, end);


            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    // update height
                    int value = (Integer) valueAnimator.getAnimatedValue();

                    ViewGroup.LayoutParams layoutParams = mGridViewG.getLayoutParams();
                    layoutParams.height = value;
                    mGridViewG.setLayoutParams(layoutParams);
                }
            });
            return animator;
        }
    }

    public class VHItemT extends RecyclerView.ViewHolder{
        CardView cardView;
        LinearLayout mLinearLayout;
        RecyclerView mRecyclerView;
        RelativeLayout mRelativeLayout;
        TextView txtExpandCollapse, txtA, txtB;
        public int textBHeight;

        public VHItemT(View itemView) {
            super(itemView);

            this.cardView = itemView.findViewById(R.id.linearlayout_tv_main);
            this.mLinearLayout = itemView.findViewById(R.id.linearlayout_tv_titlerow);
            this.mRelativeLayout = itemView.findViewById(R.id.relativelayout_tv);
            this.mRecyclerView = itemView.findViewById(R.id.my_recyclerview);
            this.txtExpandCollapse = itemView.findViewById(R.id.tv_tv_expandcollapse);
            this.txtA = itemView.findViewById(R.id.tv_tv_A);
            this.txtB = itemView.findViewById(R.id.tv_tv_B);
        }

        private void expandT() {
            // change visibility to 'VISIBLE'
            txtB.setVisibility(View.VISIBLE);

            // change direction of chevron to 'up'
            txtExpandCollapse.setText(R.string.fa_icon_chevron_up);

            // apply animation to the height of 'txtB'
            mAnimator = slideAnimator(0, textBHeight);

            // start the animation
            mAnimator.start();
        }

        private void collapseT() {
            // change direction of chevron to 'down'
            txtExpandCollapse.setText(R.string.fa_icon_chevron_down);

            int finalHeight = txtB.getHeight();

            ValueAnimator mAnimator = slideAnimator(finalHeight, 0);

            mAnimator.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationEnd(Animator animator) {
                    // Height will be 0, but set visibility to 'GONE'
                    txtB.setVisibility(View.GONE);
                }

                @Override
                public void onAnimationStart(Animator animator) {
                }

                @Override
                public void onAnimationCancel(Animator animator) {
                }

                @Override
                public void onAnimationRepeat(Animator animator) {
                }
            });
            mAnimator.start();
        }


        public ValueAnimator slideAnimator(int start, int end) {

            ValueAnimator animator = ValueAnimator.ofInt(start, end);


            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    // update height
                    int value = (Integer) valueAnimator.getAnimatedValue();

                    ViewGroup.LayoutParams layoutParams = txtB.getLayoutParams();
                    layoutParams.height = value;
                    txtB.setLayoutParams(layoutParams);
                }
            });
            return animator;
        }
    }
}

GridView adapter (currently excluded from project)

   private class MyGVAdapter extends ArrayAdapter<AdapterItem> {
        private List<AdapterItem> items = new ArrayList<>();

        MyGVAdapter(Context context, int textviewid) {
            super(context, textviewid);
        }

        void addAdapterItem(MyGVFragment.AdapterItem item) {
            items.add(item);
        }

        @Override
        public int getCount() {
            return items.size();
        }

        @Override
        public MyGVFragment.AdapterItem getItem(int position) {
            return ((null != items) ? items.get(position) : null);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @NonNull
        @Override
        public View getView(final int position, View convertView, @NonNull final ViewGroup parent) {
            View rowView;
            if (convertView == null) {
                rowView = getActivity().getLayoutInflater().inflate(R.layout.gridview_item, parent, false);
            } else {
                rowView = convertView;
            }

            TextView tv = rowView.findViewById(R.id.item_gridview);
            tv.setText(items.get(position).first);

            return rowView;
        }
    }

    class AdapterItem {
        String first;

        AdapterItem(String first) {
            this.first = first;
        }
    }
}

gridview_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/item_gridview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingStart="0dp"
        android:paddingEnd="10dp"
        android:paddingTop="10dp"
        android:paddingBottom="10dp"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textColor="?android:attr/textColorPrimary"
        />
</LinearLayout>

CardView with GridView (recyclerview_item_gv)

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:clickable="true"
    android:focusable="true"
    android:id="@+id/cv_gv"
    android:layout_marginBottom="20dp"
    >

    <LinearLayout
        android:id="@+id/lineralayout_gv_main"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="10dp"
        android:animateLayoutChanges="true">

        <LinearLayout
            android:id="@+id/linearlayout_gv_titlerow"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginBottom="2dp"
            android:weightSum="100">

            <TextView
                android:id="@+id/tv_gv_A"
                android:layout_weight="90"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:textColor="?android:attr/textColorPrimary"
                style="@android:style/TextAppearance.Medium" />

            <TextView
                android:id="@+id/tv_gv_expandcollapse"
                android:importantForAccessibility="no"
                android:clickable="true"
                android:focusable="true"
                android:layout_weight="10"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="10dp"
                android:textColor="?android:attr/textColorPrimary"
                style="@android:style/TextAppearance.Large" />
        </LinearLayout>

        <RelativeLayout
            android:id="@+id/relativelayout_gv"
            android:animateLayoutChanges="true"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <GridView
                android:id="@+id/gv_a"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:columnWidth="100dp"
                android:numColumns="auto_fit"
                android:layout_marginBottom="20dp"
                android:stretchMode="columnWidth" />
        </RelativeLayout>

    </LinearLayout>
</android.support.v7.widget.CardView>

CardView with TextView (recyclerview_item_tv.xml)

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:clickable="true"
    android:focusable="true"
    android:id="@+id/cv_tv"
    android:layout_marginBottom="20dp">

    <LinearLayout
        android:id="@+id/linearlayout_gv_main"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="10dp"
        android:animateLayoutChanges="true">

        <LinearLayout
            android:id="@+id/linearlayout_tv_titlerow"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginBottom="2dp"
            android:weightSum="100">

            <TextView
                android:id="@+id/tv_tv_A"
                android:layout_weight="90"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:textColor="?android:attr/textColorPrimary"
                style="@android:style/TextAppearance.Medium" />

            <TextView
                android:id="@+id/tv_tv_expandcollapse"
                android:importantForAccessibility="no"
                android:clickable="true"
                android:focusable="true"
                android:layout_weight="10"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="10dp"
                android:textColor="?android:attr/textColorPrimary"
                style="@android:style/TextAppearance.Large" />
        </LinearLayout>

        <RelativeLayout
            android:id="@+id/relativelayout_tv"
            android:animateLayoutChanges="true"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/tv_B"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="?android:attr/textColorPrimary"
                style="@android:style/TextAppearance.Large" />
        </RelativeLayout>

    </LinearLayout>
</android.support.v7.widget.CardView>

Upvotes: 1

Views: 812

Answers (1)

P Fuster
P Fuster

Reputation: 2334

Ok so I made a quick example of how to do it. This is how the Activity class I'm posting looks like, all items are in the same RecyclerView:

recyclerview snapshot

Bear in mind, it might not look like your code because I am trying to use (At least for me) best practices, and also shorten the amount of code and classes by containing a lot of things in the same class.

Here is the Activity:

public class RecyclerActivity extends AppCompatActivity {

RecyclerView recycler;
ArrayList<String> data;
RecyclerView.Adapter<ViewHolder> adapter;
private static final int ITEM_TYPE = 100;
private static final int HEADER_TYPE = 101;
private static final int HEADER_TYPE_2 = 102;
private static final int GRID_TYPE = 103;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_recycler);

    // find recycler,
    recycler = findViewById(R.id.recycler);
    // set the layout
    recycler.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));

    // init data,
    data = new ArrayList<>();
    data.add("Item A");
    data.add("Item B");
    data.add("Item C");

    // create the adapter
    adapter = createAdapter();

    // set the adapter
    recycler.setAdapter(adapter);
}

// creates the adapter,
private RecyclerView.Adapter<ViewHolder> createAdapter() {
    return new RecyclerView.Adapter<ViewHolder>() {
        @NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int type) {
            switch (type) {
                case HEADER_TYPE:
                    // inflate the layout,
                    ViewHolder holderHeader1 = new ViewHolder(inflateHelper(R.layout.header, parent));
                    // set an on click to the view here to create only one object,
                    holderHeader1.itemView.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            // do something
                        }
                    });
                    return holderHeader1;
                case HEADER_TYPE_2:
                    // inflate the layout,
                    ViewHolder holderHeader2 = new ViewHolder(inflateHelper(R.layout.header, parent));
                    // set an on click to the view here to create only one object,
                    holderHeader2.itemView.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            // do something
                        }
                    });
                    return holderHeader2;
                case ITEM_TYPE:
                    // inflate the layout,
                    ViewHolder holderItem = new ViewHolder(inflateHelper(R.layout.item, parent));
                    // set an on click to the view here to create only one object,
                    holderItem.itemView.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            // do something
                        }
                    });
                    return holderItem;
                case GRID_TYPE:
                    // inflate the layout,
                    ViewHolder holderGrid = new ViewHolder(inflateHelper(R.layout.grid, parent));
                    // set an on click to the view here to create only one object,
                    holderGrid.itemView.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            // do something
                        }
                    });
                    return holderGrid;
                    default:
                        // inflate the layout,
                        ViewHolder holderItemDefault = new ViewHolder(inflateHelper(R.layout.item, parent));
                        // set an on click to the view here to create only one object,
                        holderItemDefault.itemView.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View view) {
                                // do something
                            }
                        });
                        return holderItemDefault;
            }
        }

        /**
         * Keep the viewholder simple and the all the view finding here. This way you
         * only have one viewholder.
         */
        @Override
        public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
            // go through the positions
            switch (getItemViewType(position)) {
                case HEADER_TYPE:
                    Button expandButton = viewHolder.itemView.findViewById(R.id.button);
                    expandButton.setText("Expand");
                    break;
                case HEADER_TYPE_2:
                    Button collapseButton = viewHolder.itemView.findViewById(R.id.button);
                    collapseButton.setText("Collapse");
                    break;
                case ITEM_TYPE:
                    // get the current item
                    String item = data.get(position - 3);
                   TextView title = viewHolder.itemView.findViewById(R.id.title);
                   title.setText(item);
                    break;
                case GRID_TYPE:
                    break;
            }
        }

        @Override
        public int getItemCount() {
            return data.size() + 3;
        }

        @Override
        public int getItemViewType(int position) {
            switch (position) {
                case 0:
                    return HEADER_TYPE;
                case 1:
                    return HEADER_TYPE_2;
                case 2:
                    return GRID_TYPE;
                default: return ITEM_TYPE;
            }
        }
    };
}

private View inflateHelper(int resId, ViewGroup parent) {
    return LayoutInflater.from(this).inflate(resId, parent, false);
}

// inner class for viewholder to use,
class ViewHolder extends RecyclerView.ViewHolder {
    public ViewHolder(@NonNull View itemView) {
        super(itemView);
    }
}
}

You might notice that I have used one ViewHolder. This is a pattern I believe we should adopt because it simplifies code so much more. You can find the views you need in the onBind method and thus get away with using one for all types of views.

Also I add the on click listener in the onCreateViewHolder because it is much more efficient to set it there, and it allows for the viewholder to not be onClick specific.

As I said, you can use as many types as you want, you just need to check for it, and set it at the right position.

Here are the layout files if you are interested:

Header

<?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="4dp"
    android:gravity="center">

<Button
    android:id="@+id/button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Expand" />
</LinearLayout>

Item

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp"
    android:elevation="2dp"
    android:layout_margin="16dp"
    android:background="@drawable/rounded">

<TextView
    android:id="@+id/title"
    android:layout_width="0dp"
    android:layout_weight="1"
    android:layout_height="wrap_content"
    android:text="Item A"
    android:textColor="#fff"/>

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_chevron_right"
    android:tint="#fff"/>
 </LinearLayout>

Grid Layout (I cheated a bit here because I didn't see a reason to not hardcode vowels, although you might need to do it dynamically)

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginStart="32dp"
    android:layout_marginEnd="32dp"
    android:layout_marginTop="16dp"
    android:layout_marginBottom="16dp"
    android:padding="16dp"
    android:background="@drawable/rounded"
    android:backgroundTint="#fff"
    android:elevation="4dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:paddingBottom="16dp"
            android:text="French Vowels"
            android:textStyle="bold"
            style="@style/Base.TextAppearance.AppCompat.Medium"/>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="8dp">

            <TextView
                android:id="@+id/a"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:gravity="center"
                android:layout_height="wrap_content"
                android:text="a"
                style="@style/Base.TextAppearance.AppCompat.Medium"/>

            <TextView
                android:id="@+id/e"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:gravity="center"
                android:layout_height="wrap_content"
                android:text="e"
                style="@style/Base.TextAppearance.AppCompat.Medium"/>

            <TextView
                android:id="@+id/i"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:gravity="center"
                android:layout_height="wrap_content"
                android:text="i"
                style="@style/Base.TextAppearance.AppCompat.Medium"/>
        </LinearLayout>
        <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
            android:padding="8dp">

        <TextView
            android:id="@+id/o"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:gravity="center"
            android:layout_height="wrap_content"
            android:text="o"
            style="@style/Base.TextAppearance.AppCompat.Medium"/>

        <TextView
            android:id="@+id/u"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:gravity="center"
            android:layout_height="wrap_content"
            android:text="u"
            style="@style/Base.TextAppearance.AppCompat.Medium"/>

        <TextView
            android:id="@+id/y"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:gravity="center"
            android:layout_height="wrap_content"
            android:text="y"
            style="@style/Base.TextAppearance.AppCompat.Medium"/>
    </LinearLayout>
    </LinearLayout>
</FrameLayout>

Rounded Drawable

<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="rectangle" xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#222"/>
    <corners android:radius="6dp"/>
</shape>

For anyone reading, if you happen to use Kotlin, consider using my small library which eliminates this recycler view boilerplate to a simple chain of functions:

https://github.com/Pfuster12/BoilerCycle

Upvotes: 2

Related Questions