Pass multiple parameters to RecyclerView - android

In my current code, I'm able to pass and display one value to the RecyclerView. I'm not sure what I'm missing or if I have to completely change my code.
Here's my Adapter Class:
public class TechSkillsAdapter extends RecyclerView.Adapter<TrainingViewHolder> {
Context c;
ArrayList<String> trainings;
public TechSkillsAdapter(Context c, ArrayList<String> trainings) {
this.c = c;
this.trainings = trainings;
}
#Override
public TrainingViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(c).inflate(R.layout.cv_traininglist, parent, false);
return new TrainingViewHolder(v);
}
#Override
public void onBindViewHolder(TrainingViewHolder holder, int position) {
holder.date_txt.setText(trainings.get(position));
}
#Override
public int getItemCount() {
return trainings.size();
}
}
I'm retrieving the list using retrofit then passing the values to a method that displays them to the recyclerview.
Here's how I'm retrieving the trainings:
private void getTraining(final String id, String tid){
com.trendmicro.projectlara.apis.ApiServiceTraining apiService = ApiClient.getClient().create(ApiServiceTraining.class);
Call<List<Training>> call = apiService.getMyTraining(id, tid);
call.enqueue(new Callback<List<Training>>() {
#Override
public void onResponse (Call<List<Training>> call, Response<List<Training>> response) {
List<Training> training = response.body();
for(Training t: training){
fillTrainings(t.getTraining_title().toString(), t.getTraining_date().toString());
}
}
Here's my fillTrainings method.
I'm passing two values but I'm only able to display one value:
private void fillTrainings(String title, String date) {
training.add(title);
adapter = new TechSkillsAdapter(getContext(), training);
rv.setAdapter(adapter);
progressDialog.dismiss();
}
Here's what I'm trying to fix,I'm not sure how to display the date as well since the way I'm adding the values is: training.add(title);
Any help or tip is much appreciated. Thanks!

This is because you're recreate the adapter each time you have an item with this code:
#Override
public void onResponse (Call<List<Training>> call, Response<List<Training>> response) {
List<Training> training = response.body();
for(Training t: training){
// here you're only send one time
fillTrainings(t.getTraining_title().toString(), t.getTraining_date().toString());
}
}
You can fix it by sending all the list to your method by changing the method to this:
private void fillTrainings(List<Training> trainList) {
for(Training train: trainList) {
training.add(train.getTraining_title().toString());
}
adapter = new TechSkillsAdapter(getContext(), training);
rv.setAdapter(adapter);
progressDialog.dismiss();
}
then you can use it with:
#Override
public void onResponse (Call<List<Training>> call, Response<List<Training>> response) {
List<Training> training = response.body();
fillTrainings(training);
}

You are receiving a List of Training, but for some reason your adapter is backed by List of String. Why is that?
Modify your adapter to hold Trainings and then bind both the title and the date to the view.
public class TechSkillsAdapter extends RecyclerView.Adapter<TrainingViewHolder> {
Context c;
ArrayList<Training> trainings;
public TechSkillsAdapter(Context c, ArrayList<Training> trainings) {
this.c = c;
this.trainings = trainings;
}
#Override
public TrainingViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(c).inflate(R.layout.cv_traininglist, parent, false);
return new TrainingViewHolder(v);
}
#Override
public void onBindViewHolder(TrainingViewHolder holder, int position) {
holder.title_txt.setText(trainings.get(position).getTraining_title().toString());
holder.date_txt.setText(trainings.get(position).getTraining_date().toString());
}
#Override
public int getItemCount() {
return trainings.size();
}
}
And then in your onResponse just do:
#Override
public void onResponse (Call<List<Training>> call, Response<List<Training>> response) {
List<Training> training = response.body();
adapter = new TechSkillsAdapter(getContext(), training);
rv.setAdapter(adapter);
progressDialog.dismiss();
}
This should work, but you can improve this if you create and set your adapter once (in onCreate) and pass it some list, and then in onResponse just update that list and do adapter.notifyDataSetChanged() Sort of like this:
//Activity
List<Training> mTrainingsList = new ArrayList<Training>();
//onCreate
adapter = new TechSkillsAdapter(getContext(), mTrainingsList);
...
}
#Override
public void onResponse (Call<List<Training>> call, Response<List<Training>> response) {
List<Training> training = response.body();
mTrainingList.clear();
mTrainingList.addAll(training);
adapter.notifyDatasetChanged();
progressDialog.dismiss();
}
EDIT: Also, please note that in your code you're going over the list of trainings and you recreate the adapter for each item! Try to avoid this in the future.

Related

How to call Json using Retrofit

I have JSON as below:
{
"Search": [
{
"Title": "Iron Man",
"Year": "2008"
},
{
"Title": "Iron Man2",
"Year": "20010"
},
{
"Title": "Iron Man3",
"Year": "2013"
}
]
}
Then I've created APIservice as below:
public interface MyApi {
#GET("/movies")
Call<ArrayList<MovieSearch>> getartistdata();
}
My data classes as per below
public class MovieSearch {
#SerializedName("Search")
public List<Search> Search =null;
}
public class Search {
#SerializedName("Title")
public String Title="";
#SerializedName("Year")
public String Year="";
#SerializedName("Poster")
public String Poster="";
public Search() {
}
public Search(String Title, String Year, String Poster) {
this.Title = Title;
this.Year = Year;
}
}
Now I'm trying to implement viewmodel class as below
public class MyListViewModel extends ViewModel {
public String Title = "";
public String Year= "";
public String Poster = "";
public MutableLiveData<ArrayList<MyListViewModel>> mutableLiveData = new MutableLiveData<>();
private ArrayList<MyListViewModel> arrayList;
private ArrayList<MovieSearch> myList;
public String getImageurl() {
return Poster;
}
#BindingAdapter({"imageUrl"})
public static void loadimage(ImageView imageView, String imageUrl) {
Glide.with(imageView.getContext()).load(imageUrl).apply(RequestOptions.circleCropTransform()).into(imageView);
//Picasso.with(imageView.getContext()).load(imageUrl).into(imageView);
}
public MyListViewModel() {
}
public MyListViewModel(MovieSearch myList) {
this.Title = myList.Title;
this.Poster = myList.Poster;
this.Year= myList.Year;
}
public MutableLiveData<ArrayList<MyListViewModel>> getMutableLiveData() {
arrayList = new ArrayList<>();
MyApi api = MyClient.getInstance().getMyApi();
Call<ArrayList<MovieSearch>> call = api.getartistdata();
call.enqueue(new Callback<ArrayList<MovieSearch>>() {
#Override
public void onResponse(Call<ArrayList<MovieSearch>> call, Response<ArrayList<MovieSearch>> response) {
myList = new ArrayList<>();
myList = response.body();
for (int i = 0; i < myList.size(); i++) {
MovieSearch myk = myList.get(i);
MyListViewModel myListViewModel = new MyListViewModel(myk);
arrayList.add(myListViewModel);
mutableLiveData.setValue(arrayList);
}
}
#Override
public void onFailure(Call<ArrayList<MovieSearch>> call, Throwable t) {
}
});
return mutableLiveData;
}
}
But I'm getting "Cannot resolve symbol" error on the following:
public MyListViewModel(MovieSearch myList) {
this.Title = myList.Title;
this.Poster = myList.Poster;
this.Year= myList.Year;
}
I'm trying to get the data from JSON and bind it to view holder. But I couldn't figure it out how to call it. Someone please help me to fix it.
You are pretty close, you just have to make few changes in service class and viewmodel to make this work. Make below changes
MyApi interface: -> becoz your json is not arraylist but an object of MovieSearch so make change accordingly inside that the arraylist of search is there.
public interface MyApi {
#GET("/movies")
Call<MovieSearch> getartistdata();
}
MyListViewModel class:
public class MyListViewModel extends ViewModel {
public MutableLiveData<ArrayList<Search>> mutableLiveData = new MutableLiveData<>();
#BindingAdapter({"imageUrl"})
public static void loadimage(ImageView imageView, String imageUrl) {
Glide.with(imageView.getContext()).load(imageUrl).apply(RequestOptions.circleCropTransform()).into(imageView);
//Picasso.with(imageView.getContext()).load(imageUrl).into(imageView);
}
public MyListViewModel() {
//do something else, your view model is not POJO it's the handler not storage.
}
public MutableLiveData<ArrayList<Search>> getSearchResults() {
MutableLiveData<ArrayList<Search>> localData = new MutableLiveData<>();
MyApi api = MyClient.getInstance().getMyApi();
Call<MovieSearch> call = api.getartistdata();
call.enqueue(new Callback<MovieSearch>() {
#Override
public void onResponse(Call<MovieSearch> call, Response<MovieSearch> response) {
List<Search> myList = response.body().Search;
mutableLiveData.setValue(myList);
localData.setValue(myList);
}
#Override
public void onFailure(Call<ArrayList<MovieSearch>> call, Throwable t) {
//handle the error
}
});
return localData;
}
}
Call the above view model by creating an object of view model and inside some function. this part usually goes inside some UI class activity or fragment:
//listening for change in live data
// this would be in ui layer
myListViewModelObject.getSearchResults().observe(this, new Observer<ArrayList<Search>>() {
#Override
public void onChanged(#Nullable ArrayList<Search> obj) {
// handle changes here, to use the data write code below
}
});
Advice: Don't use your viewmodel to store the data it's not what is made for. It is for handling the data and actions not the keeper of data. Managing the data and business logic in viewmodel can cause many problems. Always if possible break things into smaller parts like, get retrofit object from some other class or mathod don't put it in the calling method itself this will code duplication and may impact the performance also.
Note: I haven't tested the code just made the best possible changes and it should run. Please add the missing parts if i have removed any.
Here I noticed that you are keeping an array of ViewModels. The main thing is ViewModels are not meant to be used like that.
I think its better if you read some documentation and sample implementations of ViewModels to have a better understanding.
Coming to your implementation, everything looks fine but one thing that is converting your list of MovieSearch into a list of MyListViewModel.
In ViewModel
public class MyListViewModel extends ViewModel {
private MutableLiveData<MovieSearch> mutableLiveData = new MutableLiveData<>();
#BindingAdapter({"imageUrl"})
public static void loadimage(ImageView imageView, String imageUrl) {
Glide.with(imageView.getContext()).load(imageUrl).apply(RequestOptions.circleCropTransform()).into(imageView);
//Picasso.with(imageView.getContext()).load(imageUrl).into(imageView);
}
public LiveData<MovieSearch> getMutableLiveData() {
loadData();
return mutableLiveData;
}
private void loadData(){
MyApi api = MyClient.getInstance().getMyApi();
Call<MovieSearch> call = api.getartistdata();
call.enqueue(new Callback<MovieSearch>() {
#Override
public void onResponse(Call<MovieSearch> call, Response<MovieSearch> response) {
MovieSearch movieSearch = response.body();
mutableLiveData.setValue(myList);
}
#Override
public void onFailure(Call<ArrayList<MovieSearch>> call, Throwable t) {
}
});
}
}
In activity or fragment, you can access the liveData through getMutableLiveData() and set the List<MovieSearch> to the adapter.
One more thing, given your response JSON, your API should like below
public interface MyApi {
#GET("/movies")
Call<MovieSearch> getartistdata();
}
MovieSearch is not in a list
Adapter
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private ArrayList<Search> arrayList = new ArrayList<>;
private LayoutInflater layoutInflater;
public void submitList(ArrayList<Search> searchList){
this.arrayList = searchList;
notifyDataSetChanged();
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
if (layoutInflater==null){
layoutInflater=LayoutInflater.from(parent.getContext());
}
MyListBinding myListBinding= DataBindingUtil.inflate(layoutInflater, R.layout.mylist,parent,false);
return new ViewHolder(myListBinding);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
Search search =arrayList.get(position);
holder.bind(search);
}
#Override
public int getItemCount() {
return arrayList.size();
}
class ViewHolder extends RecyclerView.ViewHolder{
private MyListBinding myListBinding;
public ViewHolder(#NonNull MyListBinding myListBinding) {
super(myListBinding.getRoot());
this.myListBinding=myListBinding;
}
public void bind(Search myli){
this.myListBinding.setMylistmodel(myli);
myListBinding.executePendingBindings();
}
public MyListBinding getMyListBinding(){
return myListBinding;
}
}
}
Activity
adapter = MyAdapter();
recyclerview=(RecyclerView)findViewById(R.id.recyclerView);
recyclerview.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
recyclerview.setAdapter(adapter);
myListViewModel = ViewModelProviders.of(this).get(MyListViewModel.class);
myListViewModel.getSearchResults().observe(this, new Observer<MovieSearch>() {
#Override
public void onChanged(#Nullable MovieSearch movieSearch) {
// handle changes here, to use the data write code below
adapter.submitList(movieSearch.Search);
}
});
And one small thing, please don't start variable names with capital letters. It is better to follow naming conventions from the start.

