I am getting a working app (kinda). It is meant to populate the screen from a request for JSON data. I thought what I have so far would work but I guess not.
onResponse does not seem to ever be called so the data is never assigned to a variable to be processed and I don't know how to fix this. Any and all help is very much appreciated.
Below is my relevant code:
My JSON data is:
{
"popular":[
{
"name":"Pizza",
"imageURL":"https://static4.depositphotos.com/1016418/315/i/600/depositphotos_3158962-stock-photo-pepperoni-pizza-isolated.jpg",
"rating":"4.8",
"deliveryTime":"45 min",
"deliveryCharges":"Free Delivery",
"price":"15",
"note":"Delicious"
},
{
"name":"Chick-Fil-A",
"imageURL":"https://media.istockphoto.com/photos/chicken-bacon-club-sandwich-picture-id585602032?k=6&m=585602032&s=612x612&w=0&h=DzlMVMOaqC25Zf78NPGzr9nLOf7wANEgv9u_vScu4d8=",
"rating":"5.0",
"deliveryTime":"15 min",
"deliveryCharges":"3",
"price":"7",
"note":"Classic"
},
{
"name":"Mac & Cheese",
"imageURL":"https://media.istockphoto.com/photos/homemade-baked-creamy-macaroni-and-cheese-picture-id483485720?k=6&m=483485720&s=612x612&w=0&h=u2Wb4hR0cIqNAm8q_4j-oFPrtC4hT_YXaq2srA2zmqI=",
"rating":"4.7",
"deliveryTime":"20 min",
"deliveryCharges":"1",
"price":"4",
"note":"Homestyle"
}
],
"recommended":[
{
"name":"Chicken Tikka Masala",
"imageURL":"https://media.istockphoto.com/photos/chicken-tikka-masala-curry-with-rice-and-naan-bread-picture-id1143530019?k=6&m=1143530019&s=612x612&w=0&h=aOCxTVcwi88NHCX9-xgNu7cmPX9rq5AGtEVYblNTnGc=",
"rating":"4.8",
"deliveryTime":"25 min",
"deliveryCharges":"2",
"price":"14",
"note":"Fan Favorite"
},
{
"name":"Strawberry Milkshake",
"imageURL":"https://thumbs.dreamstime.com/b/strawberry-milkshake-covered-whipped-cream-plastic-glass-isolated-white-background-44432579.jpg",
"rating":"4.5",
"deliveryTime":"10 min",
"deliveryCharges":"0",
"price":"4",
"note":"Chilly"
},
{
"name":"3 Tacos",
"imageURL":"https://previews.123rf.com/images/gdolgikh/gdolgikh1703/gdolgikh170300221/74432760-mexican-tacos-with-beef.jpg",
"rating":"4.6",
"deliveryTime":"17 min",
"deliveryCharges":"1",
"price":"12",
"note":"Hard or Soft Shell"
}
],
"allmenu":[
{
"name":"Pizza",
"imageURL":"https://static4.depositphotos.com/1016418/315/i/600/depositphotos_3158962-stock-photo-pepperoni-pizza-isolated.jpg",
"rating":"4.8",
"deliveryTime":"45 min",
"deliveryCharges":"Free Delivery",
"price":"15",
"note":"Delicious"
},
{
"name":"Chick-Fil-A",
"imageURL":"https://media.istockphoto.com/photos/chicken-bacon-club-sandwich-picture-id585602032?k=6&m=585602032&s=612x612&w=0&h=DzlMVMOaqC25Zf78NPGzr9nLOf7wANEgv9u_vScu4d8=",
"rating":"5.0",
"deliveryTime":"15 min",
"deliveryCharges":"3",
"price":"7",
"note":"Classic"
},
{
"name":"Mac & Cheese",
"imageURL":"https://media.istockphoto.com/photos/homemade-baked-creamy-macaroni-and-cheese-picture-id483485720?k=6&m=483485720&s=612x612&w=0&h=u2Wb4hR0cIqNAm8q_4j-oFPrtC4hT_YXaq2srA2zmqI=",
"rating":"4.7",
"deliveryTime":"20 min",
"deliveryCharges":"1",
"price":"4",
"note":"Homestyle"
},
{
"name":"Chicken Tikka Masala",
"imageURL":"https://media.istockphoto.com/photos/chicken-tikka-masala-curry-with-rice-and-naan-bread-picture-id1143530019?k=6&m=1143530019&s=612x612&w=0&h=aOCxTVcwi88NHCX9-xgNu7cmPX9rq5AGtEVYblNTnGc=",
"rating":"4.8",
"deliveryTime":"25 min",
"deliveryCharges":"2",
"price":"14",
"note":"Fan Favorite"
},
{
"name":"Strawberry Milkshake",
"imageURL":"https://thumbs.dreamstime.com/b/strawberry-milkshake-covered-whipped-cream-plastic-glass-isolated-white-background-44432579.jpg",
"rating":"4.5",
"deliveryTime":"10 min",
"deliveryCharges":"0",
"price":"4",
"note":"Chilly"
},
{
"name":"3 Tacos",
"imageURL":"https://previews.123rf.com/images/gdolgikh/gdolgikh1703/gdolgikh170300221/74432760-mexican-tacos-with-beef.jpg",
"rating":"4.6",
"deliveryTime":"17 min",
"deliveryCharges":"1",
"price":"12",
"note":"Hard or Soft Shell"
}
]
}
I have models for Allmenu, FoodData, Popular, and Recommended.
AllMenu:
package com.example.bearcateats.model;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
#Generated("jsonschema2pojo")
public class Allmenu {
#SerializedName("name")
#Expose
private String name;
#SerializedName("imageURL")
#Expose
private String imageURL;
#SerializedName("rating")
#Expose
private String rating;
#SerializedName("deliveryTime")
#Expose
private String deliveryTime;
#SerializedName("deliveryCharges")
#Expose
private String deliveryCharges;
#SerializedName("price")
#Expose
private String price;
#SerializedName("note")
#Expose
private String note;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getImageURL() {
return imageURL;
}
public void setImageURL(String imageURL) {
this.imageURL = imageURL;
}
public String getRating() {
return rating;
}
public void setRating(String rating) {
this.rating = rating;
}
public String getDeliveryTime() {
return deliveryTime;
}
public void setDeliveryTime(String deliveryTime) {
this.deliveryTime = deliveryTime;
}
public String getDeliveryCharges() {
return deliveryCharges;
}
public void setDeliveryCharges(String deliveryCharges) {
this.deliveryCharges = deliveryCharges;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
FoodData:
package com.example.bearcateats.model;
import java.util.List;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
#Generated("jsonschema2pojo")
public class FoodData {
#SerializedName("popular")
#Expose
private List<Popular> popular = null;
#SerializedName("recommended")
#Expose
private List<Recommended> recommended = null;
#SerializedName("allmenu")
#Expose
private List<Allmenu> allmenu = null;
public List<Popular> getPopular() {
return popular;
}
public void setPopular(List<Popular> popular) {
this.popular = popular;
}
public List<Recommended> getRecommended() {
return recommended;
}
public void setRecommended(List<Recommended> recommended) {
this.recommended = recommended;
}
public List<Allmenu> getAllmenu() {
return allmenu;
}
public void setAllmenu(List<Allmenu> allmenu) {
this.allmenu = allmenu;
}
}
Popular:
package com.example.bearcateats.model;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
#Generated("jsonschema2pojo")
public class Popular {
#SerializedName("name")
#Expose
private String name;
#SerializedName("imageURL")
#Expose
private String imageURL;
#SerializedName("rating")
#Expose
private String rating;
#SerializedName("deliveryTime")
#Expose
private String deliveryTime;
#SerializedName("deliveryCharges")
#Expose
private String deliveryCharges;
#SerializedName("price")
#Expose
private String price;
#SerializedName("note")
#Expose
private String note;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getImageURL() {
return imageURL;
}
public void setImageURL(String imageURL) {
this.imageURL = imageURL;
}
public String getRating() {
return rating;
}
public void setRating(String rating) {
this.rating = rating;
}
public String getDeliveryTime() {
return deliveryTime;
}
public void setDeliveryTime(String deliveryTime) {
this.deliveryTime = deliveryTime;
}
public String getDeliveryCharges() {
return deliveryCharges;
}
public void setDeliveryCharges(String deliveryCharges) {
this.deliveryCharges = deliveryCharges;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
Recommended:
package com.example.bearcateats.model;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
#Generated("jsonschema2pojo")
public class Recommended {
#SerializedName("name")
#Expose
private String name;
#SerializedName("imageURL")
#Expose
private String imageURL;
#SerializedName("rating")
#Expose
private String rating;
#SerializedName("deliveryTime")
#Expose
private String deliveryTime;
#SerializedName("deliveryCharges")
#Expose
private String deliveryCharges;
#SerializedName("price")
#Expose
private String price;
#SerializedName("note")
#Expose
private String note;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getImageURL() {
return imageURL;
}
public void setImageURL(String imageURL) {
this.imageURL = imageURL;
}
public String getRating() {
return rating;
}
public void setRating(String rating) {
this.rating = rating;
}
public String getDeliveryTime() {
return deliveryTime;
}
public void setDeliveryTime(String deliveryTime) {
this.deliveryTime = deliveryTime;
}
public String getDeliveryCharges() {
return deliveryCharges;
}
public void setDeliveryCharges(String deliveryCharges) {
this.deliveryCharges = deliveryCharges;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
Main:
package com.example.bearcateats;
import android.os.Bundle;
import android.widget.Toast;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.example.bearcateats.adapters.PopularAdapter;
import com.example.bearcateats.adapters.RecommendedAdapter;
import com.example.bearcateats.model.Allmenu;
import com.example.bearcateats.model.FoodData;
import com.example.bearcateats.model.Popular;
import com.example.bearcateats.model.Recommended;
import com.example.bearcateats.retrofit.ApiInterface;
import com.example.bearcateats.retrofit.RetrofitClient;
import com.example.bearcateats.adapters.AllMenuAdapter;
import java.util.List;
public class MainActivity extends AppCompatActivity {
ApiInterface apiInterface;
RecyclerView popularRecyclerView, recommendedRecyclerView, allMenuRecyclerView;
PopularAdapter popularAdapter;
RecommendedAdapter recommendedAdapter;
AllMenuAdapter allMenuAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
apiInterface = RetrofitClient.getRetrofitInstance().create(ApiInterface.class);
Call<List<FoodData>> call = apiInterface.getAllData();
call.enqueue(new Callback<List<FoodData>>() {
#Override
public void onResponse(Call<List<FoodData>> call, Response<List<FoodData>> response) {
System.out.println("TEST!!!!!!!!!!!!!!");
List<FoodData> MenuList;
MenuList = response.body();
if(MenuList == null) {
System.out.println("MenuList!!!!!!!!!!!!!!");
}
List <FoodData> test1 = (List<FoodData>) MenuList.get(0);
List<Popular> popular = ((FoodData) test1).getPopular();
getPopularData(popular);
System.out.println("GET POPULAR RAN!!!!!!!!!!!!!!");
getRecommendedData(MenuList.get(0).getRecommended());
}
#Override
public void onFailure(Call<List<FoodData>> call, Throwable t) {
Toast.makeText(MainActivity.this, "Server is not responding.", Toast.LENGTH_SHORT).show();
}
});
}
private void getPopularData(List<Popular> popularList){
popularRecyclerView = findViewById(R.id.popular_recycler);
popularAdapter = new PopularAdapter(this, popularList);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
popularRecyclerView.setLayoutManager(layoutManager);
popularRecyclerView.setAdapter(popularAdapter);
}
private void getRecommendedData(List<Recommended> recommendedList){
recommendedRecyclerView = findViewById(R.id.recommended_recycler);
recommendedAdapter = new RecommendedAdapter(this, recommendedList);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
recommendedRecyclerView.setLayoutManager(layoutManager);
recommendedRecyclerView.setAdapter(recommendedAdapter);
}
private void getAllMenu(List<Allmenu> allmenuList){
allMenuRecyclerView = findViewById(R.id.all_menu_recycler);
allMenuAdapter = new AllMenuAdapter(this, allmenuList);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
allMenuRecyclerView.setLayoutManager(layoutManager);
allMenuRecyclerView.setAdapter(allMenuAdapter);
allMenuAdapter.notifyDataSetChanged();
}
}
FoodDetails
{
// now we will get data from intent and set to UI
ImageView imageView;
TextView itemName, itemPrice, itemRating;
RatingBar ratingBar;
String name, price, rating, imageUrl;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_food_details);
Intent intent = getIntent();
name = intent.getStringExtra("name");
price = intent.getStringExtra("price");
rating = intent.getStringExtra("rating");
imageUrl = intent.getStringExtra("image");
imageView = findViewById(R.id.imageView5);
itemName = findViewById(R.id.name);
itemPrice = findViewById(R.id.price);
itemRating = findViewById(R.id.rating);
ratingBar = findViewById(R.id.ratingBar);
Glide.with(getApplicationContext()).load(imageUrl).into(imageView);
itemName.setText(name);
itemPrice.setText("$ "+price);
itemRating.setText(rating);
ratingBar.setRating(Float.parseFloat(rating));
}
}
Couple of silly mistakes are there. I have fixed all your bugs. Nothing wrong with your JSON or Response data classes. Correct response is received inside onResponse() callback. Issues are present other parts of your code. Just follow step by step.
Step-1
Replace below code while parsing response data from api call like:
call.enqueue(new Callback<List<FoodData>>() {
#Override
public void onResponse(Call<List<FoodData>> call, Response<List<FoodData>> response) {
if (response.isSuccessful()){
if (response.body() != null){
List<Popular> popular = response.body().get(0).getPopular();
getPopularData(popular);
getRecommendedData(response.body().get(0).getRecommended());
}
}
}
#Override
public void onFailure(Call<List<FoodData>> call, Throwable t) {
Toast.makeText(MainActivity.this, "Server is not responding.", Toast.LENGTH_SHORT).show();
}
});
Step-2
Inside PopularAdapter class replace getItemCount() method as below:
#Override
public int getItemCount() {
//return 0; // Returning 0 will show nothing in RecyclerView.
return popularList.size();
}
Step-3:
Inside RecommendedAdapter class you have used wrong xml for ViewHolder. Replace like below:
#NonNull
#Override
public RecommendedViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
//View view = LayoutInflater.from(context).inflate(R.layout.popular_recycler_items, parent, false);
View view = LayoutInflater.from(context).inflate(R.layout.recommended_recyvler_items, parent, false);
return new RecommendedViewHolder(view);
}
And also replace getItemCount() method like below:
#Override
public int getItemCount() {
return recommendedList.size();
}
UPDATE:-
Why are you calling same api inside onSuccess() of api call ? I have update your onCreate() method like below:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
apiInterface = RetrofitClient.getRetrofitInstance().create(ApiInterface.class);
Call<List<FoodData>> call = apiInterface.getAllData();
call.enqueue(new Callback<List<FoodData>>() {
#Override
public void onResponse(Call<List<FoodData>> call, Response<List<FoodData>> response) {
if (response.isSuccessful()) {
if (response.body() != null) {
getPopularData(response.body().get(0).getPopular());
getRecommendedData(response.body().get(0).getRecommended());
getAllMenu(response.body().get(0).getAllmenu());
}
}
/*
call.clone().enqueue(new Callback<List<FoodData>>() {
#Override
public void onResponse(Call<List<FoodData>> call, Response<List<FoodData>> response) {
if (response.isSuccessful()) {
if (response.body() != null) {
response.code();
getPopularData(response.body().get(0).getPopular());
getRecommendedData(response.body().get(0).getRecommended());
getAllMenu(response.body().get(0).getAllmenu());
}
}
}
#Override
public void onFailure(Call<List<FoodData>> call, Throwable t) {
response.code();
Toast.makeText(MainActivity.this, t.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
}
});*/
}
#Override
public void onFailure(Call<List<FoodData>> call, Throwable t) {
Toast.makeText(MainActivity.this, t.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
}
});
}
I see your JSON in https://francescainfranca.github.io/menuJSON/index.json is an Single FoodData JSON Object and Not an JSONArray.
This is causing the following exception when we run the application. So either Change the JSON structure or Make the application use FoodData directly instead of List<FoodData>.
Also the Toast Message (Server is not responding) is being shown on run which you have added on Failure Response.
Hope this helps you. Thanks.
Related
how are you, I hope you are well
- I am using Room library to store products in RecyclerView.
- When using SearchView to search for a product by its name, the product name appears only when typing the first letter (that is, SearchView only interacts with the first letter of the product name).
Also, when you delete the letter, the complete list of products does not appear again.
Please help me find the error in the code
Warm regards, artist
dependencies
implementation "android.arch.persistence.room:runtime:1.1.1"
annotationProcessor "android.arch.persistence.room:compiler:1.1.1"
implementation "android.arch.lifecycle:extensions:1.1.1"
//noinspection LifecycleAnnotationProcessorWithJava8
annotationProcessor "android.arch.lifecycle:compiler:1.1.1"
ProductEntry
#Entity(tableName = "product")
public class ProductEntry {
#PrimaryKey(autoGenerate = true)
private int id;
#ColumnInfo(name = "productName")
private final String productName;
private final String productBarcode, productQuantity
, productPurchasingPrice, productSellingPrice, productDescription, productCategory;
#Ignore
public ProductEntry(String productName, String productBarcode, String productQuantity
, String productPurchasingPrice, String productSellingPrice, String productDescription, String productCategory) {
this.productName = productName;
this.productBarcode = productBarcode;
this.productQuantity = productQuantity;
this.productPurchasingPrice = productPurchasingPrice;
this.productSellingPrice = productSellingPrice;
this.productDescription = productDescription;
this.productCategory = productCategory;
}
public ProductEntry(int id, String productName, String productBarcode, String productQuantity
, String productPurchasingPrice, String productSellingPrice, String productDescription, String productCategory) {
this.id = id;
this.productName = productName;
this.productBarcode = productBarcode;
this.productQuantity = productQuantity;
this.productPurchasingPrice = productPurchasingPrice;
this.productSellingPrice = productSellingPrice;
this.productDescription = productDescription;
this.productCategory = productCategory;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProductName() {
return productName;
}
public String getProductBarcode() {
return productBarcode;
}
public String getProductQuantity() {
return productQuantity;
}
public String getProductPurchasingPrice() {
return productPurchasingPrice;
}
public String getProductSellingPrice() {
return productSellingPrice;
}
public String getProductDescription() {
return productDescription;
}
public String getProductCategory() {
return productCategory;
}
}
ProductDao
#Dao
public interface ProductDao {
#Query("SELECT * FROM product ORDER BY id")
LiveData<List<ProductEntry>> loadAllTasks();
#Query("SELECT * FROM product WHERE id = :id")
LiveData<ProductEntry> loadTaskById(int id);
#Query("SELECT * FROM product WHERE productName LIKE :findProductName")
LiveData<List<ProductEntry>> findProduct(String findProductName);
#Insert
void insertTask(ProductEntry productEntry);
#Update(onConflict = OnConflictStrategy.REPLACE)
void updateTask(ProductEntry productEntry);
#Delete
void deleteTask(ProductEntry productEntry);
}
FindProductViewModel
public class FindProductViewModel extends ViewModel {
private final LiveData<List<ProductEntry>> findProduct;
public FindProductViewModel(AppDatabase database, String searchQuery) {
findProduct = database.productDao().findProduct(searchQuery);
}
public LiveData<List<ProductEntry>> getFindProduct() {
return findProduct;
}
}
FindProductViewModelFactory
public class FindProductViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private final AppDatabase mDb;
private final String mProductQuery;
public FindProductViewModelFactory(AppDatabase database, String productQuery) {
mDb = database;
mProductQuery = productQuery;
}
#NonNull
#Override
public <T extends ViewModel> T create(#NonNull Class<T> modelClass) {
//noinspection unchecked
return (T) new FindProductViewModel(mDb, mProductQuery);
}
}
ProductsActivity
public class ProductsActivity extends AppCompatActivity implements ProductAdapter.ItemClickListener {
private FloatingActionButton fabAddProduct;
private RecyclerView recyclerView;
private ProductAdapter productAdapter;
private AppDatabase mDb;
private View emptyView;
private SearchView productSearchView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_products);
initView();
setupViewModel();
setupRecycleView();
fabAddProduct.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent addProductIntent = new Intent(ProductsActivity.this, AddProductActivity.class);
startActivity(addProductIntent);
}
});
productSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
getItemFromDb(query);
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
getItemFromDb(newText);
// productAdapter.getFilter().filter(newText);
return false;
}
});
}
private void getItemFromDb(String query) {
String searchText = "%" + query + "%";
FindProductViewModelFactory factory = new FindProductViewModelFactory(mDb, searchText);
final FindProductViewModel viewModel = ViewModelProviders
.of(ProductsActivity.this, (ViewModelProvider.Factory) factory)
.get(FindProductViewModel.class);
viewModel.getFindProduct().observe(this, new Observer<List<ProductEntry>>() {
#Override
public void onChanged(#Nullable List<ProductEntry> productEntries) {
viewModel.getFindProduct().removeObserver(this);
productAdapter.setProductEntries(productEntries);
}
});
}
private void initView() {
emptyView = findViewById(R.id.empty_view);
fabAddProduct = findViewById(R.id.fabAddProducts);
recyclerView = findViewById(R.id.recyclerViewProducts);
productSearchView = findViewById(R.id.productSearchView);
}
private void setupRecycleView() {
mDb = AppDatabase.getInstance(getApplicationContext());
productAdapter = new ProductAdapter(this, this, mDb);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(productAdapter);
}
private void setupViewModel() {
MainViewModel viewModel = ViewModelProviders.of(this).get(MainViewModel.class);
viewModel.getProducts().observe(this, new Observer<List<ProductEntry>>() {
#Override
public void onChanged(#Nullable List<ProductEntry> productEntries) {
productAdapter.setProductEntries(productEntries);
if (productAdapter.getItemCount() == 0) {
recyclerView.setVisibility(View.GONE);
emptyView.setVisibility(View.VISIBLE);
productSearchView.setVisibility(View.GONE);
} else {
recyclerView.setVisibility(View.VISIBLE);
emptyView.setVisibility(View.GONE);
productSearchView.setVisibility(View.VISIBLE);
}
}
});
}
#Override
public void onItemClickListener(int itemId) {
Intent intent = new Intent(ProductsActivity.this, AddProductActivity.class);
intent.putExtra(AddProductActivity.EXTRA_PRODUCT_ID, itemId);
startActivity(intent);
}
}
To give you the simple answer, the reason you are getting only the first update is the line with viewModel.getFindProduct().removeObserver(this);
Thank you very much
I solved the problem by adding the following code
package com.artist.bookkeeper.activities;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SearchView;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import com.artist.bookkeeper.database.entry.ProductEntry;
import com.artist.bookkeeper.viewmodel.MainViewModel;
import com.artist.bookkeeper.adapters.ProductsAdapter;
import com.artist.bookkeeper.R;
import com.artist.bookkeeper.database.AppDatabase;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.ArrayList;
import java.util.List;
public class ProductStoreActivity extends AppCompatActivity implements ProductsAdapter.ItemClickListener {
private FloatingActionButton addProductFab;
private RecyclerView productsRv;
private ProductsAdapter productsAdapter;
private AppDatabase appDatabase;
private View productsEmptyView;
private SearchView productsSv;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_product_store);
initProductView();
setupProductViewModel();
setupProductRecycleView();
addProductFab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startActivity(new Intent(ProductStoreActivity.this, AddProductActivity.class));
}
});
productsSv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
setupProductViewModel();
productsFilter(newText);
return true;
}
});
}
private void productsFilter(String text) {
ArrayList<ProductEntry> productsList = new ArrayList<>();
for (ProductEntry product : productsAdapter.getProductEntries()) {
if (product.getProductName().toLowerCase().contains(text.toLowerCase())) {
productsList.add(product);
}
}
if (productsList.isEmpty()) {
Toast.makeText(this, getString(R.string.no_product), Toast.LENGTH_SHORT).show();
} else {
productsAdapter.setProductEntries(productsList);
}
}
private void initProductView() {
productsEmptyView = findViewById(R.id.productEmptyView);
addProductFab = findViewById(R.id.addProductFab);
productsRv = findViewById(R.id.productRv);
productsSv = findViewById(R.id.productSv);
}
private void setupProductRecycleView() {
appDatabase = AppDatabase.getInstance(getApplicationContext());
productsAdapter = new ProductsAdapter(this, this, appDatabase);
productsRv.setHasFixedSize(true);
productsRv.setLayoutManager(new LinearLayoutManager(this));
productsRv.setAdapter(productsAdapter);
}
private void setupProductViewModel() {
MainViewModel mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
mainViewModel.getListProductsLiveData().observe(this, new Observer<List<ProductEntry>>() {
#SuppressLint("NotifyDataSetChanged")
#Override
public void onChanged(#Nullable List<ProductEntry> productEntries) {
productsAdapter.setProductEntries(productEntries);
productsAdapter.notifyDataSetChanged();
if (productsAdapter.getItemCount() == 0) {
productsRv.setVisibility(View.GONE);
productsEmptyView.setVisibility(View.VISIBLE);
productsSv.setVisibility(View.GONE);
} else {
productsRv.setVisibility(View.VISIBLE);
productsEmptyView.setVisibility(View.GONE);
productsSv.setVisibility(View.VISIBLE);
}
}
});
}
#Override
public void onItemClickListener(int productId, String productName) {
Intent intent = new Intent(ProductStoreActivity.this, ProductDetailsActivity.class);
intent.putExtra("productId", productId);
intent.putExtra("productName", productName);
startActivity(intent);
}
}
after initializing the ViewModel i keep getting an error of null object reference , and i don't know why
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.example.foodapp.models.Recipe;
import com.example.foodapp.respositories.RecipeRepository;
public class RecipeViewModel extends ViewModel {
private String mRecipeId;
private RecipeRepository mRecipeRepository;
public RecipeViewModel() {
mRecipeRepository=RecipeRepository.getInstance();
}
public LiveData<Recipe>getRecipe(){
return mRecipeRepository.getRecipe();
}
public void searchRecipeById(String recipeId)
{ mRecipeId = recipeId;
mRecipeRepository.searchRecipeById(recipeId);
}
public String getViewModelRecipeId() {
return mRecipeId;
}
}
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.example.foodapp.models.Recipe;
import com.example.foodapp.viewmodels.RecipeViewModel;
public class RecipeActivity extends BaseActivity {
private static final String TAG = "RecipeActivity";
private AppCompatImageView mRecipeImage;
private TextView mRecipeTitle ,mRecipeRank;
private LinearLayout mRecipeIngredientsContainer;
private ScrollView mScrollView;
private RecipeViewModel mRecipeViewModel;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recipe);
mRecipeImage=findViewById(R.id.recipe_image);
mRecipeTitle=findViewById(R.id.recipe_title);
mRecipeRank=findViewById(R.id.recipe_social_score);
mRecipeIngredientsContainer=findViewById(R.id.ingredients_container);
mScrollView=findViewById(R.id.parent);
mRecipeViewModel=new ViewModelProvider(this).get(RecipeViewModel.class);
showProgressBar(true);
subscribeObserver();
getIncomingIntent();
}
private void getIncomingIntent(){
if(getIntent().hasExtra("recipe")){
Recipe recipe=getIntent().getParcelableExtra("recipe");
Log.d(TAG, "getIncomingIntent: "+recipe.getTitle());
mRecipeViewModel.searchRecipeById(recipe.getRecipeId());
}
}
private void subscribeObserver(){
mRecipeViewModel.getRecipe().observe(this, new Observer<Recipe>() {
#Override
public void onChanged(Recipe recipe) {
if(recipe!=null){
if(recipe.getRecipeId().equals(mRecipeViewModel.getViewModelRecipeId())){
setRecipeProperties(recipe);
}
}
}
});
}
private void setRecipeProperties(Recipe recipe){
if(recipe!=null){
RequestOptions requestOptions= new RequestOptions()
.placeholder(R.drawable.ic_launcher_background);
Glide.with(this).setDefaultRequestOptions(requestOptions)
.load(recipe.getImageUrl()).into(mRecipeImage);
mRecipeTitle.setText(recipe.getTitle());
mRecipeRank.setText(String.valueOf(recipe.getSocialUrl()));
mRecipeIngredientsContainer.removeAllViews();
for(String ingredients: recipe.getIngredients()){
TextView textView= new TextView(this);
textView.setText(ingredients);
textView.setTextSize(15);
textView.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
));
mRecipeIngredientsContainer.addView(textView);
}
}
showParent();
showProgressBar(false);
}
private void showParent(){
mScrollView.setVisibility(View.VISIBLE);
}
}
and this is the exception am getting:
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null object reference
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null object reference
at com.example.foodapp.RecipeActivity$1.onChanged(RecipeActivity.java:63)
at com.example.foodapp.RecipeActivity$1.onChanged(RecipeActivity.java:57)
at androidx.lifecycle.LiveData.considerNotify(LiveData.java:133)
at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:151)
at androidx.lifecycle.LiveData.setValue(LiveData.java:309)
at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)..etc
the model class :
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Arrays;
public class Recipe implements {
private String title;
private String publisher;
private String imageUrl;
private String id;
private String [] ingredients;
private float socialUrl;
public Recipe() {
}
public Recipe(String title, String publisher, String image_url, String id,
String[] ingredients, float socialUrl) {
this.title = title;
this.publisher = publisher;
this.imageUrl = image_url;
this.id = id;
this.ingredients = ingredients;
this.socialUrl = socialUrl;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getPublisher() {
return publisher;
}
public void setPublisher(String publisher) {
this.publisher = publisher;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public String getRecipeId() {
return id;
}
public void setRecipeId(String id) {
this.id = id;
}
public String[] getIngredients() {
return ingredients;
}
public void setIngredients(String[] ingredients) {
this.ingredients = ingredients;
}
public float getSocialUrl() {
return socialUrl;
}
public void setSocialUrl(float socialUrl) {
this.socialUrl = socialUrl;
}
#Override
public String toString() {
return "Recipe{" +
"title='" + title + '\'' +
", publisher='" + publisher + '\'' +
", image_url='" + imageUrl + '\'' +
", recipe_id='" + id + '\'' +
", ingredients=" + Arrays.toString(ingredients) +
", social_rank=" + socialUrl +
'}';
}}
I've tried cleaning the project and invalidate and cache and even using the deprecated ViewModelProviders class ,
but all the same and nothing has changed
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recipe);
mRecipeImage=findViewById(R.id.recipe_image);
mRecipeTitle=findViewById(R.id.recipe_title);
mRecipeRank=findViewById(R.id.recipe_social_score);
mRecipeIngredientsContainer=findViewById(R.id.ingredients_container);
mScrollView=findViewById(R.id.parent);
mRecipeViewModel=new ViewModelProvider(this).get(RecipeViewModel.class);
showProgressBar(true);
getIncomingIntent(); //this method needs to be called first
subscribeObserver(); // then this method.
}
I try to get data from my Api, I can get data and convert it into an object class I created and show it but I can't store all objects instances in an list. Someone knows ?
I tried to add every element I got in a List then try to get elements from it after but doesn't work, the list stays empty.
This is what my API returns :
{"_id":"60683c5bcdfcb74689bc8382","questionId":3,"question":"Question 3","choices":"1-/-2-/-3-/-4","category":"Other","difficulty":"Easy"}
This is my "ApiTest activity" :
package com.jojo.jojozquizz;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;
import com.jojo.jojozquizz.model.Question;
import com.jojo.jojozquizz.model.QuestionBank;
import com.jojo.jojozquizz.tools.APIListener;
import org.json.JSONException;
import org.json.JSONObject;
public class ApiTests extends AppCompatActivity implements APIListener {
private TextView text;
public static final String TAG = "MyTag";
private QuestionBank mQuestionBank;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_api_tests);
mQuestionBank = new QuestionBank();
text = findViewById(R.id.api_text);
String url = "https://nextfor.studio/questions/test";
getQuestionFromApi(url);
}
private void getQuestionFromApi(String url) {
APIListener listener = this;
RequestQueue requestQueue = Volley.newRequestQueue(this);
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
Log.d(TAG, "onResponse: " + response.toString());
Question question = new Question();
question.setId(response.getInt("questionId"));
question.setQuestion(response.getString("question"));
question.setChoices(response.getString("choices"));
question.setCategory(response.getString("category"));
question.setDifficulty(response.getString("difficulty"));
listener.questionReceived(question);
} catch (JSONException e) {
Log.i(TAG, e.getMessage());
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "onErrorResponse: " + error.getMessage());
}
});
requestQueue.add(jsonObjectRequest);
}
#Override
public void questionReceived(Question q) {
mQuestionBank.addQuestion(q);
}
}
Question class :
package com.jojo.jojozquizz.model;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;
import java.io.Serializable;
import java.util.List;
#Entity(tableName = "questions")
public class Question implements Serializable {
#PrimaryKey()
#ColumnInfo(name = "id")
private long id;
#ColumnInfo(name = "question")
private String mQuestion;
#Ignore
private List<String> mChoiceList;
#ColumnInfo(name = "choices")
private String mChoices;
#ColumnInfo(name = "answer_index")
private int mAnswerIndex;
#ColumnInfo(name = "categorie")
private String mCategory;
#Ignore
private String mTrueAnswer;
#ColumnInfo(name = "difficulty")
private String mDifficulty;
public Question() {
}
public Question(int id, String mQuestion, List<String> mChoiceList, String mCategorie, String mDifficulty) {
this.id = id;
this.mQuestion = mQuestion;
this.mChoices = mChoiceList.get(0) + "-/-" + mChoiceList.get(1) + "-/-" + mChoiceList.get(2) + "-/-" + mChoiceList.get(3);
this.mAnswerIndex = 0;
this.mCategory = mCategorie;
this.mDifficulty = mDifficulty;
}
public Question(String mQuestion, List<String> mChoiceList, String mCategorie, String mDifficulty) {
this.mQuestion = mQuestion;
this.mChoices = mChoiceList.get(0) + "-/-" + mChoiceList.get(1) + "-/-" + mChoiceList.get(2) + "-/-" + mChoiceList.get(3);
this.mAnswerIndex = 0;
this.mCategory = mCategorie;
this.mDifficulty = mDifficulty;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getQuestion() {
return mQuestion;
}
public void setQuestion(String question) {
mQuestion = question;
}
public List<String> getChoiceList() {
return mChoiceList;
}
public void setChoiceList(List<String> choiceList) {
mChoiceList = choiceList;
}
public String getChoices() {
return mChoices;
}
public void setChoices(String choices) {
mChoices = choices;
}
public void setAnswerIndex(int mAnswerIndex) {
this.mAnswerIndex = mAnswerIndex;
}
public int getAnswerIndex() {
return mAnswerIndex;
}
public String getCategory() {
return mCategory;
}
public void setCategory(String categorie) {
mCategory = categorie;
}
public String getTrueAnswer() {
return mTrueAnswer;
}
public void setTrueAnswer(String trueAnswer) {
mTrueAnswer = trueAnswer;
}
public String getDifficulty() {
return mDifficulty;
}
public void setDifficulty(String difficulty) {
mDifficulty = difficulty;
}
}
QuestionBank (this is like a list of Questions) :
package com.jojo.jojozquizz.model;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public class QuestionBank {
private List<Question> mQuestionList;
public int mNextQuestionIndex;
public QuestionBank() {
mQuestionList = new LinkedList<>();
mNextQuestionIndex = 0;
}
public QuestionBank(List<Question> questionList, boolean shuffle) {
mQuestionList = questionList;
if (shuffle)
Collections.shuffle(mQuestionList);
mNextQuestionIndex = 0;
}
public Question getQuestion() {
if (mNextQuestionIndex == mQuestionList.size())
mNextQuestionIndex = 0;
return mQuestionList.get(mNextQuestionIndex++);
}
public Question getQuestion(int i) {
return mQuestionList.get(i);
}
public void reShuffle() {
Collections.shuffle(mQuestionList);
}
public void addQuestion(Question question) {
mQuestionList.add(question);
}
public int returnListSize() {
return mQuestionList.size();
}
}
And APIListener :
package com.jojo.jojozquizz.tools;
import com.jojo.jojozquizz.model.Question;
public interface APIListener {
void questionReceived(Question q);
}
API calling is an asynchronous process. So getQuestionFromApi(url); might took some time, so if you try text.setText(mQuestionBank.getQuestion(0).getQuestion()); after getQuestionFromApi(url); line, it won't work. You could use LiveData to handle this case. Like:
class QuestionBank {
MutableLiveData<List<Question>> mQuestionList;
public int mNextQuestionIndex;
public QuestionBank() {
mQuestionList = new MutableLiveData<>(new LinkedList<>());
mNextQuestionIndex = 0;
}
public QuestionBank(List<Question> questionList, boolean shuffle) {
mQuestionList.setValue(questionList);
if (shuffle)
Collections.shuffle(mQuestionList.getValue());
mNextQuestionIndex = 0;
}
public Question getQuestion() {
if (mNextQuestionIndex == mQuestionList.getValue().size())
mNextQuestionIndex = 0;
return mQuestionList.getValue().get(mNextQuestionIndex++);
}
public Question getQuestion(int i) {
return mQuestionList.getValue().get(i);
}
public void reShuffle() {
Collections.shuffle(mQuestionList.getValue());
}
public void addQuestion(Question question) {
List<Question> tempQ = mQuestionList.getValue();
tempQ.add(question);
mQuestionList.setValue(tempQ);
}
public int returnListSize() {
return mQuestionList.getValue().size();
}
}
And inside onCreate() observe if any data has been added or not and then setText() to TextView. Like:
QuestionBank questionBank = new QuestionBank();
questionBank.mQuestionList.observe(getViewLifecycleOwner(), new Observer<List<Question>>() {
#Override
public void onChanged(List<Question> questions) {
if (!questions.isEmpty()){
textView.setText(questions.get(0).getQuestion());
Log.d("TAG==>>","New Question added Total size = "+questions.size());
}
}
});
I have a listview of products displayed on my android app. I am using a web service that returns a JSON. Earlier I had only a flat list of products to display. But now for each list, I have to add a spinner with the variants values from JSON. How do I achieve this?
My present code gives me the below error
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 168 path $[0].variants
Below is my code :
JSON response
{
"responce": "true",
"top_selling_product": [
{
"product_id": "10",
"product_name": "Palm Ruchi (Pack of 10)",
"product_name_arb": "",
"product_description_arb": "",
"category_id": "8",
"product_description": "",
"variants": [
{
"variant_id": "7",
"variant_name": "2L",
"in_stock": "0",
"price": "120",
"mrp": "130",
"unit_value": "25"
},
{
"variant_id": "8",
"variant_name": "5L",
"in_stock": "1",
"price": "240",
"mrp": "255",
"unit_value": "25"
},
{
"variant_id": "9",
"variant_name": "10L",
"in_stock": "1",
"price": "0",
"mrp": "0",
"unit_value": "0"
}
],
"product_image": "product-placeholder2.jpg",
"status": "",
"unit": "Box",
"increment": "0",
"rewards": "0",
"stock": "",
"title": "Oil"
},
{
"product_id": "11",
"product_name": "Sunflower Sunrich 1 ltr Box(pack of 10)",
"product_name_arb": "",
"product_description_arb": "",
"category_id": "8",
"product_description": "",
"variants": [],
"product_image": "product-placeholder3.jpg",
"status": "",
"unit": "Box",
"increment": "0",
"rewards": "0",
"stock": "",
"title": "Oil"
},
{
"product_id": "26",
"product_name": "TestName4",
"product_name_arb": "",
"product_description_arb": "",
"category_id": "8",
"product_description": "TestAddress4",
"variants": [],
"product_image": "img8",
"status": "",
"unit": "kg",
"increment": "1",
"rewards": "20",
"stock": "",
"title": "Oil"
}
]
}
Top_Selling_model.java
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class Top_Selling_model implements Serializable {
#SerializedName("product_id")
#Expose
private String productId;
#SerializedName("product_name")
#Expose
private String productName;
#SerializedName("product_name_arb")
#Expose
private String productNameArb;
#SerializedName("product_description_arb")
#Expose
private String productDescriptionArb;
#SerializedName("category_id")
#Expose
private String categoryId;
#SerializedName("product_description")
#Expose
private String productDescription;
#SerializedName("variants")
#Expose
private List<VariantsModel> variants = null;
#SerializedName("product_image")
#Expose
private String productImage;
#SerializedName("status")
#Expose
private String status;
#SerializedName("unit")
#Expose
private String unit;
#SerializedName("increment")
#Expose
private String increment;
#SerializedName("rewards")
#Expose
private String rewards;
#SerializedName("stock")
#Expose
private String stock;
#SerializedName("title")
#Expose
private String title;
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getProductNameArb() {
return productNameArb;
}
public void setProductNameArb(String productNameArb) {
this.productNameArb = productNameArb;
}
public String getProductDescriptionArb() {
return productDescriptionArb;
}
public void setProductDescriptionArb(String productDescriptionArb) {
this.productDescriptionArb = productDescriptionArb;
}
public String getCategoryId() {
return categoryId;
}
public void setCategoryId(String categoryId) {
this.categoryId = categoryId;
}
public String getProductDescription() {
return productDescription;
}
public void setProductDescription(String productDescription) {
this.productDescription = productDescription;
}
public List<VariantsModel> getVariants() {
return variants;
}
public void setVariants(List<VariantsModel> variants) {
this.variants = variants;
}
public String getProductImage() {
return productImage;
}
public void setProductImage(String productImage) {
this.productImage = productImage;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
public String getIncrement() {
return increment;
}
public void setIncrement(String increment) {
this.increment = increment;
}
public String getRewards() {
return rewards;
}
public void setRewards(String rewards) {
this.rewards = rewards;
}
public String getStock() {
return stock;
}
public void setStock(String stock) {
this.stock = stock;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
Variants_model.java
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
public class VariantsModel implements Serializable {
#SerializedName("variant_id")
#Expose
private String variantId;
#SerializedName("variant_name")
#Expose
private String variantName;
#SerializedName("in_stock")
#Expose
private String inStock;
#SerializedName("price")
#Expose
private String price;
#SerializedName("mrp")
#Expose
private String mrp;
#SerializedName("unit_value")
#Expose
private String unitValue;
public String getVariantId() {
return variantId;
}
public void setVariantId(String variantId) {
this.variantId = variantId;
}
public String getVariantName() {
return variantName;
}
public void setVariantName(String variantName) {
this.variantName = variantName;
}
public String getInStock() {
return inStock;
}
public void setInStock(String inStock) {
this.inStock = inStock;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public String getMrp() {
return mrp;
}
public void setMrp(String mrp) {
this.mrp = mrp;
}
public String getUnitValue() {
return unitValue;
}
public void setUnitValue(String unitValue) {
this.unitValue = unitValue;
}
}
Top_selling_Adapter.java
import android.content.Context;
import android.content.SharedPreferences;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import java.util.ArrayList;
import java.util.List;
import Config.BaseURL;
import Model.Top_Selling_model;
import gogrocer.tcc.R;
import static android.content.Context.MODE_PRIVATE;
public class Top_Selling_Adapter extends RecyclerView.Adapter<Top_Selling_Adapter.MyViewHolder> {
private List<Top_Selling_model> modelList;
ArrayList<String> variantsData = new ArrayList<>();
private Context context;
SharedPreferences preferences;
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView product_nmae, product_prize;
public ImageView image;
public Spinner spinner;
public MyViewHolder(View view) {
super(view);
product_nmae = (TextView) view.findViewById(R.id.product_name);
product_prize = (TextView) view.findViewById(R.id.product_prize);
image = (ImageView) view.findViewById(R.id.iv_icon);
spinner=(Spinner) view.findViewById(R.id.variants);
}
}
public Top_Selling_Adapter(List<Top_Selling_model> modelList) {
this.modelList = modelList;
}
#Override
public Top_Selling_Adapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.row_top_selling, parent, false);
context = parent.getContext();
return new Top_Selling_Adapter.MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(Top_Selling_Adapter.MyViewHolder holder, int position) {
Top_Selling_model mList = modelList.get(position);
preferences = context.getSharedPreferences("lan", MODE_PRIVATE);
String language=preferences.getString("language","");
Glide.with(context)
.load(BaseURL.IMG_PRODUCT_URL + mList.getProduct_image())
.placeholder(R.drawable.icon)
.crossFade()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.dontAnimate()
.into(holder.image);
// variantsData.add(mList.getVariants().getVariantName());
Log.e("Top_selling_array",mList.getVariants().getVariantName());
holder.product_prize.setText(context.getResources().getString(R.string.currency) + mList.getVariants().getPrice());
// Log.e("Top_selling_array",mList.getVariants().toString());
if (language.contains("english")) {
holder.product_nmae.setText(mList.getProduct_name());
}
else {
holder.product_nmae.setText(mList.getProduct_name_arb());
}
}
#Override
public int getItemCount() {
return modelList.size();
}
}
Home_fragment.java
private void make_top_selling() {
String tag_json_obj = "json_category_req";
ArrayList<VariantsModel> variantsList = new ArrayList<VariantsModel>();
isSubcat = false;
Map<String, String> params = new HashMap<String, String>();
params.put("parent", "");
isSubcat = true;
/* if (parent_id != null && parent_id != "") {
}*/
CustomVolleyJsonRequest jsonObjReq = new CustomVolleyJsonRequest(Request.Method.POST,
BaseURL.GET_TOP_SELLING_PRODUCTS, params, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.d(TAG, response.toString());
try {
if (response != null && response.length() > 0) {
Boolean status = response.getBoolean("responce");
if (status) {
Gson gson = new Gson();
Type listType = new TypeToken<List<Top_Selling_model>>() {
}.getType();
Type listType2 = new TypeToken<List<VariantsModel>>() {
}.getType();
top_selling_models = gson.fromJson(response.getString("top_selling_product"), listType);
top_selling_adapter = new Top_Selling_Adapter(top_selling_models);
rv_top_selling.setAdapter(top_selling_adapter);
top_selling_adapter.notifyDataSetChanged();
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d(TAG, "Error: " + error.getMessage());
if (error instanceof TimeoutError || error instanceof NoConnectionError) {
Toast.makeText(getActivity(), getResources().getString(R.string.connection_time_out), Toast.LENGTH_SHORT).show();
}
}
});
jsonObjReq.setRetryPolicy(new DefaultRetryPolicy(
retry,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
// Adding request to request queue
// Adding request to request queue
AppController.getInstance().addToRequestQueue(jsonObjReq, tag_json_obj);
}
CustomVolleyJsonRequest.java
public class CustomVolleyJsonRequest extends Request<JSONObject> {
private Response.Listener<JSONObject> listener;
private Map<String, String> params;
public CustomVolleyJsonRequest(String url, Map<String, String> params,
Response.Listener<JSONObject> reponseListener, Response.ErrorListener errorListener) {
super(Method.GET, url, errorListener);
this.listener = reponseListener;
this.params = params;
}
public CustomVolleyJsonRequest(int method, String url, Map<String, String> params,
Response.Listener<JSONObject> reponseListener, Response.ErrorListener errorListener) {
super(method, url, errorListener);
this.listener = reponseListener;
this.params = params;
}
protected Map<String, String> getParams()
throws com.android.volley.AuthFailureError {
return params;
};
#Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers));
return Response.success(new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
#Override
protected void deliverResponse(JSONObject response) {
// TODO Auto-generated method stub
listener.onResponse(response);
}
}
EDIT : Changed my POJO classes.
I think there is a problem with your models according to this JsonResponce.
Paste your json response here https://jsoneditoronline.org and see your formatted json and paste your json http://www.jsonschema2pojo.org here for creating your models. In http://www.jsonschema2pojo.org you have to choose Target language Java,
Source type Json, Annotation Style Gson and preview. Get your models from there and try again.
for (mList:mList){
for (variant : Variants){
variantList.add (variant)
}
}
Firstly create a String list.
List<String> variantNameList = new ArrayList<String>();
for (mList:mList){
for (variant : Variants){
variantNameList.add (variant. variant_name)
}
}
This is your list which icludes all variant names. Then you need an adapter
// Creating adapter for spinner
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, variantNameList);
// Drop down layout style - list view with radio
buttondataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
// attaching data adapter to spinner
spinner.setAdapter(dataAdapter);
Thats all
I have an issue where my recyclerview adds new items at the end, whereas I want the latest items at the top upon user refresh. I've been scratching around all over the net and it essentially boils down to adding setreverseLayout or setStackFromEnd. This gives other complications such as the recycler view not scrolling to the top to the latest item.
I then had a thought of maybe ordering my data list by a specific value and then it should return it as I want it. Can this be done and would it resolve my issue? I want to sort it by value adopt_rownum desc.
My Custom Adapter
package com.example.admin.paws;
import android.content.Context;
import android.content.Intent;
import android.graphics.Typeface;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import java.util.List;
/**
* Created by admin on 9/16/2016.
*/
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
public Context context;
public List<MyData> my_data;
public CustomAdapter(Context context, List<MyData> my_data) {
this.context = context;
this.my_data = my_data;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.cardnew,parent,false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
String name = my_data.get(position).getName();
String age = my_data.get(position).getAge();
String gender = my_data.get(position).getGender();
String SubstAge = age.substring(0,age.indexOf("(") -1);
String NameAgeGender = name + ", " + SubstAge + ", " + gender;
holder.about.setText(my_data.get(position).getAbout());
holder.NameAgeGender.setText(NameAgeGender);
Glide.with(context).load(my_data.get(position).getPhoto_path()).into(holder.photo_path);
//activity_card_details vars
// final String about = my_data.get(position).getAbout();
final String adoptId = my_data.get(position).getId()+"";
final String photo_path_dtls = my_data.get(position).getPhoto_path();
final String listedDate = my_data.get(position).getDatetime_listed();
final String status = my_data.get(position).getStatus();
final String breed = my_data.get(position).getBreed();
final String source = my_data.get(position).getSource();
final String contact_info = my_data.get(position).getContact_info();
final String suburb = my_data.get(position).getSuburb();
final String city = my_data.get(position).getCity();
final String province = my_data.get(position).getProvince();
final String concat_location = suburb + ", " + city + ", " + province;
final String viewCounter = my_data.get(position).getViewCounter()+"";
//When click on photo
holder.photo_path.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(context,CardDetailsActivity.class);
intent.putExtra("listedDate",listedDate);
intent.putExtra("adoptId",adoptId);
// intent.putExtra("about",about);
intent.putExtra("status",status);
intent.putExtra("breed",breed);
intent.putExtra("source",source);
intent.putExtra("contactinfo",contact_info);
intent.putExtra("location",concat_location);
intent.putExtra("photo_path_dtls",photo_path_dtls);
intent.putExtra("viewCounter",viewCounter);
context.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return my_data.size();
}
public class ViewHolder extends RecyclerView.ViewHolder
{
public TextView NameAgeGender;
public ImageView photo_path;
public TextView about;
private ViewHolder(View itemView) {
super(itemView);
about = (TextView) itemView.findViewById(R.id.about);
NameAgeGender = (TextView) itemView.findViewById(R.id.tvNameAgeGender);
// NameAgeGender.setTextColor(Color.parseColor("#9C9393"));
Typeface typeface = Typeface.createFromAsset(context.getAssets(), "fonts/Lato-Medium.ttf");
NameAgeGender.setTypeface(typeface);
//NameAgeGender.setBackgroundColor(Color.parseColor("#F35959"));
photo_path = (ImageView) itemView.findViewById(R.id.photo_path);
}
}
}
My DataList
package com.example.admin.paws;
public class MyData {
//the must be in the same order of the select column order
public int adopt_rownum
,viewCounter
,adopt_id;
private String
name
,type
,breed
,age
,gender
,size
,about
,photo_path
,source
,contact_info
,suburb
,city
,province
,datetime_listed
,status;
public MyData(
int adopt_rownum,
int viewCounter,
int adopt_id,
String name,
String type,
String breed,
String age,
String gender,
String size,
String about,
String photo_path,
String source,
String contact_info,
String suburb,
String city,
String province,
String datetime_listed,
String status)
{
this.adopt_rownum = adopt_rownum;
this.viewCounter = viewCounter;
this.adopt_id = adopt_id;
this.name = name;
this.type = type;
this.breed = breed;
this.age = age;
this.gender = gender;
this.size = size;
this.about = about;
this.photo_path = photo_path;
this.source = source;
this.contact_info = contact_info;
this.suburb = suburb;
this.city = city;
this.province = province;
this.datetime_listed = datetime_listed;
this.status = status;
}
//adopt_rownum used for filtering the records.
public int getAdopt_rownum() {
return adopt_rownum;
}
//viewcounter
public int getViewCounter() {
return viewCounter;
}
//adopt_id
public int getId() {
return adopt_id;
}
public void setId(int adopt_id) {
this.adopt_id = adopt_id;
}
//name
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//type
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
//breed
public String getBreed() {
return breed;
}
public void setBreed(String breed) {
this.breed = breed;
}
//age
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
//gender
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.age = gender;
}
//size
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
//about
public String getAbout() {
return about;
}
public void setAbout(String about) {
this.about = about;
}
//photo path
public String getPhoto_path() {
return photo_path;
}
public void setPhoto_path(String photo_path) {
this.photo_path = photo_path;
}
//source
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
//contact_info
public String getContact_info() {
return contact_info;
}
public void setContact_info(String contact_info) {
this.contact_info = contact_info;
}
//suburb
public String getSuburb() {
return suburb;
}
public void setSuburb(String suburb) {
this.contact_info = suburb;
}
//city
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
//province
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
//datetime_listed
public String getDatetime_listed() {
return datetime_listed;
}
public void setDatetime_listed(String datetime_listed) {
this.datetime_listed = datetime_listed;
}
//status
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
The fragment that displays the results. Another question on the side, inside this fragment for load_data_from_server its giving me a warning that "This Async task should be static or leaks might occur". I have no idea what this means since I'm completely new to JAVA.
package com.example.admin.paws;
import android.app.Activity;
import android.content.Context;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
/**
* A simple {#link Fragment} subclass.
* Activities that contain this fragment must implement the
* {#link feedFragment.OnFragmentInteractionListener} interface
* to handle interaction events.
* Use the {#link feedFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class feedFragment extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
public Activity FragActivity;
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
private List<MyData> data_list;
private CustomAdapter adapter;
RecyclerView recyclerView;
public String EndOfFeed;
private OnFragmentInteractionListener mListener;
public feedFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* #param param1 Parameter 1.
* #param param2 Parameter 2.
* #return A new instance of fragment fragment_feed.
*/
// TODO: Rename and change types and number of parameters
public static feedFragment newInstance(String param1, String param2) {
feedFragment fragment = new feedFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
setHasOptionsMenu(true);
View rootView = inflater.inflate(R.layout.fragment_feed, container, false);
final GridLayoutManager gridLayoutManager;
final SwipeRefreshLayout swipeRefreshLayout = rootView.findViewById(R.id.feedRefresh);
TextView tvEndOfFeed = rootView.findViewById(R.id.tvEndOfFeed);
tvEndOfFeed.setText(EndOfFeed);
//recycler view
recyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
data_list = new ArrayList<>();
load_data_from_server(0, "getFeed.php");
gridLayoutManager = new GridLayoutManager(getActivity(), 1); //2 nr of cards next to each other
recyclerView.setLayoutManager(gridLayoutManager);
//gridLayoutManager.setReverseLayout(true);
adapter = new CustomAdapter(getActivity(), data_list);
recyclerView.setAdapter(adapter);
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (gridLayoutManager.findLastCompletelyVisibleItemPosition() == data_list.size() - 1) {
load_data_from_server(data_list.get(data_list.size() - 1).getAdopt_rownum(), "getFeed.php");
}
}
});
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
//this works but issue with the ordering of the adoptrownum
load_data_from_server(data_list.get(data_list.size() -1).getAdopt_rownum(), "refreshFeed.php");
swipeRefreshLayout.setRefreshing(false);
}
});
return rootView;
}
private void load_data_from_server(final int adopt_id, final String phpScript) {
AsyncTask<Integer,Void,Void> task = new AsyncTask<Integer, Void, Void>() {
#Override
protected Void doInBackground(Integer... integers) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://10.0.2.2/app_scripts/"+phpScript+"?adopt_rownum="+integers[0])
.build();
try {
Response response = client.newCall(request).execute();
JSONArray array = new JSONArray(response.body().string());
for (int i=0; i<array.length(); i++){
JSONObject object = array.getJSONObject(i);
MyData data = new MyData(
object.getInt("ADOPT_ROWNUM"),
object.getInt("VIEWCOUNTER"),
object.getInt("ADOPT_ID"),
object.getString("NAME"),
object.getString("TYPE"),
object.getString("BREED"),
object.getString("AGE"),
object.getString("GENDER"),
object.getString("SIZE"),
object.getString("ABOUT"),
object.getString("PHOTO_PATH"),
object.getString("SOURCE"),
object.getString("CONTACT_INFO"),
object.getString("SUBURB"),
object.getString("CITY"),
object.getString("PROVINCE"),
object.getString("DATETIME_LISTED"),
object.getString("STATUS")
);
data_list.add(data);
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
System.out.println("End of content"+e);
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
adapter.notifyDataSetChanged();
}
};
task.execute(adopt_id);
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
}
I added the Comparator as instructed below but the output is a bit weird...
public class MyComparator implements Comparator<MyData > {
#Override
public int compare(final MyData o1, final MyData o2) {
Log.d("APP", "compare Starting... ");
Integer val1 = o1.getAdopt_rownum();
Log.d("APP", "compare val1... "+val1);
Integer val2 = o2.getAdopt_rownum();
Log.d("APP", "compare val1... "+val2);
Log.d("APP", "compare val1 and val2 ="+val1.compareTo(val2));
return val1.compareTo(val2);
}
}
output
compare Starting...
compare val1... 2
compare val2... 1
compare val1 and val2 =1
...
compare Starting...
compare val1... 14
compare val2... 15
compare val2 and val2 =1
Below is how i implemented it in my fragment
private void load_data_from_server(final int adopt_id, final String phpScript) {
AsyncTask<Integer,Void,Void> task = new AsyncTask<Integer, Void, Void>() {
#Override
protected Void doInBackground(Integer... integers) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://10.0.2.2/app_scripts/"+phpScript+"?adopt_rownum="+integers[0])
.build();
try {
Response response = client.newCall(request).execute();
JSONArray array = new JSONArray(response.body().string());
for (int i=0; i<array.length(); i++){
JSONObject object = array.getJSONObject(i);
MyData data = new MyData(
object.getInt("ADOPT_ROWNUM"),
object.getInt("VIEWCOUNTER"),
object.getInt("ADOPT_ID"),
object.getString("NAME"),
object.getString("TYPE"),
object.getString("BREED"),
object.getString("AGE"),
object.getString("GENDER"),
object.getString("SIZE"),
object.getString("ABOUT"),
object.getString("PHOTO_PATH"),
object.getString("SOURCE"),
object.getString("CONTACT_INFO"),
object.getString("SUBURB"),
object.getString("CITY"),
object.getString("PROVINCE"),
object.getString("DATETIME_LISTED"),
object.getString("STATUS")
);
data_list.add(data);
**Collections.sort(data_list,new MyComparator());**
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
//System.out.println("End of content"+e);
EndOfFeed = e+"";
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
adapter.notifyDataSetChanged();
}
};
task.execute(adopt_id);
}
You can sort list by a property using the following code:
Collections.sort(myList, new MyComparator());
public static class MyComparator implements Comparator<MyData > {
#Override
public int compare(final MyData o1, final MyData o2) {
return o1.getAdoptRownum().compareTo(o2.getAdoptRownum());
}
}
I usually declare comparators inside the relevant POJO class. Then you can call Collections.sort whenever you refresh your data.