i am working in PopularMovies Project that i used the movies DB
and i was successful fetch the data from the API but when i click the item in RecyclerView it doesn't work.
I have followed the tutorial for udacity
PopularMovies-issue
there is the link of the source code
and this is the adapter
What should I do?
package com.popmov.popmov.popmov
import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import com.popmov.popmov.popmov.data.Movie;
import com.squareup.picasso.Picasso;
class MoviesAdapter extends RecyclerView.Adapter<MoviesAdapter.MoviesViewHolder> {
private final Context context;
private Movie[] moviesData;
public static final String BASE_URL_IMAGE = "http://image.tmdb.org/t/p/w342";
private final MoviesAdapterOnClickHandler moviesAdapterOnClickHandler;
public interface MoviesAdapterOnClickHandler {
void onItemClickListener(int id, String title, String imageUrl, String synopsis, double rating, String releaseDate);
}
public MoviesAdapter(MoviesAdapterOnClickHandler clickHandler,Context context) {
moviesAdapterOnClickHandler = clickHandler;
this.context=context;
}
public class MoviesViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public final ImageView imageView;
public MoviesViewHolder(View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.image_view_movie);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
int adapterPosition = getAdapterPosition();
int id = moviesData[adapterPosition].getId();
String title = moviesData[adapterPosition].getTitle();
String imageUrl = moviesData[adapterPosition].getImageUrl();
String synopsis = moviesData[adapterPosition].getSynopsis();
double rating = moviesData[adapterPosition].getRating();
String releaseDate = moviesData[adapterPosition].getReleaseDate();
moviesAdapterOnClickHandler.onItemClickListener(id, title, imageUrl, synopsis, rating, releaseDate);
}
}
#Override
public MoviesViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
Context context = viewGroup.getContext();
int layoutForItem = R.layout.movies_list_item;
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(layoutForItem, viewGroup, false);
return new MoviesViewHolder(view);
}
#Override
public void onBindViewHolder(MoviesViewHolder holder, int position) {
String path = BASE_URL_IMAGE + moviesData[position].getImageUrl();
Picasso.get()
.load(path)
.placeholder(R.mipmap.poster)
.into(holder.imageView);
}
#Override
public int getItemCount() {
if (null == moviesData) {
return 0;
} else {
return moviesData.length;
}
}
}
In your movies_list_item.xml make ImageView clickable to false
android:clickable="false"
As your ImageView clickable property was set to true so your ImageView are absorbing the ClickListener
Related
I am retrieving Cloud Firesstore data in (Modelo) and I'm trying to pass these variables from the constructor "Modelo" to fragment (DescDenuncia) using a adapter (Adapter) but to do that I need to get the context to pass using the .getSupportFragmentManager() I've already tried use "AppCompatActivity activity = (AppCompatActivity) view.getContext();", but my app crashes every time, and is necessary to use a Context in the getSupportFragmentManager() to it works. I truly don't know what I should do, please someone answer me.
Modelo
package com.example.safe;
public class Modelo {
String empresa, area, setor, tipo, risco, img, data, prazo, desc, resolucao, ID;
public Modelo() {
}
public Modelo(String empresa, String area, String setor, String tipo, String risco, String img, String data, String prazo, String desc, String resolucao, String ID) {
this.empresa = empresa;
this.area = area;
this.setor = setor;
this.tipo = tipo;
this.risco = risco;
this.img = img;
this.data = data;
this.prazo = prazo;
this.desc = desc;
this.resolucao = resolucao;
this.ID = ID;
}
public String getEmpresa() {
return empresa;
}
public void setEmpresa(String empresa) {
this.empresa = empresa;
}
public String getArea() {
return area;
}
public void setArea(String area) {
this.area = area;
}
public String getSetor() {
return setor;
}
public void setSetor(String setor) {
this.setor = setor;
}
public String getTipo() {
return tipo;
}
public void setTipo(String tipo) {
this.tipo = tipo;
}
public String getRisco() {
return risco;
}
public void setRisco(String risco) {
this.risco = risco;
}
public String getImg() {
return img;
}
public void setImg(String img) {
this.img = img;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public String getPrazo() {
return prazo;
}
public void setPrazo(String prazo) {
this.prazo = prazo;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getResolucao() {
return resolucao;
}
public void setResolucao(String resolucao) {
this.resolucao = resolucao;
}
public String getID() {
return ID;
}
public void setID(String ID) {
this.ID = ID;
}
}
Adapter
package com.example.safe;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.firebase.ui.firestore.FirestoreRecyclerAdapter;
import com.firebase.ui.firestore.FirestoreRecyclerOptions;
//Vinculando o reclycler view ao firestore
public class Adapter extends FirestoreRecyclerAdapter<Modelo, Adapter.myviewholder> {
public Adapter(#NonNull FirestoreRecyclerOptions<Modelo> options) {
super(options);
}
//Sobrescrevendo o método
#Override
protected void onBindViewHolder(#NonNull myviewholder holder, int position, #NonNull final Modelo model) {
//Associando os itens do atalho ocorrências
holder.IDText.setText(model.getID());
holder.tipoText.setText(model.getTipo());
Glide.with(holder.img1.getContext()).load(model.getImg()).into(holder.img1);
///////////////////////////////////////THE PROBLEM IS HERE///////////////////////////////////////////
holder.RLAYOUT.setOnClickListener(new View. OnClickListener() {
#Override
public void onClick(View view) {
.getSupportFragmentManager().beginTransaction().replace(R.id.frameLayout, new DescDenuncia(
model.getEmpresa(),
model.getArea(),
model.getSetor(),
model.getTipo(),
model.getRisco(),
model.getImg(),
model.getData(),
model.getPrazo(),
model.getDesc(),
model.getResolucao(),
model.getID()
)).addToBackStack(null).commit();
}
});
//////////////////////////////////////////////////////////////////////////////////////////////////////
}
#NonNull
#Override
public myviewholder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.modelo_atalho_ocorrencias, parent, false);
return new myviewholder(view);
}
//Coneção do reclycler view com essa classe
public class myviewholder extends RecyclerView.ViewHolder {
//Variáveis do modelo atalho
TextView tipoText, IDText;
RelativeLayout RLAYOUT;
ImageView img1;
private Context context;
//Associando os objetos ao modelo atalho
public myviewholder(#NonNull View itemView) {
super(itemView);
tipoText = itemView.findViewById(R.id.txtTipo);
img1 = itemView.findViewById(R.id.imageView);
IDText = itemView.findViewById(R.id.txtModeloID);
RLAYOUT = itemView.findViewById(R.id.RLlayout);
}
}
}
Add a Context parameter in your myviewholder constructor that will accept an additional context argument
public class myviewholder extends RecyclerView.ViewHolder {
private Context context;
...
...
public myviewholder(#NonNull View itemView, Context context) {
super(itemView);
this.context = context
}
and in your onCreateViewHolder simply call parent.getContext() and pass it to the construction of the view holder.
public myviewholder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.modelo_atalho_ocorrencias, parent, false);
return new myviewholder(view, parent.getContext()); // add <- this
Also please practice naming classes with Uppercase Initials
I want the onclicklistener method to open the activity which is related to the object:entidad1, entidad2 or entidad3.
The OnRecipe method on the MainActivity.java which i want it to make that if the entidad1 appears it takes me to x activity, and if entidad2 appears to y activity and so, any idea of how to do it, because now it takes me all the time to the activity of the entidad1. I guess it must be related with using priority to decide which to open instead of the position.
That's my Adapter:
package com.test.platos_4;
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.RatingBar;
import android.widget.TextView;
import java.util.List;
public class Adaptador2 extends RecyclerView.Adapter<Adaptador2.ViewHolder>
{
private List<Entidad2> listItems;
private OnRecipeListener mOnRecipeListener;
public Adaptador2(List<Entidad2> listItems, OnRecipeListener onRecipeListener) {
this.listItems = listItems;
this.mOnRecipeListener = onRecipeListener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.elemento_lista2, parent, false);
return new ViewHolder(view, mOnRecipeListener);
}
#Override
public void onBindViewHolder(ViewHolder viewholder, int position) {
int resource = listItems.get(position).getImgFoto();
String title = listItems.get(position).getTitulo();
String time = listItems.get(position).getTiempo();
int barra = listItems.get(position).getRating();
//int fondo = listItems.get(position).getColorfondo();
viewholder.setData(resource, title, time, barra);
// por si necesito color de fondo viewholder.setData(resource, title, time, barra, fondo);
}
#Override
public int getItemCount() {
return listItems.size();
}
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private ImageView imgFoto;
private TextView titulo;
private TextView tiempo;
private RatingBar ratingBar;
//private ImageView colorfondo;
OnRecipeListener onRecipeListener;
public ViewHolder(View itemView, OnRecipeListener onRecipeListener) {
super(itemView);
imgFoto = itemView.findViewById(R.id.imgFoto);
titulo = itemView.findViewById(R.id.tvTitulo);
tiempo = itemView.findViewById(R.id.tvTiempo);
ratingBar = itemView.findViewById(R.id.ratingBarVerd);
//colorfondo = itemView.findViewById(R.id.colorfondo);
this.onRecipeListener = onRecipeListener;
itemView.setOnClickListener(this);
}
//por si necesito color de fondo private void setData(int resource, String title, String time, int barra, int fondo){
private void setData(int resource, String title, String time, int barra){
imgFoto.setImageResource(resource);
titulo.setText(title);
tiempo.setText(time);
ratingBar.setRating(barra);
//colorfondo.setImageResource(fondo);
}
#Override
public void onClick(View v) {
onRecipeListener.OnRecipe(getAdapterPosition());
}
}
public interface OnRecipeListener{
void OnRecipe(int priority);
}
}
And that is the MainActivity.java:
public class Comida extends AppCompatActivity implements Adaptador2.OnRecipeListener {
private RecyclerView recyclerView1;
List<Entidad2> listItems;
Adaptador2 adaptor;
private Entidad2 entidad1,entidad2,entidad3;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_comida);
recyclerView1 = findViewById(R.id.lv_1);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView1.setLayoutManager(layoutManager);
listItems = new ArrayList<>();
entidad1 = new Entidad2(R.drawable.calabacines_3, "Solomillo a la plancha", " 10 min.", 4, 20);
entidad2 = new Entidad2(R.drawable.patatas_deluxe_especiadas_70523_300_150, "Entrecot", " 15 min.", 2, 50);
entidad3 = new Entidad2(R.drawable.tomate, "Hamburguesa", " 2 min.", 5, 100);
listItems.add(entidad1);
listItems.add(entidad2);
listItems.add(entidad3);
adaptor = new Adaptador2(listItems, this);
recyclerView1.setAdapter(adaptor);
adaptor.notifyDataSetChanged();
pickEntidad();
}
#Override
public void OnRecipe(int priority) {
if (priority == 20) {
Intent in = new Intent(this, Solomillo.class);
startActivity(in);
}
if (priority == 50) {
Intent in = new Intent(this, Entrecot.class);
startActivity(in);
}
if (priority == 100) {
Intent in = new Intent(this, Hamburguesa.class);
startActivity(in);
}
}
private void pickEntidad(){
final int random = new Random().nextInt(101);
int priority1 = entidad1.getPriority();
int priority2 = entidad2.getPriority();
int priority3 = entidad3.getPriority();
listItems.clear();
if(random < priority1){
listItems.add(entidad1);
}else if(random < priority2){
listItems.add(entidad2);
}else if (random <= priority3){
listItems.add(entidad3);
}
adaptor.notifyDataSetChanged();
}
}
And the Entidad.java:
public class Entidad2 {
private int imgFoto;
private String titulo;
private String tiempo;
private int ratingBar;
private int priority;
private int d;
public Entidad2(int imgFoto, String titulo, String tiempo, int ratingBar, int priority) {
this.imgFoto = imgFoto;
this.titulo = titulo;
this.tiempo = tiempo;
this.ratingBar = ratingBar;
this.priority = priority;
}
public int getImgFoto() {
return imgFoto;
}
public String getTitulo() {
return titulo;
}
public String getTiempo() {
return tiempo;
}
public int getRating() { return ratingBar; }
public int getPriority() {
return priority;
}
}
Please help me to solve the problem and if you need more information just tell me i will post it.
You are trying to differentiate your items by Priority but you are passing item position from your adapter to your activity. You need to pass clicked item's Priority instead of its position. I have changed your Adaptor2 class
package com.test.platos_4;
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.RatingBar;
import android.widget.TextView;
import java.util.List;
public class Adaptador2 extends RecyclerView.Adapter<Adaptador2.ViewHolder>
{
private List<Entidad2> listItems;
private OnRecipeListener mOnRecipeListener;
public Adaptador2(List<Entidad2> listItems, OnRecipeListener onRecipeListener) {
this.listItems = listItems;
this.mOnRecipeListener = onRecipeListener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.elemento_lista2, parent, false);
return new ViewHolder(view, mOnRecipeListener);
}
#Override
public void onBindViewHolder(ViewHolder viewholder, int position) {
Entidad2 entidad = listItems.get(position);
int resource = entidad.getImgFoto();
String title = entidad.getTitulo();
String time = entidad.getTiempo();
int barra = entidad.getRating();
final int priority = entidad.getPriority();
//int fondo = listItems.get(position).getColorfondo();
viewholder.setData(resource, title, time, barra);
//You can pass the clicked item's priority back to your activity like this
viewholder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mOnRecipeListener.OnRecipe(priority);
}
});
// por si necesito color de fondo viewholder.setData(resource, title, time, barra, fondo);
}
#Override
public int getItemCount() {
return listItems.size();
}
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private ImageView imgFoto;
private TextView titulo;
private TextView tiempo;
private RatingBar ratingBar;
//private ImageView colorfondo;
OnRecipeListener onRecipeListener;
public ViewHolder(View itemView, OnRecipeListener onRecipeListener) {
super(itemView);
imgFoto = itemView.findViewById(R.id.imgFoto);
titulo = itemView.findViewById(R.id.tvTitulo);
tiempo = itemView.findViewById(R.id.tvTiempo);
ratingBar = itemView.findViewById(R.id.ratingBarVerd);
//colorfondo = itemView.findViewById(R.id.colorfondo);
//This is useless
//this.onRecipeListener = onRecipeListener;
}
//por si necesito color de fondo private void setData(int resource, String title, String time, int barra, int fondo){
private void setData(int resource, String title, String time, int barra){
imgFoto.setImageResource(resource);
titulo.setText(title);
tiempo.setText(time);
ratingBar.setRating(barra);
//colorfondo.setImageResource(fondo);
}
#Override
public void onClick(View v) {
}
}
public interface OnRecipeListener{
void OnRecipe(int priority);
}
}
Update: One of the problems is solved: Now updateList is resolved, the problem was that I defined mAdapter as RecyclerView.Adapter instead of MyAdapter. But now even though I am getting data, nothing shows up on the list, it's empty
--------------------ORIGINAL POST--------------------
I want to update my RecyclerView using DiffUtil to prevent duplicates.
I have 4 classes: The User class, the Activity class where I set data, the Adapter class and the DiffUtil class. I am not sure I combine all these 4 correctly.
This is the User class:
public class User {
private String mUserId;
private Uri mImageUrl;
public User(String userId, String imageUrl) {
mUserId = userId;
mImageUrl = Uri.parse(imageUrl);
}
public String getUserId() {
return mUserId;
}
public Uri getImageUrl() {
return mImageUrl;
}
}
This is how I set data dynamically (I keep getting new Json arrays from the server containing user id's to be displayed, then I set the user image from Firebase storage): (It's a function invoked by an onClick listener:)
This is the method call from the fragment:
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
updateUsersList();
}
});
This is the function:
private void updateUsersList() {
#Override
public void onResponse(JSONArray response) { // the JSON ARRAY response of user ids ["uid1", "uid334", "uid1123"]
myDataset.clear(); // clear dataset to prevent duplicates
for (int i = 0; i < response.length(); i++) {
try {
String userKey = response.get(i).toString(); // the currently iterated user id
final DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference userKeyRef = rootRef.child("users").child(userKey); // reference to currently iterated user
ValueEventListener listener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
myDataset.add(new User(dataSnapshot.getKey(), dataSnapshot.child("imageUrl").getValue().toString())); //add new user: id and image url
mAdapter.updateList(myDataset); // cannot resolve this method, why?
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.d(TAG, databaseError.getMessage());
}
};
userKeyRef.addListenerForSingleValueEvent(listener);
}
catch (JSONException e) { Log.d(TAG, "message " + e); }
}
}
This is how my DiffUtil class looks like:
public class MyDiffUtilCallBack extends DiffUtil.Callback{
ArrayList<User> oldUsers;
ArrayList<User> newUsers;
public MyDiffUtilCallBack(ArrayList<User> newUsers, ArrayList<User> oldUsers) {
this.newUsers = newUsers;
this.oldUsers = oldUsers;
}
#Override
public int getOldListSize() {
return oldUsers.size();
}
#Override
public int getNewListSize() {
return newUsers.size();
}
#Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return oldUsers.get(oldItemPosition).getUserId().equals( newUsers.get(newItemPosition).getUserId());
}
#Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return oldUsers.get(oldItemPosition).equals(newUsers.get(newItemPosition));
}
#Nullable
#Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
//you can return particular field for changed item.
return super.getChangePayload(oldItemPosition, newItemPosition);
}
}
And this is my adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private ArrayList<User> mDataset;
private MyViewHolder myHolder;
private User user;
public static class MyViewHolder extends RecyclerView.ViewHolder {
public TextView singleItemTextView;
public ImageView singleItemImage;
public View layout;
public ConstraintLayout constraintLayout;
public MyViewHolder(View v) {
super(v);
layout = v;
singleItemImage = (ImageView) v.findViewById(R.id.icon);
singleItemTextView = (TextView) v.findViewById(R.id.singleitemtv);
constraintLayout = (ConstraintLayout) v.findViewById(R.id.nbConstraintLayout);
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter(ArrayList<User> myDataset) {
mDataset = myDataset;
}
// Create new views (invoked by the layout manager)
#Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.nb_image_view, parent, false);
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
myHolder = holder;
user = mDataset.get(position);
Uri userImage = user.getImageUrl();
myHolder.singleItemTextView.setText(user.getUserId());
Glide.with(myHolder.itemView.getContext() /* context */)
.load(userImage)
.into(myHolder.singleItemImage);
myHolder.constraintLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(v.getContext(), DisplayUserActivity.class);
context.startActivity(intent);
}
});
}
public void updateList(ArrayList<User> newList) {
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new MyDiffUtilCallBack(this.mDataset, newList));
diffResult.dispatchUpdatesTo(this);
}
}
I am not sure I combine all the classes correctly (my first time using DiffUtil), and I also get cannot resolve method updateList(?)
What am I doing wrong?
This is how I define mAdapter in my Fragment:
public class MyFragment extends Fragment {
private ArrayList<User> myDataset;
private RecyclerView.Adapter mAdapter;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
rootView = inflater.inflate(R.layout.fragment_lks, container, false);
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.my_recycler_view);
myDataset = new ArrayList<User>();
mAdapter = new MyAdapter(myDataset);
The problem comes from definition of mAdapter. You defined it as RecyclerView.Adapter which is super class of your MyAdapter and it does not contain updateList(). You should change it as following:
private MyAdapter mAdapter;
Updated 1/13/2019:
I've revised your adapter with AsyncListDiffer which calculates the diffrence asynchronously then applies it to the adapter.
MyAdapter.java
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.constraint.ConstraintLayout;
import android.support.v7.recyclerview.extensions.AsyncListDiffer;
import android.support.v7.util.DiffUtil;
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;
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private AsyncListDiffer<User> mAsyncListDiffer;
public static class MyViewHolder extends RecyclerView.ViewHolder {
public TextView singleItemTextView;
public ImageView singleItemImage;
public View layout;
public ConstraintLayout constraintLayout;
public MyViewHolder(View v) {
super(v);
layout = v;
singleItemImage = (ImageView) v.findViewById(R.id.icon);
singleItemTextView = (TextView) v.findViewById(R.id.singleitemtv);
constraintLayout = (ConstraintLayout) v.findViewById(R.id.nbConstraintLayout);
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter() {
DiffUtil.ItemCallback<User> diffUtilCallback = new DiffUtil.ItemCallback<User>() {
#Override
public boolean areItemsTheSame(#NonNull User newUser, #NonNull User oldUser) {
return newUser.getUserId().equals(oldUser.getUserId());
}
#Override
public boolean areContentsTheSame(#NonNull User newUser, #NonNull User oldUser) {
return newUser.equals(oldUser);
}
};
mAsyncListDiffer = new AsyncListDiffer<>(this, diffUtilCallback);
}
// Create new views (invoked by the layout manager)
#Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.nb_image_view, parent, false);
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
User user = mAsyncListDiffer.getCurrentList().get(position);
Uri userImage = user.getImageUrl();
holder.singleItemTextView.setText(user.getUserId());
Glide.with(holder.itemView.getContext() /* context */)
.load(userImage)
.into(holder.singleItemImage);
holder.constraintLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(v.getContext(), DisplayUserActivity.class);
context.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return mAsyncListDiffer.getCurrentList().size();
}
public void updateList(List<User> newList) {
mAsyncListDiffer.submitList(newList);
}
}
User.java
public class User {
private String mUserId;
private Uri mImageUrl;
public User(String userId, String imageUrl) {
mUserId = userId;
mImageUrl = Uri.parse(imageUrl);
}
public String getUserId() {
return mUserId;
}
public Uri getImageUrl() {
return mImageUrl;
}
#Override
public boolean equals(Object other) {
if (other instanceof User) {
User user = (User) other;
return mUserId.equals(user.getUserId()) && mImageUrl.equals(user.getImageUrl());
} else {
return false;
}
}
}
In addition to #aminography's answer, I suggest you to use ListAdapter, a RecyclerView.Adapter implementation that makes it easier to update you RecyclerView with the correct animations. This class is included in the recyclerview support library.
Below is an example of usage based on your use case:
public class MyAdapter extends ListAdapter<User, UserViewHolder> {
public MyAdapter() {
super(new UserDiffCallback());
}
public UserViewHolder onCreateViewHolder(int position, int viewType) { ... }
public void onBindViewHolder(UserViewModel holder, int position) {
User userAtPosition = getItem(position); // getItem is a protected method from ListAdapter
// Bind user data to your holder...
}
}
public class UserDiffCallback extends DiffUtil.ItemCallback<User> {
#Override
public boolean areItemsTheSame(#NonNull User oldUser, #NonNull User newUser) {
return oldUser.getUserId().equals(newUser.getUserId());
}
#Override
public boolean areContentsTheSame(#NonNull User oldUser, #NonNull User newUser) {
// No need to check the equality for all User fields ; just check the equality for fields that change the display of your item.
// In your case, both impact the display.
return oldUser.getUserId().equals(newUser.getUserId())
&& (oldUser.getImageUrl() == null) ? newUser.getImageUrl() == null : oldUser.getImageUrl().equals(newUser.getImageUrl());
}
}
Then, when you need to update the list with new users, call myAdapter.submitList(newList). Juste like with AsyncListDiffer, the diff between the two list is calculated on a background Thread.
Modify your method:
public void updateList(ArrayList<User> newList) {
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new MyDiffUtilCallBack(this.mDataset, newList));
this.mDataSet.clear()
this.mDataSet.addAll(newList)
diffResult.dispatchUpdatesTo(this);
}
Looks like
public void onBindViewHolder(#NonNull ViewHolder holder, int position, #NonNull List<Object> payloads) not implemented
Implement this to use DiffUtils properly, as this method will be called for the changes, and based on the payload you can update your recyclerview items instead of calling notifyDataSetChanged()
Long post, thanks for taking the time to look through this
I have been struggling with this issue for a while now. I have an SQLiteDatabase which stores a shoe's brand name, name of shoe, and a byte image of the shoe. I was able to load all of the information to a recyclerview via a content provider. But it seems when I try and load the information to the details of each item in the recycler view, the details activity is filled with the wrong information, it returns a completely different entry other than the one I selected.
The solutions I've seen in many youtube videos have contained me creating ArrayLists to store the information, but I find that very hard to do using my SQLiteDatabase information.
Here are the classes I have...
My Closet.java class(with my getter methods)
package com.example.android.myshoecloset;
import android.database.Cursor;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by man on 12/21/2017.
*/
public class Closet
{
private static final String TAG = Closet.class.getSimpleName();
//Brand name
private final String brandName;
//Shoe Name
private final String shoeName;
//Image of the shoe
private final String shoeImage;
public Closet(String brandName, String shoeName, String shoeImage)
{
this.brandName = brandName;
this.shoeName = shoeName;
this.shoeImage = shoeImage;
}
public Closet(Cursor cursor)
{
this.brandName = null;
this.shoeName = null;
this.shoeImage = null;
}
public String getShoeImageName()
{
return shoeImage;
}
public String getBrandName()
{
return brandName;
}
public String getShoeName()
{
return shoeName;
}
}
My Adapter Class(CustomAdapter.java)
package com.example.android.myshoecloset.data;
/*Assume appropriate imports*/
/**
* Created by man on 11/23/2017.
*/
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.TaskHolder> {
private Cursor mCursor;
private Context mContext;
public static String shoeName;
public static String brandName;
public static byte[] byteArray;
public CustomAdapter(Context mContext) {
this.mContext = mContext;
}
public CustomAdapter()
{
mContext = null;
}
/* ViewHolder for each task item */
public class TaskHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView shoeBrandName;
public TextView shoeName;
public ImageView shoeImage;
public LinearLayout linearLayout;
public TaskHolder(final View itemView) {
super(itemView);
itemView.setOnClickListener(this);
shoeBrandName = (TextView) itemView.findViewById(R.id.textBrandName);
shoeImage = (ImageView) itemView.findViewById(R.id.shoeImage);
shoeName = (TextView) itemView.findViewById(R.id.textShoeName);
linearLayout = (LinearLayout) itemView.findViewById(R.id.linear_closet);
}
#Override
public void onClick(View v) {
Intent i = new Intent(v.getContext(), ShoeDetailActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
v.getContext().startActivity(i);
}
}
#Override
public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(mContext);
View itemView = inflater
.inflate(R.layout.text_row_item, parent, false);
return new TaskHolder(itemView);
}
#Override
public void onBindViewHolder(TaskHolder holder, int position) {
int idIndex = mCursor.getColumnIndex(DatabaseContract.ShoeColumns._ID);
int imgValue = mCursor.getColumnIndex(DatabaseContract.ShoeColumns.SHOE_IMAGE);
int shoeBrandName = mCursor.getColumnIndex(DatabaseContract.ShoeColumns.SHOE_BRAND);
int shoeName = mCursor.getColumnIndex(DatabaseContract.ShoeColumns.SHOE_NAME);
mCursor.moveToPosition(position);
final int id = mCursor.getInt(idIndex);
byte[] shoeImg = mCursor.getBlob(imgValue);
String brandNameStr = mCursor.getString(shoeBrandName);
String shoeNameStr = mCursor.getString(shoeName);
Bitmap bmp = BitmapFactory.decodeByteArray(shoeImg, 0, shoeImg.length);
holder.itemView.setTag(id);
holder.shoeImage.setImageBitmap(Bitmap.createScaledBitmap(bmp, 100, 100, false));
holder.shoeBrandName.setText(brandNameStr);
holder.shoeName.setText(shoeNameStr);
holder.getAdapterPosition();
CustomAdapter.shoeName = shoeNameStr;
CustomAdapter.brandName = brandNameStr;
CustomAdapter.byteArray = mCursor.getBlob(imgValue);
}
#Override
public int getItemCount() {
return (mCursor != null) ? mCursor.getCount() : 0;
}
public void swapCursor(Cursor cursor) {
if (mCursor != null) {
mCursor.close();
}
mCursor = cursor;
notifyDataSetChanged();
}
}
My ClosetFragment, which loads the information on a Loader and to the fragments recyclerview.
package com.example.android.myshoecloset;
/*Assume appropriate imports*/
public class ClosetFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
private static final String TAG = "ClosetFragment";
protected RecyclerView mRecyclerView;
protected CustomAdapter mAdapter;
private static final int CUSTOM_LOADER_ID = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_closet, container, false);
rootView.setTag(TAG);
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerView);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity().getApplicationContext()));
mAdapter = new CustomAdapter(getActivity().getApplicationContext());
mRecyclerView.setAdapter(mAdapter);
getActivity().getSupportLoaderManager().initLoader(CUSTOM_LOADER_ID, null, this);
return rootView;
}
#Override
public void onResume()
{
super.onResume();
getActivity().getSupportLoaderManager().restartLoader(CUSTOM_LOADER_ID, null, this); }
#Override
public Loader<Cursor> onCreateLoader(int id, final Bundle loaderArgs) {
return new AsyncTaskLoader<Cursor>(getActivity().getApplicationContext()) {
Cursor mTaskData = null;
#Override
protected void onStartLoading()
{
if(mTaskData != null)
{
deliverResult(mTaskData);
}
else
{
forceLoad();
}
}
public Cursor loadInBackground()
{
try
{
return getActivity().getApplicationContext().getContentResolver().query(DatabaseContract.CONTENT_URI,
null,
null,
null,
null);
} catch(Exception e)
{
Log.e("", "Failed to asynchronously load data.");
e.printStackTrace();
return null;
}
}
public void deliverResult(Cursor data)
{
mTaskData = data;
super.deliverResult(data);
}
};
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data)
{
mAdapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader)
{
mAdapter.swapCursor(null);
}
}
Finally here is my DetailsActivity(when the recyclerview item is clicked this is where the click will take you)
package com.example.android.myshoecloset;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.android.myshoecloset.data.CustomAdapter;
public class ShoeDetailActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shoe_detail);
ImageView imgShoe = (ImageView) findViewById(R.id.shoeImgDetails);
TextView brandShoe = (TextView) findViewById(R.id.shoeBrandDetails);
TextView nameShoe = (TextView) findViewById(R.id.shoeNameDetails);
byte[] b = getIntent().getByteArrayExtra("ImageBit");
Bitmap bmp = BitmapFactory.decodeByteArray(b, 0, b.length);
imgShoe.setImageBitmap(bmp);
brandShoe.setText(getIntent().getStringExtra("ShoeName"));
nameShoe.setText(getIntent().getStringExtra("BrandName"));
}
}
Thanks!
As far as I can tell, the relevant pieces of your code are the onClick method in your view holder:
#Override
public void onClick(View v) {
Intent i = new Intent(v.getContext(), ShoeDetailActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
v.getContext().startActivity(i);
}
And the onCreate method in your details activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
byte[] b = getIntent().getByteArrayExtra("ImageBit");
...
brandShoe.setText(getIntent().getStringExtra("ShoeName"));
nameShoe.setText(getIntent().getStringExtra("BrandName"));
}
In the details activity, you're trying to pull information out of the intent extras... but you've never provided that information in the first place. Making this information available to your view holder will require re-thinking how you've implemented the view holder itself.
I believe you should be able to use the viewholder's "adapter position" to get the necessary info out of your cursor at click time. Maybe something like this:
#Override
public void onClick(View v) {
int position = getAdapterPosition();
mCursor.moveToPosition(position);
Intent i = new Intent(v.getContext(), ShoeDetailActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
int imgValue = mCursor.getColumnIndex(DatabaseContract.ShoeColumns.SHOE_IMAGE);
int shoeBrandName = mCursor.getColumnIndex(DatabaseContract.ShoeColumns.SHOE_BRAND);
int shoeName = mCursor.getColumnIndex(DatabaseContract.ShoeColumns.SHOE_NAME);
i.putExtra("ImageBit", mCursor.getBlob(imgValue));
i.putExtra("ShoeName", mCursor.getString(shoeBrandName));
i.putExtra("BrandName", mCursor.getString(shoeName));
v.getContext().startActivity(i);
}
(Note that "ShoeName" and "BrandName" appear to be swapped, but they're also swapped in your details activity so I left them that way.)
If this getAdapterPosition() + mCursor method doesn't work for you, you can always pass the necessary information to the view holder at bind time, and update the onClick() method when binding.
Let me know if this works for you. If it doesn't, we can try to figure it out from here.
I'm building on a tutorial I did in which I created a RecyclerView screen with cards with selectable options. I want one of the selectable options to bring the user to a new activity that has more information & options about the card they selected. My problem is when I try to transfer traits of that specific card to the next SlideViewActivity.java activity I am unable to successfully do so. I tried transforming my list into an array then sending that, but I keep obtaining a null value (which could be due to my syntax for all I know).
Any clarification & guidance would be appreciated, let me know if you would want any of the other code as well.
public class Adapter extends RecyclerView.Adapter<Adapter.MyViewHolder> {
private Context mContext;
private List<Properties> dogList;
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView title, count;
public ImageView thumbnail, overflow;
public MyViewHolder(View view) {
super(view);
title = (TextView) view.findViewById(R.id.title);
count = (TextView) view.findViewById(R.id.count);
thumbnail = (ImageView) view.findViewById(R.id.thumbnail);
overflow = (ImageView) view.findViewById(R.id.overflow);
}
}
public Adapter(Context mContext, List<Properties> dogList) {
this.mContext = mContext;
this.dogList = dogList;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.card, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
Properties dog = dogList.get(position);
holder.title.setText(dog.getName());
// loading dog cover using Glide library
Glide.with(mContext).load(dog.getThumbnail()).into(holder.thumbnail);
holder.overflow.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
showPopupMenu(holder.overflow);
}
});
}
/**
* Showing popup menu when tapping on icon
*/
private void showPopupMenu(View view) {
// inflate menu
PopupMenu popup = new PopupMenu(mContext, view);
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.menu, popup.getMenu());
popup.setOnMenuItemClickListener(new MyMenuItemClickListener());
popup.show();
}
/**
* Click listener for popup menu items
*/
class MyMenuItemClickListener implements PopupMenu.OnMenuItemClickListener {
public MyMenuItemClickListener() {
}
#Override
public boolean onMenuItemClick(MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.action_add_favourite:
Toast.makeText(mContext, "Add to favourite", Toast.LENGTH_SHORT).show();
return true;
case R.id.action_more_info:
Intent slideStart = new Intent(mContext, SlideViewActivity.class);
String[] dogArray = new String[dogList.size()];
slideStart.putExtra("List", dogArray);
Log.e("putting extra", String.valueOf(dogArray[0]));
//TODO:MAKE NAME TRANSFERS WORK
mContext.startActivity(slideStart);
return true;
default:
}
return false;
}
}
Adding Properties.java:
public class Properties {
private String name;
private String info;
private int thumbnail;
public Properties() {
}
public Properties(String name, String info, int thumbnail) {
this.name = name;
this.info = info;
this.thumbnail = thumbnail;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getInfo() {
return info;
}
public void getInfo(String info) {
this.info = info;
}
public int getThumbnail() {
return thumbnail;
}
public void setThumbnail(int thumbnail) {
this.thumbnail = thumbnail;
}
}
You can pass ArrayList<T>, if T is Serializable.
for example:
ArrayList<String> list = new ArrayList<String>();
intent.putExtra("list", list);
use getSerializableExtra to extract data
Your array is dogList.size()-d null values.
String[] dogArray = new String[dogList.size()];
slideStart.putExtra("List", dogArray);
You should see null logged here.
Log.e("putting extra", String.valueOf(dogArray[0]));
It's not clear what type of class you have for Properties, but if it is your class, and not java.util.Properties, you should implement Parcelable on that class, then you'd have
intent.putParcelableArrayList("List", dogList)
(Tip: You should rename that class to Dog to avoid confusion for yourself and others)
But if you are just worried about getting null, case matters. You put a key="List", so make sure you aren't getting key="list" or anything else but "List"
Two ways possible for that::
First
While Sending the list using intent::
Intent slideStart = new Intent(mContext, SlideViewActivity.class);
slideStart.putExtra("List", dogList);
mContext.startActivity(slideStart);
In This just pass the list as is it is ::
But your class Properties should be implements "Serializable"
Now while receiving that intent ::
ArrayList<Properties> dogList =(ArrayList<Properties>) getIntent().getExtras().getSerializable("List");
Second way :: using Library
One library is there for converting string to arraylist & vice versa ,
compile 'com.google.code.gson:gson:2.4'
So, for this while sending list convert that list to string like ::
Type listType = new TypeToken<List<Properties>>() {
}.getType();
String listString=new Gson().toJson(dogList,listType );
pass this simple as string with intent:::
Intent slideStart = new Intent(mContext, SlideViewActivity.class);
slideStart.putExtra("List", listString);
mContext.startActivity(slideStart);
And while getting it back in other activity::
String listString =getIntent().getExtras().getString("List");
Type listType = new TypeToken<List<Properties>>() {}.getType();
List<Properties> list=new Gson().fromJson(listString, listType);
Tell me if need more help..
I have done a similar project I will share my code. Please take a look and if have doubts ping me
DataAdapter.java
package com.example.vishnum.indiavideo;
import android.content.Context;
import android.content.Intent;
import android.provider.MediaStore;
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.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import java.util.ArrayList;
import java.util.List;
public class DataAdapter extends RecyclerView.Adapter<DataAdapter.ViewHolder> {
private Context context;
List<Video_Details> video;
public DataAdapter(List<Video_Details> video, Context context) {
super();
this.context = context;
this.video = video;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.card_row, parent, false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final Video_Details videoDetails = video.get(position);
String url;
final String VideoID;
holder.title.setText(video.get(position).getTitle());
VideoID= video.get(position).getV_id();
url = video.get(position).getThumb();
Glide.with(context)
.load(url)
.override(150,70)
.into(holder.thumb);
//viewHolder.thumb.setText(android.get(i).getVer());
// viewHolder.tv_api_level.setText(android.get(i).getApi());
holder.vm.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(v.getContext(), "You Clicked"+video.get(position).getV_id(), Toast.LENGTH_SHORT).show();
Intent intent = new Intent(v.getContext(),Play_Video.class);
intent.putExtra("VideoId",(video.get(position).getV_id()));
intent.putExtra("Title",(video.get(position).getTitle()));
v.getContext().startActivity(intent);
}
}
);
}
#Override
public int getItemCount() {
return video.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
public TextView title;
public ImageView thumb;
public String videoid;
public View vm;
public ViewHolder(View view) {
super(view);
vm = view;
title = (TextView)view.findViewById(R.id.title);
thumb = (ImageView) view.findViewById(R.id.thumb);
//tv_version = (TextView)view.findViewById(R.id.tv_version);
//tv_api_level = (TextView)view.findViewById(R.id.tv_api_level);
}
}
}
Video_Details.java
package com.example.vishnum.indiavideo;
public class Video_Details {
private String id;
private String v_id;
private String title;
private String thumb;
public String getId() {
return id;
}
public String getV_id() {
return v_id;
}
public String getTitle() {
return title;
}
public String getThumb() {
return (Constants.urlvideo+v_id+"/0.jpg");
}
public void setId(String id) {
this.id = id;
}
public void setV_id(String v_id) {
this.v_id = v_id;
}
public void setTitle(String title) {
this.title = title;
}
public void setThumb(String v_id) {
this.thumb =Constants.urlvideo+v_id+"/0.jpg";
}
}