Getting "No adapter attached; skipping layout" error when parsing a named JSON inside another list

I want to parse some JSON (Car models):
{"modelos": [{"nome": AMAROK},{"nome": JETTA}]}
I have the code below: ADAPTER
public class ListaVeiculosAdapter extends RecyclerView.Adapter<ListaVeiculosAdapter.ListaVeiculosViewHolder> {
private List<VeiculosResponse> veiculos;
public ListaVeiculosAdapter(List<VeiculosResponse> veiculos) {
this.veiculos = veiculos;
}
public ListaVeiculosAdapter() {
}
#NonNull
#Override
public ListaVeiculosViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_veiculo, parent, false);
return new ListaVeiculosViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ListaVeiculosViewHolder holder, int position) {
holder.textNomeVeiculo.setText(veiculos.get(position).getNome());
}
#Override
public int getItemCount() {
return (veiculos!= null && veiculos.size()>0) ? veiculos.size() : 0;
}
static class ListaVeiculosViewHolder extends RecyclerView.ViewHolder{
private TextView textNomeVeiculo;
public ListaVeiculosViewHolder(View itemView){
super(itemView);
textNomeVeiculo = itemView.findViewById(R.id.text_veiculo);
}
}}
Main Activity:
RecyclerView recyclerVeiculos;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lista_veiculos);
// Toolbar toolbar = findViewById(R.id.my_toolbar);
//setSupportActionBar(toolbar);
recyclerVeiculos = findViewById(R.id.my_recycler_view);
ListaVeiculosAdapter adapter = new ListaVeiculosAdapter();
ApiService.getInstance().getModels().enqueue(new Callback<VeiculosResult>() {
#Override
public void onResponse(Call<VeiculosResult> call, Response<VeiculosResult> response) {
RecyclerView.LayoutManager linearLayoutManager = new LinearLayoutManager(ListaVeiculosActivity.this);
recyclerVeiculos.setLayoutManager(linearLayoutManager);
recyclerVeiculos.setAdapter(new ListaVeiculosAdapter(response.body().getModelos()));
}
#Override
public void onFailure(Call<VeiculosResult> call, Throwable t) {
}
});
}}
The problem is that I get the error
"E/RecyclerView: No adapter attached; skipping layout"
when I try to run the application.
Other Retrofit configuration codes:
public interface VeiculosService {
#GET("marcas/59/modelos")
Call<VeiculosResult> getModels();
}
private static VeiculosService INSTANCE;
public static VeiculosService getInstance() {
if(INSTANCE == null){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://parallelum.com.br/fipe/api/v1/carros/")
.addConverterFactory(MoshiConverterFactory.create())
.build();
INSTANCE = retrofit.create(VeiculosService.class);
}
return INSTANCE;
}
My idea is to create a list (recycler view) with car models so the user can choose which car they want.
You have to set layout manager and set adapter to your recyclerview before API call.
Modify your MainActivity like this.
RecyclerView recyclerVeiculos;
List<VeiculosResponse> veiculos;
ListaVeiculosAdapter adapter;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lista_veiculos);
// Toolbar toolbar = findViewById(R.id.my_toolbar);
//setSupportActionBar(toolbar);
recyclerVeiculos = findViewById(R.id.my_recycler_view);
RecyclerView.LayoutManager linearLayoutManager = new
LinearLayoutManager(ListaVeiculosActivity.this);
recyclerVeiculos.setLayoutManager(linearLayoutManager);
// create an empty list and pass it to your adapter
veiculos = new ArrayList<>()
adapter = new ListaVeiculosAdapter(veiculos)
recyclerVeiculos.setAdapter(adapter);
ApiService.getInstance().getModels().enqueue(new Callback<VeiculosResult>() {
#Override
public void onResponse(Call<VeiculosResult> call, Response<VeiculosResult> response) {
if (response.isSuccessful() && response.body() != null){
veiculos.addAll(response.body().getModelos());
// after getting new data you have to notify your adapter that your data set is changed like below.
adapter.notifyDataSetChanged();
}
}
#Override
public void onFailure(Call<VeiculosResult> call, Throwable t) {
}
});
// ...
I think what has been stated by #Jakir will sort out the skipping adpter issue. This mostly occurs when there is an issue with how you are attaching adapter to recyclerView.
//Set layoutmanager attaching adapter to recyclerView
recyclerView.setLayoutManager(myLayoutManager)
recyclerView.setAdapter(myAdapter);
myAdapter.notifyDatasetChanged();
Then for the json response you need to create a pojo classes for it.
This will fetch the array of Json objects. ModelusData class will now contain the individual modelus info
public class ModelusObject {
#SerializedName("modelus")
private List<ModelusData> list;
public List<ModelusData> getList() {
return list;
}
public void setList(List<ModelusData> list) {
this.list = list;
}
}
For the ModelusData class
public class ModelusData{
#SerializedName("nome")
String nome;
#SerializedName("codigo")
String codigo;
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getCodigo() {
return codigo;
}
public void setCodigo(String codigo) {
this.codigo = codigo;
}
}

