How to shuffle items from Firebase Database?
Let's say my DB contains 4 items [1,2,3,4]. In my RecyclerView, I want to display shuffled, something like [2,3,1,4]. How can I do that?
This is the code in my Fragment. So it works retrieving data but I need to shuffle it before displaying.
RecyclerView mRecyclerView;
FirebaseDatabase mFirebaseDatabase;
DatabaseReference mRef;
#Override
public View onCreateView(#NotNull LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstanceState) {
if (rootView == null) {
rootView = inflater.inflate(R.layout.fragment_store, viewGroup, false);
setHasOptionsMenu(false);
mRecyclerView = rootView.findViewById(R.id.recycler_view);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 3));
mFirebaseDatabase = FirebaseDatabase.getInstance();
mRef = mFirebaseDatabase.getReference("Store");
}
return rootView;
}
For retrieving data from Firebase Database I am using the following code:
#Override
public void onStart() {
super.onStart();
FirebaseRecyclerOptions<Store2> options =
new FirebaseRecyclerOptions.Builder<Store2>()
.setQuery(mRef, Store2.class)
.build();
FirebaseRecyclerAdapter<Store2, StoreHolder> firebaseRecyclerAdapter =
new FirebaseRecyclerAdapter<Store2, StoreHolder>(options) {
#NonNull
#Override
public StoreHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.store_gridview, parent, false);
return new StoreHolder(view);
}
#Override
protected void onBindViewHolder(#NonNull final StoreHolder holder, int position, #NonNull final Store2 model) {
holder.setDetails(getContext(), model.getImage(), model.getName(), model.getPrice());
}
};
firebaseRecyclerAdapter.startListening();
mRecyclerView.setAdapter(firebaseRecyclerAdapter);
}
There are no option available for shuffling data in firebase.
But you can do it locally .
Pass your list in this.
Collections.shuffle(arrayList);
When you are using the following reference:
mRef = mFirebaseDatabase.getReference("Store");
It means that you are trying to display all children that exist under Store node. If under this node you store children that are added using the push() method, the default ordering is chronological, since every pushed id contains a time component. If you want to change this behaviour, you need to get all the items and shuffle them manually.
As #DougStevenson mentioned in his comment, you have to use the same reference, get all items, add them to a list, shuffle them and then use a ListAdapter for that.
Please also note, that when you pass a DocumentReference or a Query object to the setQuery() method, where is no further mechanism that can help you shuffle your items. Unfortunately, you'll lose the benefits of the Firebase-UI library but you'll get your work done.
As #DougStevenson and #frankenstein mentioned, I added all items into a list, then I was able the shuffle it using Collections.shuffle(arrayList);
I couldn't do it using FirebaseRecyclerAdapter, and I needed a custom Adapter for my RecyclerView.ViewHolder.
What is the downside of not using FirebaseRecyclerAdapter? Or is it any? Maybe someone with experience can answer this question.
My fragment:
private View rootView;
private RecyclerView recyclerView;
private StoreAdapter storeAdapter;
private ArrayList<Store> arrayStore;
#Override
public View onCreateView(#NotNull LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstanceState) {
if (rootView == null) {
rootView = inflater.inflate(R.layout.fragment_store, viewGroup, false);
setHasOptionsMenu(false);
recyclerView = rootView.findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 3));
DatabaseReference reference = FirebaseDatabase.getInstance().getReference().child("Data");
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
arrayStore = new ArrayList<>();
for(DataSnapshot snapshot: dataSnapshot.getChildren()) {
Store store = snapshot.getValue(Store.class);
arrayStore.add(store);
}
storeAdapter = new StoreAdapter(getActivity() ,arrayStore);
recyclerView.setAdapter(storeAdapter);
Collections.shuffle(arrayStore, new Random(3));
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
return rootView;
}
My adapter:
private final Context context;
private ArrayList<Store> arrayStore;
public StoreAdapter(Context context, ArrayList<Store> arrayStore) {
this.context = context;
this.arrayStore = arrayStore;
}
#NonNull
#Override
public StoreAdapter.GameViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
return new StoreAdapter.GameViewHolder(LayoutInflater.from(context).inflate(
R.layout.store_gridview, viewGroup, false));
}
#Override
public void onBindViewHolder(#NonNull final StoreAdapter.GameViewHolder viewHolder, final int position) {
setColor(viewHolder);
viewHolder.itemName.setText(arrayStore.get(position).getName());
Glide.with(viewHolder.view.getContext())
.load(arrayStore.get(position).getImage())
.thumbnail(0.75f)
.into(viewHolder.itemImage);
viewHolder.view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
sendData(viewHolder, position);
}
});
}
#Override
public int getItemCount() {
return arrayStore.size();
}
class GameViewHolder extends RecyclerView.ViewHolder {
private final View view;
private final ImageView itemImage, itemRarity;
private final TextView itemName;
GameViewHolder(#NonNull View itemView) {
super(itemView);
this.view = itemView;
itemRarity = view.findViewById(R.id.itemRarity);
itemImage = view.findViewById(R.id.itemImage);
itemName = view.findViewById(R.id.itemName);
}
}
private void sendData(#NonNull final StoreAdapter.GameViewHolder viewHolder, int position) {
if (viewHolder.getAdapterPosition() != RecyclerView.NO_POSITION) {
Intent intent = new Intent(context, DetailsActivity.class);
intent.putExtra("name", arrayStore.get(position).getName());
intent.putExtra("rarity", arrayStore.get(position).getRarity());
intent.putExtra("item", arrayStore.get(position).getImage());
intent.putExtra("price", arrayStore.get(position).getPrice());
intent.putExtra("key", "Store");
context.startActivity(intent);
}
}
private void setColor(#NonNull final StoreAdapter.GameViewHolder viewHolder) {
String rarity = arrayStore.get(viewHolder.getAdapterPosition()).getRarity();
if (rarity.contains("legendary")) {
viewHolder.itemRarity.setImageResource(R.drawable.color_legendary_gradient);
} else if (rarity.contains("classic")) {
viewHolder.itemRarity.setImageResource(R.drawable.color_classic_gradient);
} else if (rarity.contains("epic")) {
viewHolder.itemRarity.setImageResource(R.drawable.color_epic_gradient);
} else if (rarity.contains("elite")) {
viewHolder.itemRarity.setImageResource(R.drawable.color_elite_gradient);
} else if (rarity.contains("rare")) {
viewHolder.itemRarity.setImageResource(R.drawable.color_rare_gradient);
} else if (rarity.contains("special")) {
viewHolder.itemRarity.setImageResource(R.drawable.color_special_gradient);
}
}
My bean class:
private String image, name, price, rarity;
public Store() {
}
public String getImage() {
return image;
}
public String getName() {
return name;
}
public String getPrice() {
return price;
}
public String getRarity() {
return rarity;
}
Related
My Recyclerview is filled with a list containing links to images. But the images can change (let's say the user can draw something on them), but the link to the image itself does not change. I need to make it so that when the image changes, it is also drawn again in the recycler.
notifyDataSetChanged() does not work in this case, apparently because it reacts to changes in the sheet, and in fact there are no changes in the sheet itself, the links are the same in it. But the images are changed by links.
Maybe somehow you can push the recycler so that it is updated or draws items again?
My Activity:
public class MyDrawing extends Fragment {
private RecyclerView recyclerView;
private RecyclerViewAdapter recyclerViewAdapter;
private View root;
private TextView text;
public MyDrawing() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
root = inflater.inflate(R.layout.fragment_my_drawing, container, false);
recyclerView = root.findViewById(R.id.recyclerViewMyDrawing);
text = root.findViewById(R.id.text);
List<CacheImageModel> cacheImageModels = getCacheImageByState();
recyclerViewAdapter = new RecyclerViewAdapter(cacheImageModels, getContext());
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
recyclerView.setAdapter(recyclerViewAdapter);
recyclerViewAdapter.notifyDataSetChanged();
return root;
}
My Adapter:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
private List<CacheImageModel> cacheImageModels;
private Context context;
private String imgUrl;
private int imageKey;
private String category;
private String nameImage;
private ProgressBar progressBarItem;
public RecyclerViewAdapter(List<CacheImageModel> cacheImageModels, Context context) {
this.cacheImageModels = cacheImageModels;
this.context = context;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recycler_view_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
imgUrl = cacheImageModels.get(position).getImageCacheUrl();
nameImage = cacheImageModels.get(position).getName();
category = cacheImageModels.get(position).getCategory();
imageKey = cacheImageModels.get(position).getImageKey();
holder.progressBarItem.setVisibility(View.VISIBLE);
holder.relativeLayout.setLayoutParams(new FrameLayout.LayoutParams(MyApp.getScreenWidth(context) / 2,
MyApp.getScreenWidth(context) / 2));
Glide.with(context)
.load(imgUrl)
.listener(new RequestListener<Drawable>() {
#Override
public boolean onLoadFailed(#Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
return false;
}
#Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
holder.progressBarItem.setVisibility(View.GONE);
return false;
}
})
.into(holder.image);
holder.image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int posit = holder.getAdapterPosition();
if (posit < 0 || posit >= cacheImageModels.size()) {
Log.d("POSITION", "Number postition " + posit + " its error");
} else {
String urlImagePosition = cacheImageModels.get(posit).getImageCacheUrl();
String namePosition = cacheImageModels.get(posit).getName();
String categoryPosition = cacheImageModels.get(posit).getCategory();
int imageKeyPosition = cacheImageModels.get(posit).getImageKey();
Uri uri = ContentUris.withAppendedId(MCDataContract.CONTENT_URI, imageKeyPosition);
Intent intent = new Intent(v.getContext(),
ColoringActivity.class);
intent.putExtra("urlImagePosition", urlImagePosition);
intent.putExtra("nameImage", namePosition);
intent.putExtra("keyPosition", imageKeyPosition);
intent.putExtra("categoryPosition", categoryPosition);
intent.setData(uri);
v.getContext().startActivity(intent);
}
}
});
}
#Override
public long getItemId(int position) {
return super.getItemId(position);
}
#Override
public int getItemCount() {
return cacheImageModels.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private ImageView image;
private ProgressBar progressBarItem;
private RelativeLayout relativeLayout;
private CardView cardView;
public ViewHolder(#NonNull View itemView) {
super(itemView);
image = itemView.findViewById(R.id.imageView);
progressBarItem = itemView.findViewById(R.id.progressBarItem);
relativeLayout = itemView.findViewById(R.id.parentItem);
cardView = itemView.findViewById(R.id.cardView);
}
}
}
notifyDataSetChanged() is most likely working as planned. You can set a breakpoint in your view holder binding code to check this.
It is more likely that Glide is pulling the cached versions of the images. You will need a way to get Glide to ignore what's in cache and reload from the URL.
This post addresses the same issue and may be of help to you.
I've been trying to delete data from firebase with an OnClick RecyclerView method, but nothing works. I can't even make my OnClick method works. I have watched videos and tutorials, I've tried so much stuff and nothing helps...
Here is my adapter class:
public class myadapter extends RecyclerView.Adapter<myadapter.myviewholder>{
ArrayList<datamodel> dataholder;
public myadapter(ArrayList<datamodel> dataholder) {
this.dataholder = dataholder;
}
#NonNull
#Override
public myviewholder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list, parent, false);
return new myviewholder(view);
}
#Override
public void onBindViewHolder(#NonNull myviewholder holder, int position) {
Glide.with(holder.itemView.getContext()).load(dataholder.get(position).getImageurl()).into(holder.CircleImg);
holder.header.setText(dataholder.get(position).getHeader());
holder.descr.setText(dataholder.get(position).getDescr());
holder.price.setText(dataholder.get(position).getPrec());
}
#Override
public int getItemCount() {
return dataholder.size();
}
class myviewholder extends RecyclerView.ViewHolder{
CircleImageView CircleImg;
TextView header, descr, price;
public myviewholder(#NonNull View itemView) {
super(itemView);
CircleImg = itemView.findViewById(R.id.circle1);
header = itemView.findViewById(R.id.txtheader1);
descr = itemView.findViewById(R.id.txtdescr1);
price = itemView.findViewById(R.id.txtprecio1);
}
}
And my fragment (the one that have the recyclerView):
public class articulos extends Fragment {
private FirebaseUser firebaseUser;
private String uid;
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
private DatabaseReference myRef;
private Context mContext;
private myadapter myadapter;
RecyclerView recyclerView;
ArrayList<datamodel> dataholder;
public articulos() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* #param param1 Parameter 1.
* #param param2 Parameter 2.
* #return A new instance of fragment articulos.
*/
// TODO: Rename and change types and number of parameters
public static articulos newInstance(String param1, String param2) {
articulos fragment = new articulos();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_articulos, container, false);
recyclerView = view.findViewById(R.id.rv);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
myRef = FirebaseDatabase.getInstance().getReference();
dataholder= new ArrayList<>();
getDataFromFirebase();
//datamodel ob1 = new datamodel("Esta nueva", "5000MXN", "wwsad.adasd", "asdasdsa");
// dataholder.add(ob1);
//recyclerView.setAdapter(new myadapter(dataholder));
return view;
}
private void getDataFromFirebase() {
firebaseUser = FirebaseAuth.getInstance().getCurrentUser();
uid = firebaseUser.getUid();
Query query = myRef.child("Usuarios").child(uid).child("articulos");
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
ClearAll();
for(DataSnapshot snapshot1 : snapshot.getChildren()){
datamodel datamodel = new datamodel();
datamodel.setImageurl(snapshot1.child("imageurl").getValue(String.class));
datamodel.setDescr(snapshot1.child("descripcion").getValue(String.class));
datamodel.setHeader(snapshot1.child("nombre").getValue(String.class));
datamodel.setPrec(snapshot1.child("precio").getValue(String.class));
dataholder.add(datamodel);
}
myadapter = new myadapter(dataholder);
recyclerView.setAdapter(new myadapter(dataholder));
myadapter.notifyDataSetChanged();
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
private void ClearAll(){
if(dataholder != null){
dataholder.clear();
if(myadapter != null){
myadapter.notifyDataSetChanged();
}
}
dataholder = new ArrayList<>();
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button mbtnNuevoArticulo = view.findViewById(R.id.btnNuevoArticulo);
mbtnNuevoArticulo.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Navigation.findNavController(v).navigate(R.id.agregarart);
}
});
}
If anyone can tell me what to do and how to do it, please.. I need it so bad
Try set onclicklistener on viewholder class
class myviewholder extends RecyclerView.ViewHolder implements View.OnClickListener {
CircleImageView CircleImg;
TextView header, descr, price;
public myviewholder(#NonNull View itemView) {
super(itemView);
CircleImg = itemView.findViewById(R.id.circle1);
header = itemView.findViewById(R.id.txtheader1);
descr = itemView.findViewById(R.id.txtdescr1);
price = itemView.findViewById(R.id.txtprecio1);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
//do something when clicked
}
}
You can apply onclick listener by using following code :
holder.header.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//do something.
}
});
add this code in your onBindViewHolder of Adapter. Just make sure to change the id of holder.
full code :
#Override
public void onBindViewHolder(#NonNull myviewholder holder, int position) {
Glide.with(holder.itemView.getContext()).load(dataholder.get(position).getImageurl()).into(holder.CircleImg);
holder.header.setText(dataholder.get(position).getHeader());
holder.descr.setText(dataholder.get(position).getDescr());
holder.price.setText(dataholder.get(position).getPrec());
holder.header.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//do something.
}
});
}
Create object of view inside your view holder and also initialize button.setOnClickListener(this) inside viewHolder itself and setting on click inside onBinfViewHolder(...)(only in JAVA). In kotlin you just need to set on click method inside onBindViewHolder(...).
Example:
class YourAdapter():RecyclerView.Adapter<YourAdapter.YourViewHolder>() {
class YourViewHolder(view: View):RecyclerView.ViewHolder(view){
val yourButton:Button = view.findViewById(R.id.landingViewBtn)
}
override fun onBindViewHolder(holder: YourViewHolder, position: Int) {
holder.yourButton.setOnClickListener {
//do Something
val intent = Intent(context,nextActivity::class.java)
context.startActivity(intent)
}
}
}
Im working on a news reader app that uses fragments. I get my data from the newsapi. So far I have managed to pull the data from the api and display it in a recyclerview but when I click on an item, nothing happens!
public class HomeFragment extends Fragment implements RecyclerViewAdapter.OnItemClickListener {
public static final String API_KEY= "c03ceb6a99b14050875f56xxxxxxxx";
private RecyclerView recyclerView;
private List<Article> articles = new ArrayList<>();
private RecyclerViewAdapter recyclerViewAdapter;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_home,container,false);
recyclerView = rootView.findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerViewAdapter = new RecyclerViewAdapter(articles,getActivity());
recyclerViewAdapter.setOnItemClickListener(HomeFragment.this);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setNestedScrollingEnabled(false);
LoadJsonData();
return rootView;
}
public void LoadJsonData(){
ApiInterface apiInterface = ApiClient.getApiClient().create(ApiInterface.class);
String country = Utils.getCountry();
Call <News> call;
call = apiInterface.getNews(country,API_KEY);
call.enqueue(new Callback<News>() {
#Override
public void onResponse(Call<News> call, Response<News> response) {
if (response.isSuccessful() && response.body().getArticle() != null){
if (!articles.isEmpty()){
articles.clear();
}
articles = response.body().getArticle();
recyclerViewAdapter = new RecyclerViewAdapter(articles,getActivity());
recyclerView.setAdapter(recyclerViewAdapter);
recyclerViewAdapter.setOnItemClickListener(HomeFragment.this);
recyclerViewAdapter.notifyDataSetChanged();
} else {
Toast.makeText(getActivity(),"No result",Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<News> call, Throwable t) {
}
});
}
#Override
public void onItemClick(int position) {
articles.get(position);
}
}
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
private List<Article> articles;
private Context context;
private OnItemClickListener onItemClickListener;
public interface OnItemClickListener {
void onItemClick(int position);
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
public RecyclerViewAdapter(List<Article> articles, Context context) {
this.articles = articles;
this.context = context;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(context).inflate((R.layout.card_view_layout), viewGroup, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder viewHolder, int i) {
final ViewHolder holder = viewHolder;
Article model = articles.get(i);
RequestOptions requestOptions = new RequestOptions();
requestOptions.placeholder(Utils.getRandomDrawbleColor());
requestOptions.error(Utils.getRandomDrawbleColor());
requestOptions.diskCacheStrategy(DiskCacheStrategy.ALL);
requestOptions.centerCrop();
Glide.with(context)
.load(model.getUrlToImage())
.apply(requestOptions)
.listener(new RequestListener<Drawable>() {
#Override
public boolean onLoadFailed(#Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
holder.progressBar.setVisibility(View.GONE);
return false;
}
#Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
holder.progressBar.setVisibility(View.GONE);
return false;
}
})
.transition(DrawableTransitionOptions.withCrossFade())
.into(holder.imageView);
holder.title.setText(model.getTitle());
holder.desc.setText(model.getDescription());
holder.source.setText(model.getSource().getName());
holder.time.setText(" \u2022 " + Utils.DateToTimeFormat(model.getPublishedAt()));
}
#Override
public int getItemCount() {
return articles.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView title, desc, source, time;
ImageView imageView;
ProgressBar progressBar;
public ViewHolder(#NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.title);
desc = itemView.findViewById(R.id.description);
source = itemView.findViewById(R.id.source);
time = itemView.findViewById(R.id.time);
imageView = itemView.findViewById(R.id.headline_image);
progressBar = itemView.findViewById(R.id.progress_load);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (onItemClickListener != null){
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION){
onItemClickListener.onItemClick(position);
}
}
}
});
}
}
}
It seems all you are doing inside your onClick is getting the item position. It is now up to you to do something with that position.
So the question comes down to, what do you want to do?
Launch a details activity/fragment to display more info? That is a typical pattern for mobile.
Intent i = new Intent(context,DetailActivity.class);
i.putExtra(Your Data);
startActivity(i);
In my fragment I have an EditText which basically allows, when submitted, to query the values from Firebase Realtime Database and show them in a Recycler View.
Everything is fine when I search the first time but, when I search more than once, the previous results stay in the Recycler View.
I've tried to clear my lists but I got no success. I think I found the problem but I just can't solve it. I need to clear my bundle everytime I pass the retrieved values from the DB from one fragment to another. This is my fragment code (where I call the adaptor, not where I retrieve the DB values):
public class FilteredResultsFragment extends android.support.v4.app.Fragment {
ImageAdapter imageAdapter;
List<String> listOfImages = new ArrayList<>();
List<String> listOfNames = new ArrayList<>();
private RecyclerView recyclerView;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View viewFilteredResults = inflater.inflate(R.layout.fragment_filtered_results, container, false);
recyclerView = viewFilteredResults.findViewById(R.id.recyclerViewFiltered);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
imageAdapter = new ImageAdapter(listOfImages, listOfNames, getContext());
recyclerView.setAdapter(imageAdapter);
return viewFilteredResults;
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (getArguments() != null) {
removeAll();
listOfImages.addAll(getArguments().getStringArrayList("listOfImages"));
listOfNames.addAll(getArguments().getStringArrayList("listOfNames"));
Log.d("IMAGENS", "img "+listOfImages);
Log.d("Nomes","nome "+listOfNames);
imageAdapter.notifyDataSetChanged();
}
}
public void removeAll(){
listOfImages.clear();
listOfNames.clear();
imageAdapter.notifyDataSetChanged();
}
}
As you can see, I've created a function that should allow me to clear the data... And this is my Adaptor:
public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ImageViewHolder> {
List<String> listaPaisesFiltrados;
List<String> listaNomesFiltrados;
android.content.Context context;
ImageAdapter(List<String> listaPaisesFiltrados, List<String> listaNomesFiltrados, android.content.Context c) {
this.listaPaisesFiltrados = listaPaisesFiltrados;
this.listaNomesFiltrados = listaNomesFiltrados;
this.context = c;
}
#NonNull
#Override
public ImageViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item_view, parent, false);
return new ImageViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull ImageViewHolder holder, int position) {
String imageUrl = listaPaisesFiltrados.get(holder.getAdapterPosition());
String nomePais = listaNomesFiltrados.get(holder.getAdapterPosition());
android.content.Context context1 = context.getApplicationContext();
Log.d("ADAPTER", "imageUrl : " + imageUrl + " position : " + holder.getAdapterPosition());
Typeface tpPaisNome = Typeface.createFromAsset(holder.itemView.getContext().getAssets(),"fonts/RobotoSlab-Bold.ttf"); // Define a Typeface
holder.textView.setTypeface(tpPaisNome);
holder.textView.setText(nomePais);
Glide.with(holder.imageView.getContext()).load(imageUrl).centerCrop().into(holder.imageView);
}
#Override
public int getItemCount() {
Log.d("ADAPTER", "SIZE : " + listaPaisesFiltrados.size());
return listaPaisesFiltrados.size();
}
public class ImageViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
TextView textView;
public ImageViewHolder(View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.imgPaisFiltrado);
textView = itemView.findViewById(R.id.txtNomePaisFiltrado);
}
}
}
EDIT
In my ExploreFragment (where I have the query and the EditText) I send two ArrayLists via Bundle to the fragment that opens the adaptor. I need to clear my Bundler but, when I use blunder.clear() than no value is passed to the other fragment and no image is displayed. This is my code
srcView.setOnEditorActionListener(new EditText.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
boolean handled = false;
if (actionId == EditorInfo.IME_ACTION_DONE) {
txtRecomendado.setVisibility(View.INVISIBLE);
scrollView.setVisibility(View.INVISIBLE);
final Bundle bundler = new Bundle();
bundler.clear();
FirebaseDatabase mDatabase = FirebaseDatabase.getInstance();
DatabaseReference paisNomeContinentes = mDatabase.getReference().child("paises");
Query queries = paisNomeContinentes.orderByChild("Nome").startAt(String.valueOf(srcView.getText())).endAt(String.valueOf(srcView.getText())+"\uf8ff");
Log.d("AQUIPUTO","teste "+String.valueOf(srcView.getText()));
queries.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
List<String> imagemPaisList = new ArrayList<>();
List<String> nomePaisList = new ArrayList<>();
for (DataSnapshot ds : dataSnapshot.getChildren()) {
String imagemPais = ds.child("Imagem").getValue(String.class);
String nomePais = ds.child("Nome").getValue(String.class);
imagemPaisList.add(imagemPais);
nomePaisList.add(nomePais);
}
int urlCount = imagemPaisList.size();
// int randomImage = new Random().nextInt(urlCount);
for (int i = 0; i < nomePaisList.size(); i++) {
//Integer randomVariavel = new Random().nextInt(urlCount);
randomImagemPaisList.add(imagemPaisList.get(i));
randomNomePaisList.add(nomePaisList.get(i));
}
getFragmentManager().popBackStack();
FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();
transaction.setCustomAnimations(R.anim.slide_up_info,R.anim.slide_down_info);
FilteredResultsFragment fragment = new FilteredResultsFragment();
bundler.putStringArrayList("listOfImages", (ArrayList<String>) randomImagemPaisList);
bundler.putStringArrayList("listOfNames", (ArrayList<String>) randomNomePaisList);
fragment.setArguments(bundler);
transaction.replace(R.id.fragContainerExploreFilteredResults, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
How can I clear my bundle? I've tried to clear it after I use:
bundler.putStringArrayList("listOfImages", (ArrayList<String>) randomImagemPaisList);
bundler.putStringArrayList("listOfNames", (ArrayList<String>) randomNomePaisList);
fragment.setArguments(bundler);
And if I do this no value is passed from one fragment to another. I've tried to clear it when the fragment.trasaction is complete but I still get the same 'error'...
might be possible that updated data is not notify with recyclerview, you can create doRefresh method in adapter class and then update the list and notify it, please see the below code
Fragment Code
public class FilteredResultsFragment extends android.support.v4.app.Fragment {
ImageAdapter imageAdapter;
List<String> listOfImages = new ArrayList<>();
List<String> listOfNames = new ArrayList<>();
private RecyclerView recyclerView;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View viewFilteredResults = inflater.inflate(R.layout.fragment_filtered_results, container, false);
setAdapter();
return viewFilteredResults;
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (getArguments() != null) {
listOfImages.clear();
listOfNames.clear();
listOfImages.addAll(getArguments().getStringArrayList("listOfImages"));
listOfNames.addAll(getArguments().getStringArrayList("listOfNames"));
Log.d("IMAGENS", "img "+listOfImages);
Log.d("Nomes","nome "+listOfNames);
setAdapter();
}
}
private void setAdapter() {
if (listOfImages != null && listOfNames != null) {
if (imageAdapter == null) {
imageAdapter = new ImageAdapter(getContext());
}
imageAdapter.doRefresh(listOfImages, listOfNames);
if (recyclerView.getAdapter() == null) {
recyclerView = viewFilteredResults.findViewById(R.id.recyclerViewFiltered);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(imageAdapter);
}
}
}
}
and Adapter class as below
public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ImageViewHolder> {
List<String> listaPaisesFiltrados;
List<String> listaNomesFiltrados;
android.content.Context context;
ImageAdapter(android.content.Context c) {
this.context = c;
}
public void doRefresh(List<String> listaPaisesFiltrados, List<String> listaNomesFiltrados) {
this.listaPaisesFiltrados = listaPaisesFiltrados;
this.listaNomesFiltrados = listaNomesFiltrados;
notifyDataSetChanged();
}
#NonNull
#Override
public ImageViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item_view, parent, false);
return new ImageViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull ImageViewHolder holder, int position) {
String imageUrl = listaPaisesFiltrados.get(holder.getAdapterPosition());
String nomePais = listaNomesFiltrados.get(holder.getAdapterPosition());
android.content.Context context1 = context.getApplicationContext();
Log.d("ADAPTER", "imageUrl : " + imageUrl + " position : " + holder.getAdapterPosition());
Typeface tpPaisNome = Typeface.createFromAsset(holder.itemView.getContext().getAssets(),"fonts/RobotoSlab-Bold.ttf"); // Define a Typeface
holder.textView.setTypeface(tpPaisNome);
holder.textView.setText(nomePais);
Glide.with(holder.imageView.getContext()).load(imageUrl).centerCrop().into(holder.imageView);
}
#Override
public int getItemCount() {
Log.d("ADAPTER", "SIZE : " + listaPaisesFiltrados.size());
return listaPaisesFiltrados.size();
}
public class ImageViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
TextView textView;
public ImageViewHolder(View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.imgPaisFiltrado);
textView = itemView.findViewById(R.id.txtNomePaisFiltrado);
}
}
}
you can try to create new adapter and set to your recycler.
listOfImages.clear();
listOfNames.clear();
/*
Adding your new searching data here
*/
recyclerView.setAdapter(new ImageAdapter(listOfImages, listOfNames, getContext());
recyclerView.invalidate();
When you try to clear by calling removeAll, you only clear the list objects from the Fragment class. What you need is to clear the list objects in your RecyclerView.Adapter class.
Try this:
Move your removeAll to your adapter. That way, you clear the list objects in that adapter.
In your Fragment, just call your removeAll from your Adapter.
Sample Code:
Fragment.java
//No more removeAll() here
public void callAdapterToRemove() {
imageAdapter.removeAll();
}
ImageAdapter.java
//Now it's here
public void removeAll() {
listaPaisesFiltrados.clear();
listaNomesFiltrados.clear();
notifyDataSetChanged();
}
I am having problems updating my RecyclerView with new data. If I press a confirmation button on a CardView in the first tab, the card should get added to the second tab but it won't update it there until I rotate the screen. I get the data for the card from reading a text file. Please advise me how to call the notifyDataSetChange method after I have added the new data to my text file. I have tried everything and all I get is NullPointerExceptions. The RecyclerViews are in fragments and I use FragementStatePagerAdapter.
I'll put some of my classes here. Ask if you need more information.
RecyclerViewAdapter.java
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewHolder> {
private List<String> mListTitle;
private List<String> mListDesc;
private List<String> mListPoints;
private List<String> mListDates;
private String fragment_tag;
public RecyclerViewAdapter() {
}
public RecyclerViewAdapter(List<List<String>> super_list, String tag) {
this.mListTitle = super_list.get(0);
this.mListDesc = super_list.get(1);
this.mListPoints = super_list.get(2);
this.mListDates = super_list.get(3);
fragment_tag = tag;
}
#Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new RecyclerViewHolder(inflater, parent, fragment_tag);
}
#Override
public void onBindViewHolder(RecyclerViewHolder holder, int position) {
holder.mTitleText.setText(mListTitle.get(position));
holder.mDescText.setText(mListDesc.get(position));
holder.mPointsText.setText(mListPoints.get(position));
if (fragment_tag.equals("completed")) {
holder.mDateText.setText(mListDates.get(position));
}
}
#Override
public int getItemCount() {
return mListTitle.size();
}
}
class RecyclerViewHolder extends RecyclerView.ViewHolder {
RecyclerView recyclerView;
RecyclerViewAdapter mAdapter;
public TextView mTitleText, mDescText, mDateText, mPointsText, popupTitle;
public Button mConfButton, popCancelBtn, popAcceptBtn;
public RecyclerViewHolder(View itemView) {
super(itemView);
}
public RecyclerViewHolder(final LayoutInflater inflater, final ViewGroup container, String tag) {
// Inflating the card layout depending on the tag parameter.
super(inflater.inflate
((tag.equals("challenges")) ? R.layout.card_view_chall : R.layout.card_view_comp, container,
false));
mTitleText = itemView.findViewById(R.id.title_holder);
mDescText = itemView.findViewById(R.id.desc_holder);
mPointsText = itemView.findViewById(R.id.points_holder);
mDateText = itemView.findViewById(R.id.date_holder);
if (tag.equals("challenges")) {
mConfButton = itemView.findViewById(R.id.card_conf_button);
mConfButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Setting the layout inflater for popup window.
LayoutInflater pInflater = (LayoutInflater) itemView.getContext().getSystemService(LAYOUT_INFLATER_SERVICE);
ViewGroup container1 = (ViewGroup) pInflater.inflate(R.layout.confirmation_popup, null);
final PopupWindow popupWindow = new PopupWindow(container1, 700, 600, true);
popupTitle = container1.findViewById(R.id.popuptext);
popAcceptBtn = container1.findViewById(R.id.accept_button);
popCancelBtn = container1.findViewById(R.id.cancel_button);
popupTitle.setText(mTitleText.getText().toString());
// Dismisses the popup window
popCancelBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
popupWindow.dismiss();
}
});
// Click listener for dialog accept button.
popAcceptBtn.setOnClickListener(new View.OnClickListener() {
String date;
#Override
public void onClick(View view) {
List<String> list = new ArrayList<>();
list.add(mTitleText.getText().toString());
list.add(mDescText.getText().toString());
list.add(mPointsText.getText().toString());
list.add(date = new SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(new Date()));
// Saving data from current card into the completed challenges list.
TempDataReader reader = new TempDataReader(itemView.getContext());
new TempDataReader(itemView.getContext()).saveFile(list);
// I want to notify the dataset change here if possible!
popupWindow.dismiss();
}
});
popupWindow.showAtLocation(itemView, Gravity.CENTER, 25, 100);
}
});
}
}
}
SectionsPagerAdapter.java
public class SectionsPagerAdapter extends FragmentStatePagerAdapter{
private ViewPager viewPager;
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public void addFragment(Fragment fragment, String title){
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
#Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
}
}
CompletedFragment.java
public class CompletedFragment extends Fragment {
RecyclerView recyclerView;
RecyclerViewAdapter adapter;
public Fragment newInstance() {
return new CompletedFragment();
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.completed_fragment, container, false);
recyclerView = view.findViewById(R.id.completed_frag);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
TempDataReader reader = new TempDataReader(getActivity());
List<List<String>> super_list = reader.readCompFile();
if(super_list == null || super_list.size() < 1){
return null;
} else{
adapter = new RecyclerViewAdapter(super_list,"completed");
recyclerView.setAdapter(adapter);
return view;
}
}
}
EDIT:
Added the code for the second fragment, which is the one that should be updated after the onClick at RecyclerViewHolder-class.
You have to add a function in your adapter for adding data:
public void addData(String title, String desc, String point, String date) {
this.mListTitle.add(title);
this.mListDesc.add(desc);
this.mListPoints.add(point);
this.mListDates.add(date);
notifyDataSetChanged();
}
If you want to enable animations call notifyItemInserted() instead of notifyDataSetChanged();
It is important that you add a String to every list because in your onBindViewHolder() you get the item to display from every list with list.get(position). Otherwise you'll get a IndexOutOfBoundsException.
You can create an interface and use as a callback. Send it as a parameter of the RecyclerViewAdapter and then to your RecyclerViewHolder. When the item should be added you call the callback that will get you back to your fragment. There you can read the file again and call notifyDataSetChanged.
I know i explain pretty bad so i will try to change your code so that it does what i said:
this will be your RecyclerViewAdapter:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewHolder> {
private List<String> mListTitle;
private List<String> mListDesc;
private List<String> mListPoints;
private List<String> mListDates;
private String fragment_tag;
private Runnable callback;
public RecyclerViewAdapter() {
}
public RecyclerViewAdapter(List<List<String>> super_list, String tag, Runnable callBack) {
//add the callback here(Runnable) and save it into a local variable
this.callback=callback;
this.mListTitle = super_list.get(0);
this.mListDesc = super_list.get(1);
this.mListPoints = super_list.get(2);
this.mListDates = super_list.get(3);
fragment_tag = tag;
}
#Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new RecyclerViewHolder(inflater, parent, fragment_tag, callback);
//send the callback to your viewHolder
}
#Override
public void onBindViewHolder(RecyclerViewHolder holder, int position) {
holder.mTitleText.setText(mListTitle.get(position));
holder.mDescText.setText(mListDesc.get(position));
holder.mPointsText.setText(mListPoints.get(position));
if (fragment_tag.equals("completed")) {
holder.mDateText.setText(mListDates.get(position));
}
}
#Override
public int getItemCount() {
return mListTitle.size();
}
}
class RecyclerViewHolder extends RecyclerView.ViewHolder {
RecyclerView recyclerView;
RecyclerViewAdapter mAdapter;
public TextView mTitleText, mDescText, mDateText, mPointsText, popupTitle;
public Button mConfButton, popCancelBtn, popAcceptBtn;
public RecyclerViewHolder(View itemView) {
super(itemView);
}
public RecyclerViewHolder(final LayoutInflater inflater, final ViewGroup container, String tag, Runnable callback) {
//ADD the callback to the parameters list here
// Inflating the card layout depending on the tag parameter.
super(inflater.inflate
((tag.equals("challenges")) ? R.layout.card_view_chall : R.layout.card_view_comp, container,
false));
mTitleText = itemView.findViewById(R.id.title_holder);
mDescText = itemView.findViewById(R.id.desc_holder);
mPointsText = itemView.findViewById(R.id.points_holder);
mDateText = itemView.findViewById(R.id.date_holder);
if (tag.equals("challenges")) {
mConfButton = itemView.findViewById(R.id.card_conf_button);
mConfButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Setting the layout inflater for popup window.
LayoutInflater pInflater = (LayoutInflater) itemView.getContext().getSystemService(LAYOUT_INFLATER_SERVICE);
ViewGroup container1 = (ViewGroup) pInflater.inflate(R.layout.confirmation_popup, null);
final PopupWindow popupWindow = new PopupWindow(container1, 700, 600, true);
popupTitle = container1.findViewById(R.id.popuptext);
popAcceptBtn = container1.findViewById(R.id.accept_button);
popCancelBtn = container1.findViewById(R.id.cancel_button);
popupTitle.setText(mTitleText.getText().toString());
// Dismisses the popup window
popCancelBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
popupWindow.dismiss();
}
});
// Click listener for dialog accept button.
popAcceptBtn.setOnClickListener(new View.OnClickListener() {
String date;
#Override
public void onClick(View view) {
List<String> list = new ArrayList<>();
list.add(mTitleText.getText().toString());
list.add(mDescText.getText().toString());
list.add(mPointsText.getText().toString());
list.add(date = new SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(new Date()));
// Saving data from current card into the completed challenges list.
TempDataReader reader = new TempDataReader(itemView.getContext());
new TempDataReader(itemView.getContext()).saveFile(list);
// I want to notify the dataset change here if possible!
//call the callback
callback.run();
popupWindow.dismiss();
}
});
popupWindow.showAtLocation(itemView, Gravity.CENTER, 25, 100);
}
});
}
}
}
And this will be your fragment:
public class CompletedFragment extends Fragment {
RecyclerView recyclerView;
RecyclerViewAdapter adapter;
public Fragment newInstance() {
return new CompletedFragment();
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.completed_fragment, container, false);
recyclerView = view.findViewById(R.id.completed_frag);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
TempDataReader reader = new TempDataReader(getActivity());
List<List<String>> super_list = reader.readCompFile();
if(super_list == null || super_list.size() < 1){
return null;
} else{
adapter = new RecyclerViewAdapter(super_list,"completed", new Runnable() {
#Override
public void run() {
//here read the list again and call notifyDataSetChanged on your recycler
}
});
);
recyclerView.setAdapter(adapter);
return view;
}
}
}
Hope it helps and it works for you. If i did anything wrong, let me know, i can't run the code right now so...
edited, i forgot to add code in the callback