1

Scenario:

I am trying to create a RecyclerView which will show a few different kinds of item (food, book, recipes) in one list. The data will be fetched from the server with the help of retrofit when the user types in some keyword into the search bar. So, I have an activity with a search bar, another to show the data in a recycler view.

What is have done so far: 1. Fetch data from server with the help of recycler view on search. 2. Send the data (in serializable) form from the search activity to display activity. 3. Create a recycler view with multiple view adapter (With the help of some tutorials listed below).

links:

  1. Example 1

  2. Example 2

Some related Projects:

  1. Some Example Project

Understanding

What i have understood so far is that for showing multiple types of items in the recycler view, we need to create an abstract view holder and individual view holders for each object. They must also have their corresponding layout and they layouts are inflated dynamically into the recycler view holder.

Problem:

  1. Trying to set adapter to my recycler view adapter gives error.
  2. The problem Im facing is that I cannot seem to bind the data that I received from the server with the corresponding view holder.

Code:

I will leave out the activity where I am fetching data because its working.

SearhResultActivity (this activity receives the data from SearchActivity and is supposed to show the recycler view with the data)

package com.example.naisse.Activity;

import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v7.widget.RecyclerView;
import android.widget.FrameLayout;
import com.example.naisse.AdapterAndClass.ResultAdaptar;
import com.example.naisse.AdapterAndClass.SearchRecyclerAdapter;
import com.example.naisse.Api.Model.Search.Article;
import com.example.naisse.Api.Model.Search.Food;
import com.example.naisse.Api.Model.Search.Hadith;
import com.example.naisse.Api.Model.Search.Manuscript;
import com.example.naisse.Api.Model.Search.Quran;
import com.example.naisse.Fragment.SearchTabFragment;
import com.example.naisse.R;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;

public class SearchResultActivity extends HomeActivity {

private static final String TAG = "SearchResultActivity";

@BindView(R.id.search_result_recycler)
RecyclerView recyclerView;

private SearchRecyclerAdapter searchRecyclerAdapter;

private List<String> dataList;

protected FrameLayout frames;
private FragmentManager mFragmentManager;
private ResultAdaptar resultAdaptar;


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

    frames = findViewById(R.id.search_bar_holder);
    mFragmentManager = (this).getSupportFragmentManager();
    mFragmentManager.beginTransaction().add(R.id.search_bar_holder, new SearchTabFragment()).commit();


    Food foodData = new Food();
    ArrayList<Book> getBookArray = new ArrayList<>();
    ArrayList<Recipe> getRecipe = new ArrayList<>();

    foodData = (Food) getIntent().getSerializableExtra("foodDetails");
    getBookArray = (ArrayList<Book>) getIntent().getSerializableExtra("bookList");
    getRecipe = (ArrayList<Recipe>) getIntent().getSerializableExtra("recipeList");

    /*There is something wrong here, it says "Attempt to invoke virtual method on a null object reference" for searchRecyclerAdapter*/
    searchRecyclerAdapter = new SearchRecyclerAdapter(this);
    recyclerView.setAdapter(searchRecyclerAdapter);

}

}

SearchRecyclerAdapter (This adapter holders the codes for declaration and connecting all the viewHolders to the abstract viewHolder with connects to the recycler view)

package com.example.naisse.AdapterAndClass;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.example.naisse.Api.Model.Search.Food;
import com.example.naisse.Api.Model.Search.Quran;
import com.example.naisse.R;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;

public class SearchRecyclerAdapter extends RecyclerView.Adapter<SearchRecyclerAdapter.AbstractViewHolder>{

private static final String TAG = "SearchRecyclerAdapter";

public static final int TYPE_FOOD = 0;
public static final int TYPE_BOOK = 1;


private Context context;
private List<Object> data;

/*I belive im missing something here*/
public SearchRecyclerAdapter(Context context){
    this.context = context;
}

public void add(List searchData){
    if(data == null){
        data = new ArrayList<>();
    }
    data.clear();
    data.addAll(searchData);
    notifyDataSetChanged();
}


@Override
public AbstractViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {

    switch (i){
        case TYPE_FOOD:{
            /*search_result_fragment is the xml layout for food adapter*/
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.search_result_fragment, viewGroup, false);
            return new FoodViewAdapter(view);
        }
        case TYPE_BOOK:{
            /*search_result_fragment is the xml layout for book adapter*/
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.viewholder_book, viewGroup, false);
            return new BooksViewAdapter(view);
        }

        default:
            throw new IllegalArgumentException("Invalid view type");

    }

}

@Override
public void onBindViewHolder(AbstractViewHolder abstractViewHolder, int i) {
    Object object = data.get(i);
    abstractViewHolder.bind(object);
}

@Override
public int getItemViewType(int i){
    Object object = data.get(i);

    if (object instanceof Food){
        return TYPE_FOOD;
    }else if (object instanceof Book){
        return TYPE_BOOK;
    }

    throw new IllegalArgumentException("Invalid position");
}

@Override
public int getItemCount() {
    return data == null ? 0 : data.size();
}

public abstract class AbstractViewHolder<T> extends RecyclerView.ViewHolder{

    public AbstractViewHolder(@NonNull View itemView) {
        super(itemView);

        ButterKnife.bind(this, itemView);
    }

    public abstract void bind(T type);
}