RecyclerView Not showing data from Json array

Whenever i try to add data to recycler view, the recycler view doesn't show any data. I tried debugging the program and I am successfully getting JSON data using Retrofit into the application(Checked by printing it in Log). But RecyclerView shows no data.Here is my code:
CartActivity.java
public class CartActivity extends AppCompatActivity {
RecyclerView listshowrcy;
List<CartDisplay> cartlist = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cart);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
API api = retrofit.create(API.class);
String username = getIntent().getStringExtra("Username");
Call<List<CartDisplay>> call = api.getCartContent(username);
call.enqueue(new Callback<List<CartDisplay>>() {
#Override
public void onResponse(Call<List<CartDisplay>> call, Response<List<CartDisplay>> response) {
List<CartDisplay> cart = response.body();
for(CartDisplay cartContent : cart){
cartlist.add(cartContent);
}
}
#Override
public void onFailure(Call<List<CartDisplay>> call, Throwable t) {
}
});
listshowrcy = (RecyclerView)findViewById(R.id.cartList);
listshowrcy.setHasFixedSize(true);
CartAdapter cardadapter = new CartAdapter(cartlist,this,username);
listshowrcy.setAdapter(cardadapter);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
listshowrcy.setLayoutManager(linearLayoutManager);
}
}
CartAdapter.java
public class CartAdapter extends RecyclerView.Adapter<CartAdapter.Holderview> {
private List<CartDisplay> cartlist;
private Context context;
private String username;
public CartAdapter(List<CartDisplay> cartlist, Context context,String username) {
this.cartlist = cartlist;
this.context = context;
this.username = username;
}
#Override
public Holderview onCreateViewHolder(ViewGroup parent, int viewType) {
View layout = LayoutInflater.from(parent.getContext()).inflate(R.layout.cart_item,parent,false);
return new Holderview(layout);
}
#Override
public void onBindViewHolder(Holderview holder, int position) {
holder.pname.setText(cartlist.get(position).getP_name());
holder.pquant.setText(cartlist.get(position).getQuantity());
holder.price.setText(String.valueOf(cartlist.get(position).getPrice()));
}
#Override
public int getItemCount() {
return cartlist.size();
}
class Holderview extends RecyclerView.ViewHolder
{
TextView pname;
TextView pquant;
TextView price;
Holderview(View itemview){
super(itemview);
pname = (TextView)itemview.findViewById(R.id.product_name);
pquant = (TextView)itemview.findViewById(R.id.product_quant);
price = (TextView)itemview.findViewById(R.id.product_price);
}
}
}
After you get your response you must notify adapter, that data has changed:
#Override
public void onResponse(Call<List<CartDisplay>> call, Response<List<CartDisplay>> response) {
List<CartDisplay> cart = response.body();
cartList.clear(); // don't forget to clear list, to avoid duplicates
for(CartDisplay cartContent : cart){
cartlist.add(cartContent);
}
adapter.notifyDataSetChanged();
}
Another way: you can create method: adapter.setData(cardList)
And there refresh adapter data and call notifyDataSetChanged()
In addition to #kdblue's answer, there is another issue with your code. The adapter doesn't know that new data has been added to the underlying list.
You can either use notifyDataSetChanged:
List<CartDisplay> cart = response.body();
for(CartDisplay cartContent : cart){
cartlist.add(cartContent);
}
cardadapter.notifyDataSetChanged();
Or let the adapter handle new items directly, by adding a method to the adapter like:
public void add(CartDisplay cartDisplay) {
cartlist.add(user);
notifyItemInserted(cartlist.size());
}
And adding the items directly to the adapter:
List<CartDisplay> cart = response.body();
for(CartDisplay cartContent : cart){
cardadapter.add(cartContent);
}
Be aware that you will have to change you code structure to apply these strategies.

