0

I am trying to implement multiple view or so called heterogeneous recyclerview using firebase realtime database. I found a example of heterogeneous recyclerview implementation on github.

I followed this to implement on firebase. But my code didn't work.

fragment_search.java (one of the fragment in my tabbed view) This is my fragment file which contains the main recyclerview which will hold multiple views

public class fragment_search extends Fragment {

RecyclerView recyclerViewSearch;

private static SingleSearchBannerModel singleSearchBannerModel;
private static SingleSearchType1Model singleSearchType1Model;

private static DatabaseReference databaseReference;

private List<Object> objects = new ArrayList<>();

private static SearchMainAdapter searchMainAdapter;

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_search,null);

    //Recycler view initialized and Properties Set.
    recyclerViewSearch = view.findViewById(R.id.recyclerview_search);
    recyclerViewSearch.setHasFixedSize(true);
    recyclerViewSearch.setLayoutManager(new LinearLayoutManager(getContext()));

    databaseReference = FirebaseDatabase.getInstance().getReference();
    return view;
}


private List<Object> getObjects(){
    objects.addAll(getBannerData());
    objects.addAll(getType1Data());
    return objects;
}

public static List<SingleSearchBannerModel> getBannerData(){
    final List<SingleSearchBannerModel> singleSearchBannerModelList = new ArrayList<>();
    databaseReference.child("Featured").addChildEventListener(new ChildEventListener() {
        @Override
        public void onChildAdded(DataSnapshot dataSnapshot, String s) {
            singleSearchBannerModel = dataSnapshot.getValue(SingleSearchBannerModel.class);

            singleSearchBannerModelList.add(singleSearchBannerModel);
            searchMainAdapter.notifyDataSetChanged();

            System.out.println("this is image in getBanner == "+singleSearchBannerModel.getImage());
        }

        @Override
        public void onChildChanged(DataSnapshot dataSnapshot, String s) {

        }

        @Override
        public void onChildRemoved(DataSnapshot dataSnapshot) {

        }

        @Override
        public void onChildMoved(DataSnapshot dataSnapshot, String s) {

        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });
    return singleSearchBannerModelList;
}

public static List<SingleSearchType1Model> getType1Data(){
    final List<SingleSearchType1Model> singleSearchType1ModelList = new ArrayList<>();
    databaseReference.child("Featured").addChildEventListener(new ChildEventListener() {
        @Override
        public void onChildAdded(DataSnapshot dataSnapshot, String s) {
            singleSearchType1Model = dataSnapshot.getValue(SingleSearchType1Model.class);

            singleSearchType1ModelList.add(singleSearchType1Model);
            searchMainAdapter.notifyDataSetChanged();
        }

        @Override
        public void onChildChanged(DataSnapshot dataSnapshot, String s) {

        }

        @Override
        public void onChildRemoved(DataSnapshot dataSnapshot) {

        }

        @Override
        public void onChildMoved(DataSnapshot dataSnapshot, String s) {

        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });
    return singleSearchType1ModelList;
}

@Override
public void onStart() {
    super.onStart();
    searchMainAdapter = new SearchMainAdapter(getContext(),getObjects());
    System.out.println("this is getObject == "+getObjects());
    System.out.println("this is getBanner == "+getBannerData());
    System.out.println("this is getType1 == "+getType1Data());
    recyclerViewSearch.setAdapter(searchMainAdapter);
}
}

fragment_home.xml this is the xml file of fragment which contains one recyclerview (My main recyclerview).

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<android.support.v7.widget.RecyclerView
    android:id="@+id/recyclerview_search"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

</android.support.v7.widget.RecyclerView>

Model files

*I have two model files. Basically getter and setters *

SingleSearchBannerModel.java My firsts model file.

public class SingleSearchBannerModel {

String Image;

public SingleSearchBannerModel() {
}

public SingleSearchBannerModel(String image) {
    Image = image;
}

public String getImage() {
    return Image;
}

public void setImage(String image) {
    Image = image;
}
}

SingleSearchType1Model.java This is my second model file.

public class SingleSearchType1Model {

String Image;

public SingleSearchType1Model() {
}

public SingleSearchType1Model(String image) {
    Image = image;
}

public String getImage() {
    return Image;
}

public void setImage(String image) {
    Image = image;
}
}

RecyclerView Adapters

Adapters are the ones used to set the value to the recyclerView. I have three Adapters as I'm trying to insert two views in one recyclerview. One is the MainAdapter which combines the other two Adapters. The MainAdapter is set to the the recyclerview of fragment_search.java (my main fragment)

SearchBannerAdapter.java This adapter is to generate the required layout file and populate the inflated layout file.

public class SearchBannerAdapter extends RecyclerView.Adapter<SearchBannerAdapter.TaskViewHolder>{
List<SingleSearchBannerModel> data;

public SearchBannerAdapter(List<SingleSearchBannerModel> data) {
    this.data = data;
}

@NonNull
@Override
public TaskViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.search_single_banners,parent,false);
    return new TaskViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull TaskViewHolder holder, int position) {
    SingleSearchBannerModel singleSearchBannerModel = data.get(position);

    Picasso.get().load(singleSearchBannerModel.getImage()).fit().into(holder.imageView);
}

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

