Search code examples
javaandroidandroid-recyclerviewretrofit

Retrofit RecyclerViewAdapter List not assigned


I am stuck in something

List is null as you see but I want to assign it to the response from API before the pass it to the Adapter. But the List passed as null and throw null exception. I call the API method in onCreate() method before the Adapter initialization but it does not work somehow.

I could not understand what I miss here. How can the List is not assigned. I tried to initialize new ArrayList and it did not throw any error but this time the RecyclerView is empty.

Any help?

Code:

public class InventoryInfoActivity extends AppCompatActivity {


InventoryInfoRecyclerViewAdapter inventoryInfoRecyclerViewAdapter;
RecyclerView recyclerView;
ProductService productService;
List<Product> productList;

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

    productService = new ProductService(this);
    // CALL METHOD
    this.getProductList();

    recyclerView  = findViewById(R.id.recycler_inventory);
    recyclerView.setLayoutManager(new LinearLayoutManager(InventoryInfoActivity.this));
    inventoryInfoRecyclerViewAdapter = new InventoryInfoRecyclerViewAdapter(productList,this);
    recyclerView.setAdapter(inventoryInfoRecyclerViewAdapter);
}

// API REQUEST
private void getProductList() {
    productService.getProducts().enqueue(new Callback<List<Product>>() {
        @Override
        public void onResponse(Call<List<Product>> call, Response<List<Product>> response) {
            if (response.isSuccessful()) {
                Log.i("REQUEST","OK");
               // ASSIGN
                productList = response.body();
            }else {
                Log.i("REQUEST","BAD");
                productList = new ArrayList<>();
            }
        }

        @Override
        public void onFailure(Call<List<Product>> call, Throwable t) {
            Log.i("REQUEST","FAILED");
            productList = new ArrayList<>();
        }
    });
}


@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.search_action,menu);
    MenuItem menuItem = menu.findItem(R.id.action_search);

    SearchView searchView = (SearchView) menuItem.getActionView();
    searchView.setQueryHint("Ara");

    searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String query) {
            return false;
        }

        @Override
        public boolean onQueryTextChange(String newText) {
            filter(newText);
            return false;
        }
    });


    return super.onCreateOptionsMenu(menu);
}

private void filter(String text) {
    ArrayList<Product> filteredlist = new ArrayList<>();
    for (Product item : productList) {
        if (item.getProductName().toLowerCase().contains(text.toLowerCase())) {
            filteredlist.add(item);
        }
    }
    if (filteredlist.isEmpty()) {
        Toast.makeText(this, "Böyle bir ürün yok", Toast.LENGTH_SHORT).show();
    } else {
        inventoryInfoRecyclerViewAdapter.filterList(filteredlist);
    }
}
}

LogCat:

E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.tempter.stoktakip, PID: 32165
java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference

Solution

  • The problem is here that after you get your response you should inform your adapter that you have a new List. There are many cool methods in RecyclerView called notifiyDataSetChanged.

    What you did is not all wrong. Let me explain what you can do in your code:

    1. The variable that you define is a list which doesn’t have any value so its null.

    private List<Product> items = new ArrayList<Product>();

    or you should fix the getItems method in your adapter like below:

     public int getItems() {
        return items == null || items.isEmpty() ? 0 : items.size();
    }
    
    1. After you get your response, please pass the new list to adapter. First you need to create in adapter a method that pass those data like below
        //  in your adapter
        public void setData(List<String> newItems) {
            adapterItems.clear();
            adapterItems.addAll(newItems);
            notifyDataSetChange();
        }
    

    and then after your successfull api call you should do it like below:

    private void getProductList() {
            productService.getProducts().enqueue(new Callback<List<Product>>() {
                @Override
                public void onResponse(Call<List<Product>> call, Response<List<Product>> response) {
                    if (response.isSuccessful()) {
                        Log.i("REQUEST", "OK");
                        // ASSIGN
                        productList = response.body();
            
                        //!! This is here important
    
                        inventoryInfoRecyclerViewAdapter.setData(productList);
    
                        //!! This is here important 
    
                    } else {
                        Log.i("REQUEST", "BAD");
                        productList = new ArrayList<>();
                    }
                }
    
                @Override
                public void onFailure(Call<List<Product>> call, Throwable t) {
                    Log.i("REQUEST", "FAILED");
                    productList = new ArrayList<>();
                }
            });
        }
    

    Now that’s it. You should now have you recyclerView updated with the new list.