onCreateView wait queries result

I need to execute a query to my DB to get some IDs and then use those IDs to execute another query to Realm DB and return the result to the Adapter which is used to create UI. My problem is that the adapter is created in onCreatView (main thread) and i need to let that wait queries result. Do you know how can i do? Really thanks :)
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Realm realm = Realm.getDefaultInstance();
cardList = new ArrayList(realm.where(Card.class).findAll().sort("rarity"));
View rootView = inflater.inflate(R.layout.inventory_fragment_layout, null);
RecyclerView recyclerView = rootView.findViewById(R.id.inventory_recycler);
recyclerView.setLayoutManager(new GridLayoutManager(this.getContext(), 2));
adapter = new inventoryFragmentRecyclerAdapter(this.getContext(), getCards()); <----- getCards() is used here.
adapter.setClickListener(this);
recyclerView.setAdapter(adapter);
TextView topText = rootView.findViewById(R.id.inv_topText);
topText.setTypeface(Typeface.createFromAsset(getActivity().getAssets(), "fonts/Square.ttf"));
return rootView;
}
.
.
.
.
.
.
public List<Card> getCards() { //It is used when adapter is created, and it need to contain all cards to display
//Realm realm = Realm.getDefaultInstance();
String urlToGet = "myurl";
String user_name = settings.getString("username", null);
OkHttpClient client = new OkHttpClient();
RequestBody formBody = new FormBody.Builder()
.add("user_name", user_name)
.build();
Request request = new Request.Builder()
.url(urlToGet)
.post(formBody)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, final Response response) throws IOException {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
responseCardInInventory = response.body().string();
handler.post(new Runnable() {
#Override
public void run() {
List<CardInInv> cardListInInv = new ArrayList<>();
Gson gson = new Gson();
CardInInventory civ = gson.fromJson(responseCardInInventory, CardInInventory.class);
cardListInInv = getCards(civ);
//HERE I NEED TO USE REALM TO EXECUTE A QUERY USING cardListInInv
}
});
}
});
//HERE I NEED TO RETURN THE RESULT OF THE QUERY EXECUTED BEFORE
return cardsIninv;
}
EDIT 1: Hre is my adapter code
public class inventoryFragmentRecyclerAdapter extends RecyclerView.Adapter<inventoryFragmentRecyclerAdapter.ViewHolder> {
private List<Card> dataCard = new ArrayList<>();
private LayoutInflater mInflater;
private ItemClickListener mClickListener;
// data is passed into the constructor
public inventoryFragmentRecyclerAdapter(Context context, List<Card> data) {
this.mInflater = LayoutInflater.from(context);
this.dataCard = data;
}
// inflates the cell layout from xml when needed
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.inventory_rw, parent, false);
return new ViewHolder(view);
}
// binds the data to the in each cell
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.cardImage.setImageBitmap(BitmapFactory.decodeByteArray(dataCard.get(position).getCardImage(), 0, dataCard.get(position).getCardImage().length));
}
// total number of cells
#Override
public int getItemCount() {
return dataCard.size();
}
// stores and recycles views as they are scrolled off screen
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
ImageView cardImage;
//instanzio le componenti della pagina
ViewHolder(View itemView) {
super(itemView);
cardImage = itemView.findViewById(R.id.inv_rw_cardimage);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
if (mClickListener != null) mClickListener.onItemClick(view, getAdapterPosition());
}
}
// convenience method for getting data at click position
Card getItem(int id) {
return dataCard.get(id);
}
// allows clicks events to be caught
public void setClickListener(ItemClickListener itemClickListener) {
this.mClickListener = itemClickListener;
}
// parent activity will implement this method to respond to click events
public interface ItemClickListener {
void onItemClick(View view, int position);
}
}
Move your query to your adapter and start your adapter with an empty list. Make your query call at the end of your constructor. When you've successfully finished your query, notify your adapter that it has changed and let it handle the changes with your new data at the end of your queries finishing successfully. So after doing that your adapter would look something like this.
public inventoryFragmentRecyclerAdapter(Context context, List<Card> data) {
this.mInflater = LayoutInflater.from(context);
getCards();
}
public void getCards() { //It is used when adapter is created, and it need to contain all cards to display
//Realm realm = Realm.getDefaultInstance();
String urlToGet = "myurl";
String user_name = settings.getString("username", null);
OkHttpClient client = new OkHttpClient();
RequestBody formBody = new FormBody.Builder()
.add("user_name", user_name)
.build();
Request request = new Request.Builder()
.url(urlToGet)
.post(formBody)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, final Response response) throws IOException {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
responseCardInInventory = response.body().string();
handler.post(new Runnable() {
#Override
public void run() {
List<CardInInv> cardListInInv = new ArrayList<>();
Gson gson = new Gson();
CardInInventory civ = gson.fromJson(responseCardInInventory, CardInInventory.class);
cardListInInv = getCards(civ);
//use realm to do all your card inventory stuff to get your resulting object
dataCard = yourObjectListAfterUsingRealm;
notifyDataSetChanged();
}
});
}
});
}
move This line adapter = new inventoryFragmentRecyclerAdapter(context, yourList);
inside onResponse() and pass the list directly without the getCards();
Edit:
initialize your adapter when your data is ready.where ? this is based on your needs.
or initialize it with empty list and call adapter.notifyDataSetChanged() when something changed (added or removed from the list or a brand new list is initialized)
Create your adapter with empty list
adapter = new inventoryFragmentRecyclerAdapter(this.getContext(), new ArrayList<Card>());
and then in your adapter create a method like this
public void updateCardList(List<Card> cardList) {
this.mCards = cardList;
notifyDataSetChanged();
}
and call this function in the
#Override
public void onResponse(Call call, final Response response)