public class FoodViewAdapter extends AbstractViewHolder<Food> {
    @BindView(R.id.result_food_title)
    TextView foodTitle;
    @BindView(R.id.result_food_content)
    TextView content;

    private FoodViewAdapter(View itemView){
        super(itemView);
    }

    @Override
    public void bind(Food type) {
        foodTitle.setText("Food");
        content.setText("title");
    }

}

public class BooksViewAdapter extends AbstractViewHolder<Book> {
    @BindView(R.id.result_book_title)
    TextView bookTitle;
    @BindView(R.id.result_book_content)
    TextView content;

    private BooksViewAdapter(View itemView){
        super(itemView);
    }

    @Override
    public void bind(Book type) {
        bookTitle.setText("title");
        content.setText("Text");
    }

}

}

Question:

How do i set the adapter and how do I bind my data to the corresponding views?

P.S Please let me know if you want to see some other classes or XML layouts.

Logcat

E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.naisse, PID: 12570
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.naisse/com.example.naisse.Activity.SearchResultActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v7.widget.RecyclerView.setAdapter(android.support.v7.widget.RecyclerView$Adapter)' on a null object reference
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:193)
    at android.app.ActivityThread.main(ActivityThread.java:6669)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
 Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v7.widget.RecyclerView.setAdapter(android.support.v7.widget.RecyclerView$Adapter)' on a null object reference
    at com.example.naisse.Activity.SearchResultActivity.onCreate(SearchResultActivity.java:67)
    at android.app.Activity.performCreate(Activity.java:7136)
    at android.app.Activity.performCreate(Activity.java:7127)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048) 
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) 
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) 
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808) 
    at android.os.Handler.dispatchMessage(Handler.java:106) 
    at android.os.Looper.loop(Looper.java:193) 
    at android.app.ActivityThread.main(ActivityThread.java:6669) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 
Mill3r
  • 454
  • 8
  • 26
  • 1
    What error do you get? At first glance your app looks o.k., the only thing I'm not sure about, is, that you're calling Butterknife.bind() in the AbstractViewHolder. – Ridcully May 04 '19 at 07:05
  • in my SearchResultActivity class when im setting the adapter to recycler view, i get the error (check comment in code). What is think is im missing something in the SearchResultActivity constructor by which i can pass the data to the recycler view. Im not really sure about it though. – Mill3r May 04 '19 at 07:14
  • try to share all your log cat. attempt to null is mostly for wrong initialization of views – Radesh May 04 '19 at 07:17
  • I have added the logcat. – Mill3r May 04 '19 at 07:21
  • logcat suggests that your `recyclerView` is null in `SearchResultActivity`. – Ranjan May 04 '19 at 07:31
  • Yes. I know that. I am looking for a solution to that. – Mill3r May 04 '19 at 07:37
  • Im sure there is something wrong somewhere in my RecyclerViewAdapter and they way Im binding data to each view. – Mill3r May 04 '19 at 07:38

1 Answers1

1

You use butterknife and forget add bind views in activity

Simply add this line

ButterKnife.bind(this);

To your onCreate() of SearchResultActivity

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_search_result);
    ButterKnife.bind(this);
    //other codes
}

Second Part

for second part you need to add data to your recycelerView (adapter)

For doing this we change your add function in adapter. use this code instead of yours

public void add(ArrayList newData){
    if(data == null){
        data = new ArrayList<>();
    }
    data.addAll(searchData);
    notifyDataSetChanged();
}

Now in SearchResultActivity try to send your data to adapter like this

searchRecyclerAdapter = new SearchRecyclerAdapter(this);
recyclerView.setAdapter(searchRecyclerAdapter);
searchRecyclerAdapter.add(foodData);
searchRecyclerAdapter.add(getBookArray);
searchRecyclerAdapter.add(getRecipe);
Radesh
  • 10,690
  • 3
  • 42
  • 60
  • I suppose i add this inside the onCreate() of SearchResultActivity? – Mill3r May 04 '19 at 07:43
  • @Mill3r yes in SearchResultActivity – Radesh May 04 '19 at 07:44
  • This seems to have solved the "null" reference of the SearchResultActivity, so the the recycler view opens in the, however my RecyclerView is still empty (second part of my question). – Mill3r May 04 '19 at 07:47
  • I tried this and heres what is happening, while adding the objects (foodData, getBookArray, etc.) to searchRecyclerAdapter, i am getting an error in each line stating **Food cannot be converted to List** or **ArrayList cannot be converted to List** – Mill3r May 04 '19 at 08:32
  • @Mill3r check edited answer . i change List to ArrayList – Radesh May 04 '19 at 08:39
  • @Mill3r remeber that your foodData is not a ArrayList and if you what use this you need to add it to a new ArrayList – Radesh May 04 '19 at 08:40
  • Yes I had noticed that and had done what you had suggested already. But my recycler view is still empty. – Mill3r May 04 '19 at 08:46
  • I have also noticed something, while debugging, the application is not entering onCreateViewHolder method at all. – Mill3r May 04 '19 at 09:16
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/192828/discussion-between-radesh-and-mill3r). – Radesh May 04 '19 at 09:49
  • Hey, I found your question A little similar to my one, could you please help me with, https://stackoverflow.com/questions/67246126/how-to-make-recyclerview-multiple-viewtypes-with-retrofit-and-viewmodel – Moataz Apr 25 '21 at 09:02