public static class TaskViewHolder extends RecyclerView.ViewHolder{
    View mView;
    ImageView imageView;
    public TaskViewHolder(View itemView) {
        super(itemView);
        mView = itemView;
        imageView = mView.findViewById(R.id.search_banner_imgView);
    }
}
}

search_single_banners.xml The xml file which is inflated in SearchBannerAdapter.java

<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">

<android.support.v7.widget.CardView
    android:layout_width="290dp"
    android:layout_height="160dp"
    android:id="@+id/longCard"
    android:layout_marginTop="50dp"
    android:layout_marginEnd="20dp"
    app:cardCornerRadius="20sp"
    app:cardElevation="@dimen/ten"
    app:cardPreventCornerOverlap="false"
    android:layout_centerHorizontal="true">

    <ImageView
        android:id="@+id/search_banner_imgView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/test1"/>

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

SearchType1Adapter.java This is the second adapter which inflates search_type_1.xml

public class SearchType1Adapter extends RecyclerView.Adapter<SearchType1Adapter.TaskViewHolder>{

List<SingleSearchType1Model> data;

public SearchType1Adapter(List<SingleSearchType1Model> data) {
    this.data = data;
}

@NonNull
@Override
public TaskViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.search_type_1,parent,false);
    return new TaskViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull TaskViewHolder holder, int position) {
    SingleSearchType1Model singleSearchType1Model = data.get(position);

    Picasso.get().load(singleSearchType1Model.getImage()).fit().into(holder.imageView);
}

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

public static class TaskViewHolder extends RecyclerView.ViewHolder{
    View mView;
    ImageView imageView;
    public TaskViewHolder(View itemView) {
        super(itemView);
        mView = itemView;
        imageView = mView.findViewById(R.id.search_single_type_1_imgView);
    }
}
}

search_type_1.xml The file that is inflated in SearchType1Adapter.java

<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">

<android.support.v7.widget.CardView
    android:layout_width="290dp"
    android:layout_height="160dp"
    android:id="@+id/longCard"
    android:layout_marginTop="50dp"
    android:layout_marginEnd="20dp"
    app:cardCornerRadius="20sp"
    app:cardElevation="@dimen/ten"
    app:cardPreventCornerOverlap="false"
    android:layout_centerHorizontal="true">

    <ImageView
        android:id="@+id/search_single_type_1_imgView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/test1"/>

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

SearchMainAdapter.java This is the main adapter which is used in fragment_search to populate the main recyclerview.

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

private Context context;
private List<Object> items;
private final int BANNER = 1;
private final int TYPE1 = 2;

public SearchMainAdapter(Context context, List<Object> items) {
    this.context = context;
    this.items = items;
}

@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    View view;
    RecyclerView.ViewHolder holder;
    switch (viewType){
        case BANNER:
            view = inflater.inflate(R.layout.search_banners,parent,false);
            holder = new BannerViewHolder(view);
            break;
        case TYPE1:
            view = inflater.inflate(R.layout.search_type_1,parent,false);
            holder = new Type1ViewHolder(view);
            break;
        default:
            view = inflater.inflate(R.layout.search_banners,parent,false);
            holder = new Type1ViewHolder(view);
            break;
    }
    return holder;
}

@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
    if(holder.getItemViewType() == BANNER)
        BannerView((BannerViewHolder) holder);
    else if (holder.getItemViewType() == TYPE1)
        Type1View((Type1ViewHolder) holder);
}

private void BannerView(BannerViewHolder holder){
    SearchBannerAdapter searchBannerAdapter = new SearchBannerAdapter(getBannerData());
    holder.recyclerView.setLayoutManager(new LinearLayoutManager(context,LinearLayoutManager.HORIZONTAL,false));
    holder.recyclerView.setAdapter(searchBannerAdapter);
}

private void Type1View(Type1ViewHolder holder){
    SearchType1Adapter searchType1Adapter = new SearchType1Adapter(getType1Data());
    holder.recyclerView.setLayoutManager(new LinearLayoutManager(context,LinearLayoutManager.HORIZONTAL,false));
    holder.recyclerView.setAdapter(searchType1Adapter);
}

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