Populate ListView using Retrofit 2

I'm trying to populate a ListView with a JSON from a server. I'm getting the data from the server but I can't seem to figure out how to put it in a ListView. The error I'm getting is in the onResponse.
It says: "Error:(84, 54) error: constructor JucatoriLiberiArrayAdapter in class JucatoriLiberiArrayAdapter cannot be applied to given types;
required: Context,ArrayList
found: FragmentActivity,Call>
reason: actual argument Call> cannot be converted to ArrayList by method invocation conversion"
I think I've tried almost every Alt+Enter fix there is. Also tried to change type from Call<ArrayList<FreePlayers> to just ArrayList<> or just Call<>.
This is my class:
#Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
final Retrofit builder = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.baseUrl(ROOT)
.build();
REST_CLIENT = builder.create(API.class);
final Call<FreePlayers> request = REST_CLIENT.getFreePlayers(1);
request.enqueue(new Callback<FreePlayers>() {
#Override
public void onResponse(final Call<FreePlayers> call, final Response<FreePlayers> response) {
ArrayList<FreePlayers> players = null;
players = call;
ListView lv = (ListView) getView().findViewById(android.R.id.list);
JucatoriLiberiArrayAdapter adapter = new JucatoriLiberiArrayAdapter(getActivity(),
players);
lv.setAdapter(adapter);
}
#Override
public void onFailure(final Call<FreePlayers> call, final Throwable t) {
}
});
}
My API interface:
public interface API {
#FormUrlEncoded
#POST("/fan-sport/app-test/")
Call<ArrayList<FreePlayers>> getFreePlayers(#Field("getFreePlayers") int freePlayers);
}
And this is my custom adapter:
public class JucatoriLiberiArrayAdapter extends BaseAdapter {
private Context context;
private ArrayList<FreePlayers> players;
public JucatoriLiberiArrayAdapter(Context context, ArrayList<FreePlayers> playersList) {
super();
this.context = context;
this.players = playersList;
}
#Override
public int getCount() {
return 0;
}
#Override
public Object getItem(final int position) {
return null;
}
#Override
public long getItemId(final int position) {
return 0;
}
#Override
public View getView(final int position, View convertView, final ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
convertView = inflater.inflate(R.layout.list_view_jucatori_liberi, null);
TextView playerName = (TextView) convertView.findViewById(R.id.name);
playerName.setText((CharSequence) players.get(position));
}
return convertView;
}
}
In your onResponse() you receive a Call object wheras your adapter expects a List<FreePlayers>, the response argument is the list of players you want to give to your adapter.
Try this code:
final Call<List<FreePlayers>> request = REST_CLIENT.getFreePlayers(1);
request.enqueue(new Callback<List<FreePlayers>>() {
#Override
public void onResponse(final Call<List<FreePlayers>> call, final Response<List<FreePlayers>> response) {
ArrayList<FreePlayers> players = null;
players = response.body();
ListView lv = (ListView) getView().findViewById(android.R.id.list);
JucatoriLiberiArrayAdapter adapter = new JucatoriLiberiArrayAdapter(getActivity(),
players);
lv.setAdapter(adapter);
}
#Override
public void onFailure(final Call<FreePlayers> call, final Throwable t) {
}
});
call is assgined to players, but players type is ArrayList<FreePlayers> and call type is Call<List<FreePlayers>. Not the same. The adapter expects ArrayList<FreePlayers>.
You're declaring
Call<FreePlayers>,
but expect to receive
Call<ArrayList<FreePlayers>>.
Also, you're Adapter is broken. These methods should look something like:
#Override
public int getCount() {
if (players == null) return 0;
return players.size();
}
#Override
public Object getItem(final int position) {
if (players == null || players.size() == 0) return null;
return players.get(position);
}
#Override
public long getItemId(final int position) {
return getItem(position).getId(); // or something like an id that suits you.
}

Categories

Resources