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.
Related
I am making a simple app in which the user will able to search for books by its name, with google.com book api. For now, I wish to present a list of books with android in their name. I am doing it with Retrofit2 and RecycleView, but nothing is showing.
MainActivity:
public class MainActivity extends AppCompatActivity {
RecyclerView recyclerView;
KnjigaAdapter knjigaAdapter;
List<KnjigaModel> listaKnjiga;
public static final String BASE_URL = "https://www.googleapis.com/books/";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
uzmiKomentare();
}
public void uzmiKomentare() {
Gson gson = new GsonBuilder().serializeNulls().create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
KnjigaApi knjigaApi = retrofit.create(KnjigaApi.class);
final Call<KnjigaModel> pozivZaListuKnjiga = knjigaApi.getKnjige("android");
pozivZaListuKnjiga.enqueue(new Callback<KnjigaModel>() {
#Override
public void onResponse(Call<KnjigaModel> call, Response<KnjigaModel> response) {
if (!response.isSuccessful()) {
return;
}
//generateRecycleView(WHAT TO PUT HERE!!!!);
}
#Override
public void onFailure(Call<KnjigaModel> call, Throwable t) {
Log.d("MainActivity:", t.getMessage());
}
});
}
private void generateRecycleView(List<KnjigaModel> knjige) {
listaKnjiga = new ArrayList<>();
recyclerView = findViewById(R.id.recycleview);
knjigaAdapter = new KnjigaAdapter(this, knjige);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(knjigaAdapter);
if (knjigaAdapter.getItemCount() == 0){
Log.i("List is empty: ","YES");
}
else {
Log.i("list is empty: ","No");
}
}
}
api inteface:
public interface KnjigaApi {
#GET("v1/volumes")
Call<KnjigaModel> getKnjige(#Query("q") String knjiga);
}
model class:
public class KnjigaModel {
#SerializedName("title")
#Expose
private String imeKnjige;
#SerializedName("authors")
#Expose
private String imeAutora;
#SerializedName("thumbnail")
#Expose
private String slikaKnjige;
public KnjigaModel(String imeKnjige, String imeAutora,String slikaKnjige) {
this.imeKnjige = imeKnjige;
this.imeAutora = imeAutora;
this.slikaKnjige = slikaKnjige;
}
public String getImeKnjige() {
return imeKnjige;
}
public String getImeAutora() {
return imeAutora;
}
public String getSlikaKnjige() {
return slikaKnjige;
}
}
and my adapter:
public class KnjigaAdapter extends RecyclerView.Adapter<KnjigaAdapter.KomentariViewHolder> {
private List<KnjigaModel> listaKnjiga;
private LayoutInflater inflater;
private Context context;
public KnjigaAdapter(Context context, List<KnjigaModel> listaKnjiga) {
this.listaKnjiga = listaKnjiga;
this.context = context;
}
#Override
public KomentariViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
inflater = LayoutInflater.from(context);
// Inflate the custom layout
View postView = inflater.inflate(R.layout.single_item, parent, false);
// Return a new holder instance
return new KomentariViewHolder(postView);
}
#Override
public void onBindViewHolder(KomentariViewHolder holder, int position) {
KnjigaModel knjige = listaKnjiga.get(position);
holder.naslovKnjige.setText(knjige.getImeKnjige());
holder.imeAutora.setText(knjige.getImeAutora());
Glide.with(context)
.load(knjige.getSlikaKnjige())
.into(holder.slikaKnjige);
}
#Override
public int getItemCount() {
return listaKnjiga.size();
}
public class KomentariViewHolder extends RecyclerView.ViewHolder {
private TextView naslovKnjige;
private TextView imeAutora;
private ImageView slikaKnjige;
public KomentariViewHolder(View itemView) {
super(itemView);
naslovKnjige = itemView.findViewById(R.id.ime_knjige);
imeAutora = itemView.findViewById(R.id.autor_knjige);
slikaKnjige = itemView.findViewById(R.id.sika_korica);
}
}
}
my JSON format:
https://www.googleapis.com/books/v1/volumes?q=android
First of all, I strongly recommend to use RxJava for asynchronous requests, though it's totally optional.
Your Problem:
Your "getKnjige" Method returns only ONE Model, though the endpoint is named volumes (plural), you need to wrap your KnjigaModel in a class like
data class KnjigaResponse(val items: List<KnjigaModel>)
(Kotlin for simplicity, you can also generate a Java class with a single member volumes member that holds a list of KnjigaModel)
In addition, your model is wrong. authors is not a string, but a List of Strings, and "thumbnail" is wrapped in an "imageLinks" Object, and title is wrapped in a volumeInfo Object.
Your retrofit interface then would look like this:
public interface KnjigaApi {
#GET("v1/volumes")
Call<KnjigaResponse> getKnjige(#Query("q") String knjiga);
Request:
final Call<KnjigaResponse> pozivZaListuKnjiga = knjigaApi.getKnjige("android");
pozivZaListuKnjiga.enqueue(new Callback<KnjigaResponse>() {
#Override
public void onResponse(Call<KnjigaResponse> call, Response<KnjigaResponse> response) {
if (!response.isSuccessful()) {
return;
}
generateRecycleView(response.items);
}
#Override
public void onFailure(Call<KnjigaResponse> call, Throwable t) {
Log.d("MainActivity:", t.getMessage());
}
});
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;
}
}
I am trying to do this..
.
I am consuming two different requests resources from the same API and in MainActivity doing two different calls. But, I can't show the content I want from both JSON on one RecyclerView view.
MainActivity.java
public class MainActivity extends AppCompatActivity {
private Retrofit retrofit;
private static final String TAG = "Football";
private RecyclerView recyclerView;
private ListaPartidosAdapter listaPartidosAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView)findViewById(R.id.recyclerView);
listaPartidosAdapter = new ListaPartidosAdapter(this);
recyclerView.setAdapter(listaPartidosAdapter);
recyclerView.setHasFixedSize(true);
final LinearLayoutManager layoutManager = new LinearLayoutManager(this, VERTICAL, true);
recyclerView.setLayoutManager(layoutManager);
retrofit = new Retrofit.Builder()
.baseUrl("http://api.football-data.org/v2/")
.addConverterFactory(GsonConverterFactory.create())
.build();
obtenerDatos();
}
private void obtenerDatos() {
Calendar calendar = Calendar.getInstance();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
String todayDate=df.format(calendar.getTime());
calendar.add(Calendar.DATE,3);
String endDate = df.format(calendar.getTime());
Log.i(TAG, "todayDate : " + todayDate);
Log.i(TAG, "endDate : " + endDate);
footballdataService service = retrofit.create(footballdataService.class);
Call<PartidosRespuesta> partidosRespuestaCall = service.obtenerlistaPartidos(todayDate,endDate);
Call<StandingsRespuesta> standingsRespuestaCall = service.obtenerStandings();
partidosRespuestaCall.enqueue(new Callback<PartidosRespuesta>() {
#Override
public void onResponse(Call<PartidosRespuesta> call, Response<PartidosRespuesta> response) {
if(response.isSuccessful()) {
PartidosRespuesta partidosRespuesta = response.body();
List<Partido> listaPartidos = partidosRespuesta.getMatches();
listaPartidosAdapter.adicionarListaPartidos((ArrayList<Partido>) listaPartidos);
}
else {
Log.e(TAG, "onResponse: " + response.errorBody());
}
}
#Override
public void onFailure(Call<PartidosRespuesta> call, Throwable t) {
Log.e(TAG, "onFailure: " + t.getMessage());
}
});
standingsRespuestaCall.enqueue(new Callback<StandingsRespuesta>() {
#Override
public void onResponse(Call<StandingsRespuesta> call, Response<StandingsRespuesta> response) {
if(response.isSuccessful()) {
StandingsRespuesta standingsRespuesta = response.body();
List<Stand> listaStands = standingsRespuesta.getStandings();
listaPartidosAdapter.adicionarListaStands((ArrayList<Stand>) listaStands);
}
}
#Override
public void onFailure(Call<StandingsRespuesta> call, Throwable t) {
}
});
}
}
As I say before, each request has a different enqueue Call. I don't know if it is the right way of do it but think yes because each call has its own service.
ListaPartidosAdapter.java
public class ListaPartidosAdapter extends RecyclerView.Adapter<ListaPartidosAdapter.ViewHolder> {
private static final String TAG = "Football_Adapter";
private ArrayList<Partido> dataset;
private ArrayList<Stand> dataset_stand;
private Context context;
public ListaPartidosAdapter(Context context) {
this.context = context;
this.dataset = new ArrayList<Partido>();
this.dataset_stand = new ArrayList<Stand>();
}
#Override
public ListaPartidosAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_partidos, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ListaPartidosAdapter.ViewHolder holder, int position) {
Partido p = dataset.get(position);
String status = p.getStatus();
if (status.equals("SCHEDULED")){
String status_ = "SCH";
holder.status.setText(status_);
}
holder.utcDate.setText(p.getUtcDate());
Partido.EquipoCasa homeTeam = p.getHomeTeam();
String id_homeTeam = homeTeam.getId();
holder.homeTeam.setText(homeTeam.getName());
Partido.EquipoVisita awayTeam = p.getAwayTeam();
holder.awayTeam.setText(awayTeam.getName());
Stand s = dataset_stand.get(position);
Stand.Table table = (Stand.Table) s.getTable();
Stand.Table.Equipo team = (Stand.Table.Equipo) table.getEquipo();
String id_equipo = team.getId();
holder.homeTeam.setText(team.getName());
if(id_homeTeam.equals(id_equipo)){
Glide.with(context)
.load(team.getCrestUrl())
.centerCrop()
.crossFade()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(holder.team);
//holder.team.setImageDrawable(team.getCrestUrl());
}
}
#Override
public int getItemCount() {
return dataset.size()+dataset_stand.size();
}
public void adicionarListaPartidos(ArrayList<Partido> listaPartidos){
dataset.addAll(listaPartidos);
notifyDataSetChanged();
}
public void adicionarListaStands(ArrayList<Stand> listaStands){
dataset_stand.addAll(listaStands);
notifyDataSetChanged();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView status;
private TextView utcDate;
private TextView homeTeam;
private TextView awayTeam;
public ImageView team;
public ViewHolder(View itemView) {
super(itemView);
status = (TextView) itemView.findViewById(R.id.status);
utcDate = (TextView) itemView.findViewById(R.id.utcDate);
homeTeam = (TextView) itemView.findViewById(R.id.homeTeam);
awayTeam = (TextView) itemView.findViewById(R.id.awayTeam);
team = (ImageView) itemView.findViewById(R.id.team);
}
}
}
The problem comes in this line. Stand s = dataset_stand.get(position);, if a comment it with code below, It works without using the second JSON or the second request but as I showed on image I want to merge two different requests on the same RecyclerView view.
The problem statement
The problem comes in this line. Stand s = dataset_stand.get(position);, if a comment it with code below, It works without using the second JSON or the second request.
Yes, it's expected. Why? Because you're calling RecyclerView.Adapter#notifyDataSetChanged() method inside adicionarListaPartidos(ArrayList<Partido> listaPartidos) after inserting data to ArrayList in RecyclerView array adapter class which informs ArrayAdapter to redraw/refresh components inside RecyclerView container.
Now RecycelrView starts binding component within the onBindViewHolder(ListaPartidosAdapter.ViewHolder holder, int position) method. So, what's happening here?
#Override
public void onBindViewHolder(ListaPartidosAdapter.ViewHolder holder, int position) {
Partido p = dataset.get(position);
String status = p.getStatus();
if (status.equals("SCHEDULED")){
String status_ = "SCH";
holder.status.setText(status_);
}
holder.utcDate.setText(p.getUtcDate());
Partido.EquipoCasa homeTeam = p.getHomeTeam();
// other code
Stand s = dataset_stand.get(position); // <====== the problem
// other code
}
Your dataset_stand list will be empty (size of the list is 0) until you get data/response from standingsRespuestaCall.enqueue() method in your activity.
Keep in mind that Retrofit's Call#enque() method is Asynchronous. Which means in your case obtenerDatos() method executes top to bottom in a single hit. You only get data when Retrofit returns success response with onResponse() method.
The easiest way to fix this issue is to comment out the notifyDataSetChanged() method inside adicionarListaPartidos(ArrayList<Partido> listaPartidos). Like below
public void adicionarListaPartidos(ArrayList<Partido> listaPartidos){
dataset.addAll(listaPartidos);
// notifyDataSetChanged(); // <====== just COMMENT OUT this line
}
This will prevent onBindViewHolder() being called. When the second request standingsRespuestaCall.enqueue() completes it's operation, as per your code notifies dataset changed. Like below.
public void adicionarListaStands(ArrayList<Stand> listaStands){
dataset_stand.addAll(listaStands);
notifyDataSetChanged(); // <==== DO NOT remove this
}
Side Note: Your code is problematic. You're using multiple request to fill a single RecyclerView container. Your RecyclerView fails to display record if standingsRespuestaCall.enqueue() fails to get response from server.
PS: Please check method names I mentioned in my answer properly and alter the code accordingly. Do not get confused with the method names.
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.
am trying to Fetch the movies data from Mysql DB and show it to Recycler view
but when i run the app nothing shows
here is code i am using Retrofite Library
but i can't parse the Data to the Recycler view
i've made Adapter and Model Class normally like the Json
MainActivity.class
public class MainActivity extends AppCompatActivity {
private static final String url="http://192.168.1.109/stu/";
RecyclerView recyclerViewMovies;
List<MovieListsModels> movies;
MoviesAdapter adapter;
TextView Errortxt;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Errortxt = (TextView)findViewById(R.id.txterror);
recyclerViewMovies = (RecyclerView)findViewById(R.id.recyclerview);
recyclerViewMovies.setHasFixedSize(true);
recyclerViewMovies.setLayoutManager(new LinearLayoutManager(this));
movies = new ArrayList<>();
loadDatafromServer();
}
private void loadDatafromServer() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build();
Api api = retrofit.create(Api.class);
Call<MovieListsModels> call = api.ShowMoviesData();
call.enqueue(new Callback<MovieListsModels>() {
#Override
public void onResponse(Call<MovieListsModels> call, Response<MovieListsModels> response) {
try {
MovieListsModels movie = response.body();
adapter = new MoviesAdapter(MainActivity.this, (List<MovieListsModels>) movie);
recyclerViewMovies.setAdapter(adapter);
}
catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Call<MovieListsModels> call, Throwable t) {
Errortxt.setText(t.getMessage().toString());
}
});
}
this is the interface of the methods
Api.class Interface
public interface Api {
#GET("config.php")
Call<MovieListsModels> ShowMoviesData();
}
MovieLists.class
public class MovieListsModels {
public MovieListsModels() {
}
int id;
String movie_name;
String movie_image;
String movie_genre;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getMovie_name() {
return movie_name;
}
public void setMovie_name(String movie_name) {
this.movie_name = movie_name;
}
public String getMovie_image() {
return movie_image;
}
public void setMovie_image(String movie_image) {
this.movie_image = movie_image;
}
public String getMovie_genre() {
return movie_genre;
}
public void setMovie_genre(String movie_genre) {
this.movie_genre = movie_genre;
}
public MovieListsModels(int id, String movie_name, String movie_image, String movie_genre) {
this.id = id;
this.movie_name = movie_name;
this.movie_image = movie_image;
this.movie_genre = movie_genre;
}
}
MovieAdapter.class
public class MoviesAdapter extends RecyclerView.Adapter<MoviesAdapter.MovieHolderView> {
private Context mContext;
private List<MovieListsModels> MovieList = new ArrayList<>();
public MoviesAdapter(Context mContext, List<MovieListsModels> movieList) {
this.mContext = mContext;
MovieList = movieList;
}
#NonNull
#Override
public MovieHolderView onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_item,parent,false);
MovieHolderView holder = new MovieHolderView(view);
return holder;
}
#Override
public void onBindViewHolder(#NonNull MovieHolderView holder, int position) {
MovieListsModels list = MovieList.get(position);
holder.txtName.setText(list.getMovie_name());
holder.txtGenre.setText(list.getMovie_genre());
Picasso.get()
.load(list.getMovie_image())
.into(holder.imgMovie);
}
#Override
public int getItemCount() {
return MovieList.size();
}
public class MovieHolderView extends RecyclerView.ViewHolder {
TextView txtName,txtGenre;
ImageView imgMovie;
public MovieHolderView(View itemView) {
super(itemView);
txtName =(TextView)itemView.findViewById(R.id.movieName);
txtGenre =(TextView)itemView.findViewById(R.id.movieGenre);
imgMovie =(ImageView)itemView.findViewById(R.id.movieImg);
}
}
}
If you receive a list of movies is better because you expect a list, I suppose
public void onResponse(Call<MovieListsModels> call, Response<MovieListsModels> response) {
try {
List<MovieListsModels> movie = response.body();
adapter = new MoviesAdapter(MainActivity.this, movies);
And I believe that not executing the notifyDataSetChanged, you can added like that:
private Context mContext;
private List<MovieListsModels> MovieList = new ArrayList<>();
public MoviesAdapter(Context mContext, List<MovieListsModels> movieList) {
this.mContext = mContext;
MovieList = movieList;
notifiyDataSetChanged();
If you are having json response of the form {..}, you are having an object response and you should expect an object as you have done i.e, Call<YourObject>
If you are having json response of the form [..], you are having an array response and you should expect an array i.e, Call<List<YourObject>>
In your case, i hope its an array(second case), So make changes as per the above answer done by #Guillodacosta
First don't forget to add the internet permission in your manifest file
<uses-permission android:name="android.permission.INTERNET" />
Second try this
Picasso.with(mContext).load(list.getMovie_image()).into(holder.imgMovie);