@Override
public int getItemViewType(int position) {
    if (items.get(position) instanceof SingleSearchBannerModel)
        return BANNER;
    if (items.get(position) instanceof SingleSearchType1Model)
        return TYPE1;
    return -1;
}

public static class BannerViewHolder extends RecyclerView.ViewHolder{
    RecyclerView recyclerView;
    public BannerViewHolder(View itemView) {
        super(itemView);
        recyclerView = itemView.findViewById(R.id.recyclerview_banners);
    }
}
public static class Type1ViewHolder extends RecyclerView.ViewHolder{
    RecyclerView recyclerView;
    public Type1ViewHolder(View itemView) {
        super(itemView);
        recyclerView = itemView.findViewById(R.id.recyclerview_search_type_1);
    }
}
}

search_banners.xml and search_type_1.xml have the same code

<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v7.widget.RecyclerView
    android:id="@+id/recyclerview_banners"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

</android.support.v7.widget.RecyclerView>

The above code is not working. I want to know where i went wrong. My main objective is to make an app something like google play store or hotstar (In terms of UI). So I assume they are using single recyclerview and multiple view within it.

Thanks for help.

Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
Ritesh Singh
  • 782
  • 9
  • 19
  • 1
    please share example links of heterogenous recyclerview using firebase if you know one. – Ritesh Singh Aug 04 '18 at 08:58
  • **[This](https://stackoverflow.com/questions/50777733/firebaserecycleradapter-doesnt-recognize-first-layout-as-a-position)** might help you. – Alex Mamo Aug 04 '18 at 09:29
  • thanks for replying. But he is able to show one of the view. In my case I'm not able to see even one view (layout). So i don't think the problem is similar @AlexMamo – Ritesh Singh Aug 04 '18 at 09:55
  • 1
    I did a bit of debugging and came to know that my **mainAdapters.java** `getItemViewType()` method is not being called. And as much i know this is the first method that should be called in my adapter after the constructor. Only the constructor is called from mainAdapter.java @AlexMamo – Ritesh Singh Aug 04 '18 at 10:14

1 Answers1

0

I know this answer is a bit late but it might still help someone in future. Here is how I achived the goal

public class HeteroActivity extends AppCompatActivity {
private ArrayList<Object> objects = new ArrayList<>();
private static final String TAG = HeteroActivity.class.getSimpleName();

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_hetero);
    RecyclerView recyclerView = findViewById(R.id.recycler_View);
    MainAdapter adapter = new MainAdapter(this, getObject());
    recyclerView.setAdapter(adapter);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));

}

private ArrayList<Object> getObject() {
    DatabaseReference reference = FirebaseDatabase.getInstance().getReference().child("Data");
    reference.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {

            objects.add(getVerticalData(dataSnapshot).get(0));
            objects.add(getHorizontalData(dataSnapshot).get(0));
        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {

        }
    });


    return objects;
}

public static ArrayList<SingleVertical> getVerticalData(DataSnapshot dataSnapshot) {
    ArrayList<SingleVertical> singleVerticals = new ArrayList<>();


    for (DataSnapshot snapshot : dataSnapshot.getChildren()){

        singleVerticals.add(new SingleVertical(

                snapshot.child("price").getValue(String.class),
                snapshot.child("location").getValue(String.class),
                snapshot.child("image").getValue(String.class)

        ));
        Log.e(TAG,"Text loaded");
    }



    return singleVerticals;
}


public static ArrayList<SingleHorizontal> getHorizontalData(DataSnapshot dataSnapshot) {
    ArrayList<SingleHorizontal> singleHorizontals = new ArrayList<>();
    for (DataSnapshot snapshot : dataSnapshot.getChildren()){
        singleHorizontals.add(new SingleHorizontal(

                snapshot.child("price").getValue(String.class),
                snapshot.child("location").getValue(String.class),
                snapshot.child("image").getValue(String.class)
        ));
        Log.e(TAG,"Horizontal data loaded");
    }
    return singleHorizontals;
}

}

Then in your main Adapter do this:

private void verticalView(final VerticalViewHolder holder) {
    DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference().child("Data");
    databaseReference.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            VerticalAdapter adapter1 = new VerticalAdapter(getVerticalData(dataSnapshot), context);
            holder.recyclerView.setLayoutManager(new LinearLayoutManager(context));
            holder.recyclerView.setAdapter(adapter1);
        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {

        }
    });


}




private void horizontalView(final HorizontalViewHolder holder) {
    DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference().child("Data");
    databaseReference.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            HorizontalAdapter adapter = new HorizontalAdapter(getHorizontalData(dataSnapshot),context);
            holder.recyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false));
            holder.recyclerView.setAdapter(adapter);
        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {

        }
    });
}
Gideon
  • 3
  • 3