I have three fragments switched using a ViewPager and the third one contains a ListView.
My problem is that the ListView is not populated on the first time that I switch to that fragment. I have to switch back to the first then back to the third again and only will the ListView be populated. What seems to be the problem here?
How does ViewPager works anyways?
Here is my TabAdapter
public class TabAdapter extends FragmentStatePagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
TabAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
}
Here is ListAdapter for the listView
public class ListAdapter extends BaseAdapter {
private ArrayList data = new ArrayList();
public ListAdapter(LinkedHashMap<String, ArrayList<String>> lhm) {
try {
data.addAll(lhm.entrySet());
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public int getCount() {
return data.size();
}
#Override
public LinkedHashMap.Entry<String, ArrayList<String>> getItem(int position) {
return (LinkedHashMap.Entry) data.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
#NonNull
#Override
public View getView(final int position, #Nullable View convertView, #NonNull ViewGroup parent) {
final LinkedHashMap.Entry<String, ArrayList<String>> item = getItem(position);
final ArrayList quotesList = item.getValue();
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.archive_list, parent, false);
}
TableRow tableRow = convertView.findViewById(R.id.container);
TextView title = convertView.findViewById(R.id.title);
ImageView copy = convertView.findViewById(R.id.copy);
copy.setVisibility(View.INVISIBLE);
title.setText(item.getKey() + " Quotes");
tableRow.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getContext(), ViewActivity.class);
intent.putExtra("title", item.getKey());
intent.putStringArrayListExtra("quotes", quotesList);
if (interstitialAd.isLoaded()) {
interstitialAd.show();
startActivity(intent);
} else {
startActivity(intent);
}
}
});
return convertView;
}
}
Update
The problem seems to be from the Linkedhashmap. I tried manually putting values into the Hashmap and it worked. The problem now is my current code. I populate my hashmap by retrieving data from firebase and that seems to be the problem? Pershaps it isn't populated as quickly?
private void initArchives() {
DatabaseReference dBase = FirebaseDatabase.getInstance().getReference().child("archive");
dBase.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for(DataSnapshot snapshot : dataSnapshot.getChildren()) {
DataPojo dp = snapshot.getValue(DataPojo.class);
hm.put(dp.getName(), dp.getMessages());
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_archive, container, false);
listView = view.findViewById(R.id.listView);
searchView = view.findViewById(R.id.searchView);
initArchives();
listAdapter = new ListAdapter(hm);
listView.setAdapter(listAdapter);
listView.invalidateViews();
loadAds();
return view;
}
First of all FragmentStatePagerAdapter is really a pain in the ass. But we have to work with that. I suggest you to Ctrl + click on FragmentStatePagerAdapter and read source code.
Another thing is adding fragments to your ArrayList with addFragment() method might not be the best idea since FragmentStatePagerAdapter is saving the states of the fragments and loads them from its own FragmentManager.
So the fragment you added might not be the fragment you are getting.
A possible solution to that would be using tags.
There are some must read articles about the topic here here and here.
And oh one other thing. ViewPager creates three fragments at a time when using FragmentStatePagerAdapter. First it creates fragment to display and next the fragment on the right and last the fragment on the left. Since you have three fragments, the last (3rd) fragment is not created right away (There is no fragment on the left of the first fragment). Maybe you are loading some data that takes time to download on the third fragment? And next time you display the 3rd fragment it loads from the saved state of FragmentStatePagerAdapter. These are just some possibilities to think about. Not necessarily is the right solution. Good luck.
Related
Problem Statement
I have a problem of data being changed in fragment whenever the data is modified in the BottomSheetDialogFragment
App Description
In my app I have the MainActivity which host 2 Fragment in it's ViewPager. 1st Fragment for app content and 2nd Fragment (let's call it GalleryFragment) that shows the gallery view. User can tap on gallery item which loads up the BottomSheet (let's call it GalleryBottomSheet) - that hosts RecyclerView to show gallery item in full screen. Now initially app feeds the GalleryFragment with the ArrayList to show the gallery items. When user clicks on the Gallery item, this ArrayList is passed to the GalleryBottomSheet.
So What's Happening
What's happening is whenever I am removing an item from the ArrayList in my GalleryBottomSheet, It automatically also remove that item in the ArrayList of GalleryFragment. In short, Any update in the Arraylist from GalleryBottomSheet impacting the ArrayList in GalleryFragment
What do you want
I want seperation of concerns. I don't want the change made in the ArrayList of GalleryBottomSheet to impact the original ArrayList of GalleryFragment
Show me the damn code
To make this question concise, I am adding only the important part of the code.
~ GalleryFragment.java
public class GalleryFragment extends Fragment {
private RecyclerView recyclerView;
private ArrayList<String> arrayList = new ArrayList<>(); //This is the one which will be passed to the GalleryBottomSheet
private GalleryAdapter galleryAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_gallery, container, false);
//setting up all the UI work...
arrayList = FindFiles(); //FindFiles is a private function searching for all file in the dir and adding the path as a string to the arraylist
galleryAdapter = new GalleryAdapter(getActivity()); //init the adapter
recyclerView.setAdapter(galleryAdapter); //setting the adapter
return view;
}
}
private class GalleryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final Activity activity;
GalleryAdapter(Activity context) {
this.activity = context;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.gallery_content_item, parent, false);
return new ItemViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
if(holder instanceof ItemViewHolder){
//setting up stuff..
}
}
#Override
public int getItemCount() {
return arrayList.size();
}
private class ItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
ImageView imageView;
ItemViewHolder(View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.imageView);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
Bundle bundle = new Bundle();
bundle.putStringArrayList("list", arrayList);
GalleryBottomSheet galleryBottomSheet= GalleryBottomSheet.newInstance();
galleryBottomSheet.setOnRefreshListener( new GalleryBottomSheet.GalleryInterface() {
#Override
public void onRefresh() {
//here the actual arrayList size reduced even though the arrayList that was modified exist in GalleryBottomSheet
System.out.println("CURRENT LIST SIZE: " + arrayList.size());
}
});
galleryBottomSheet.setArguments(bundle);
galleryBottomSheet.show(getParentFragmentManager(), "galleryPager");
}
}
~ GalleryBottomSheet.java
public class GalleryBottomSheet extends BottomSheetDialogFragment {
static GalleryBottomSheet newInstance() {
return new GalleryBottomSheet();
}
public interface GalleryInterface {
void onRefresh();
}
private RecyclerView recyclerView;
private ViewAdapter viewAdapter;
private Button deleteBtn;
private GalleryInterface galleryInterface;
public void setOnRefreshListener( GalleryInterface galleryInterface){
this.galleryInterface = galleryInterface;
}
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
//.. setting up sheetDialog
return bottomSheetDialog;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(BottomSheetDialogFragment.STYLE_NO_FRAME, R.style.GalleryBottomStyle);
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.gallery_sheet, container, false);
Bundle bundle = getArguments();
ArrayList<String> filePathArr = bundle.getStringArrayList("list"); //here arrayList from GalleryFragment
//Setting up all UI views...
deleteBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int positionPager = recyclerView.computeVerticalScrollOffset(); //just for demo, in the actual app we are using addOnScrollListener for position
filePathArr.remove(positionPager);
viewAdapter.notifyItemRemoved(positionPager);
galleryInterface.onRefresh(); //this is where GalleryFragment shows that the arraylist is modified too.
Toast.makeText(getActivity(), "Deleted Successfully",Toast.LENGTH_SHORT).show();
}
});
return view;
}
//setting up viewAdapter and other stuff
}
Well, it turns out the main problem is that object is stored in memory by reference. By modifying the arrayList from GalleryBottomSheet it also changes the original arrayList since the exact arrayList was referred in GalleryBottomSheet from GalleryFragment. The solution is to simply initializing a new ArrayList in GalleryBottomSheet
Changing from:
ArrayList<String> filePathArr = bundle.getStringArrayList("list");
to:
ArrayList<String> filePathArr = new ArrayList<>(bundle.getStringArrayList("list"));
I've been duelling with this problem for a good few hours now. I have a nested RecyclerView (i.e. a RecyclerView that encompasses an inner Recycler view). Both the parent and child recycler view's are dynamic. The problem I encounter is that I cannot find a way to correctly notify the child (inner) recycler view when a CRUD, in particular a delete, occurs. At first it works ok, but then I get all sorts of random errors from "You must be a direct descend view" or getAdapterPosition returning -1 or just simply incorrect positions. I think my implementation is pretty standard so I ask what is the correct way to notify the inner recycler view.
I am pretty close to returning to my former implementation which involved an array of fragments each containing a recycling view, but I question about the performance of such design. My code is as follows:
Parent RecyclerView
public class RecipeRecyclerAdapter extends RecyclerView.Adapter<RecipeRecyclerAdapter.ViewHolder>
{
public interface OnRecipeRecyclerListener
{
//--------------------------- Proxy methods for OnDishRecyclerListener -----------------
void renameDish(int DishPosition, int RecipePosition);
void deleteDish(int DishPosition, int RecipePosition);
//--------------------------- OnRecipeRecyclerListener methods ----------------------------
void deleteRecipe(int RecipePosition);
void renameRecipe(int RecipePosition);
}
//Recycler Pool and tools
private RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();
//Recycler Parameters
private ArrayList<Recipe> allRecipes;
private Context context;
//Listener
#Setter
private OnRecipeRecyclerListener onRecipeRecyclerListener;
public RecipeRecyclerAdapter(Context context, ArrayList<Recipe> allRecipes)
{
this.allRecipes = allRecipes;
this.context = context;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_Recipe, parent, false);
return new RecipeRecyclerAdapter.ViewHolder(view, onRecipeRecyclerListener, context);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position)
{
Recipe Recipe = allRecipes.get(position);
holder.RecipeName.setText(Utils.colourFirstLetter(context, Recipe.getRecipeName(), R.color.progressFxBar));
holder.RecipeDate.setText(Utils.getDate(Recipe.getTimestamp()));
// Create layout manager with initial prefetch item count
LinearLayoutManager layoutManager = new LinearLayoutManager(
holder.DishsRecycler.getContext(),
LinearLayoutManager.VERTICAL,
false
);
layoutManager.setInitialPrefetchItemCount(Recipe.getDishs().size());
DishRecyclerAdapter DishsRecyclerAdapter = new DishRecyclerAdapter(Recipe.getDishs(), holder, context);
holder.DishsRecycler.setLayoutManager(layoutManager);
holder.DishsRecycler.setAdapter(DishsRecyclerAdapter);
holder.DishsRecycler.setRecycledViewPool(viewPool);
}
#Override
public int getItemCount()
{
return allRecipes.size();
}
static class ViewHolder extends RecyclerView.ViewHolder implements DishRecyclerAdapter.OnDishRecyclerListener
private OnRecipeRecyclerListener onRecipeRecyclerListener;
private Context context;
TextView RecipeName, RecipeDate;
ImageView addDish;
//The Dishs Recycler
RecyclerView DishsRecycler;
public ViewHolder(#NonNull View itemView, OnRecipeRecyclerListener onRecipeRecyclerListener, Context context)
{
super(itemView);
this.onRecipeRecyclerListener = onRecipeRecyclerListener;
this.context = context;
RecipeName = itemView.findViewById(R.id.RecipeName);
RecipeDate = itemView.findViewById(R.id.RecipeDate);
addDish = itemView.findViewById(R.id.addDish);
DishsRecycler = itemView.findViewById(R.id.DishsRecyclerView);
loadListeners(itemView);
}
private void loadListeners(#NonNull View initView)
{
RecipeName.setOnClickListener(v ->
{
PopupMenu popup = new PopupMenu(context, v);
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.Recipe_floating_menu, popup.getMenu());
popup.show();
popup.setOnMenuItemClickListener(item ->
{
switch (item.getItemId())
{
case R.id.menuDeleteRecipe:
onRecipeRecyclerListener.deleteRecipe(getAdapterPosition());
return true;
case R.id.menuRenameRecipe:
onRecipeRecyclerListener.renameRecipe(getAdapterPosition());
return true;
case R.id.menuRecipeProps:
onRecipeRecyclerListener.RecipeProps(getAdapterPosition());
return true;
default:
return false;
}
});
});
addDish.setOnClickListener(v ->
{
onRecipeRecyclerListener.addDish(getAdapterPosition());
});
}
//******************************* OnDishRecyclerListener *******************************
#Override
public void renameDish(int position)
{
onRecipeRecyclerListener.renameDish(position, getAdapterPosition());
}
#Override
public void deleteDish(int position)
{
onRecipeRecyclerListener.deleteDish(position, getAdapterPosition());
}
}
}
Child (inner) RecyclerView
public class DishRecyclerAdapter extends RecyclerView.Adapter<DishRecyclerAdapter.ViewHolder>
{
public interface OnDishRecyclerListener
{
void renameDish(int position);
void deleteDish(int position);
}
private OnDishRecyclerListener onDishRecyclerListener;
private ArrayList<Dish> allDishs;
private Context context;
public DishRecyclerAdapter(ArrayList<Dish> allDishs, OnDishRecyclerListener onDishRecyclerListener, Context context)
{
this.onDishRecyclerListener = onDishRecyclerListener;
this.allDishs = allDishs;
this.context = context;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_Dishs, parent, false);
return new ViewHolder(context, view, onDishRecyclerListener);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position)
{
Dish Dish = allDishs.get(position);
holder.DishName.setText(Dish.getDishName());
}
#Override
public int getItemCount()
{
return allDishs.size();
}
public class ViewHolder extends RecyclerView.ViewHolder
{
private Context context;
TextView DishName; //plus a bunch of other Views I just removed for the sake of simplicity
OnDishRecyclerListener onDishRecyclerListener;
public ViewHolder(Context context, #NonNull View itemView, OnDishRecyclerListener onDishRecyclerListener)
{
super(itemView);
this.context = context;
DishName = itemView.findViewById(R.id.DishName);
this.onDishRecyclerListener = onDishRecyclerListener;
loadListeners(itemView);
}
private void loadListeners(#NonNull View v)
{
//Rename an Dish
DishName.setOnClickListener(view ->
{
PopupMenu popup = new PopupMenu(context, v);
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.Dish_floating_menu, popup.getMenu());
popup.show();
popup.setOnMenuItemClickListener(item ->
{
switch (item.getItemId())
{
case R.id.menuDeleteDish:
onDishRecyclerListener.deleteDish(getAdapterPosition());
return true;
case R.id.menuRenameDish:
onDishRecyclerListener.renameDish(getAdapterPosition());
return true;
case R.id.menuDishProps:
return true;
default:
return false;
}
});
});
}
}
}
An extraction of the fragment calling the parent recycler view:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_Recipe_panel, container, false);
recyclerRecipe = view.findViewById(R.id.RecipeRecyclerView);
SimpleItemAnimator simpleItemAnimator = (SimpleItemAnimator) recyclerRecipe.getItemAnimator();
if(simpleItemAnimator !=null)
{
simpleItemAnimator.setSupportsChangeAnimations(true);
}
RecipeAdapter = new RecipeRecyclerAdapter(getContext(), allRecipes);
RecipeAdapter.setOnRecipeRecyclerListener(this);
//recyclerRecipe.setHasFixedSize(true);
recyclerRecipe.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerRecipe.setAdapter(RecipeAdapter);
return view;
}
public void createRecipe(String RecipeName)
{
Recipe Recipe = new Recipe(RecipeName, getContext());
allRecipes.add(0,Recipe);
RecipeAdapter.notifyItemInserted(0);
}
#Override
public void deleteRecipe(int RecipePosition)
{
allRecipes.remove(RecipePosition);
RecipeAdapter.notifyItemRemoved(RecipePosition);
}
#Override
public void addDish(int RecipePosition)
{
allRecipes.get(RecipePosition).getDishs().add(new Dish(DishName));
RecipeAdapter.notifyItemChanged(RecipePosition);
}
#Override
public void deleteDish(int DishPosition, int RecipePosition)
{
Recipe Recipe = allRecipes.get(RecipePosition);
Dish Dish = Recipe.getDishs().get(DishPosition);
Dish.getTimer().destroyTimer();
Recipe.getDishs().remove(DishPosition);
RecipeAdapter.notifyItemChanged(RecipePosition);
}
I figured out what the problem was (after LOADS OF HOURS). I needed to notify first the parent recycler and then the child recycler in that order.
//adding an item to the inner list
recipeAdapter.notifyItemChanged(recipePosition);
dishsRecycler.getAdapter().notifyItemInserted(recipe.getDishs().size()-1);
//deleting an inner list item
recipeAdapter.notifyItemChanged(recipePosition);
dishsRecycler.getAdapter().notifyItemRemoved()
However the biggest culprit was having a common recyclerPool for all the inner recyclerviews, so removed this line from the code
//REMOVED THESE LINES
private RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();
holder.DishsRecycler.setRecycledViewPool(viewPool);
Also, I refrained from using notifyDataSet() as that for some reason throws NO_POSITION (-1).
I'm implementing a similar case.
I have 2 RecyclerViews, one nested. Where you can delete items either from nested or parent RecyclerView.
It guess you must update Recyclers every time an item changed or removed.
For comprehension I read this article first:
https://medium.com/android-news/recyclerview-optimisations-a4b141dd433d
And I agree answer by Ken John, when he said you need to notify RecyclerView updates first to parent then to nested; otherwise you get an error and your app will crash.
However, other important thing is how to do the notification updates.
For the nested RecyclerView, I used
// for items updated
notifyItemChanged(position);
// for items deleted
notifyItemRemoved(position);
but the mentioned above not working fine for parent RecyclerView, really I'm not sure why, but I solved as follow:
// for items updated
notifyItemChanged(position);
// for items deleted
notifyItemRemoved(position); // this line does not work for me
notifyDataSetChanged(); // it works fine
The last instruction spend a more bit of time, but works fine.
Note: I don't know yet why notifyItemRemoved(position) doesn't work for parent, and I have call notifyDataSetChanged()
I have two containers in my home activity, one is Top Bar Fragment Container and the other is ViewPagers Container. Top Bar Fragment Container consist of only one fragment which is the Top Bar Fragment (allows the user to pick a tag) while Viewpagers consists of 2 fragment which in each fragments they have a recycler view to display the posts with the selected tags on Top Bar Fragment.
What im trying to achieve is that when an item is clicked on the Top Bar Fragment, it gets the value to text then sends it to the Activity using an Interface and later on Fragment Viewpagers will get that value from the activity and set the firebase query in accordance with the text passed.
So far, the fragments are doing fine sending data to each other using interfaces. However, adapters only listens once, when view is created and not when data is passed from Fragments. How do I update the adapters after sending in data from Top Bar Fragment?
Here is my ViewPagerFragment(RecyclerView):
FirebaseRecyclerOptions options, options2;
FirebaseRecyclerAdapter<Question, QuestionsViewHolder> adapter;
<!---- oncreate Code---->
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
String ChosenTag = tvtest.getText().toString();
if (text.equals("")){
Query query = questionReference.orderByChild("location").equalTo("jakarta");
options = new FirebaseRecyclerOptions.Builder<Question>()
.setQuery(query, Question.class)
.build();
adapter = new FirebaseRecyclerAdapter<Question, QuestionsViewHolder>(options) {
#Override
protected void onBindViewHolder(#NonNull QuestionsViewHolder holder, int position, #NonNull Question model) {
holder.username.setText(model.getUsername());
}
#NonNull
#Override
public QuestionsViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.snippet_card_question_new, viewGroup, false);
QuestionsViewHolder viewHolder = new QuestionsViewHolder(view);
return viewHolder;
}
};
qListRv.setAdapter(adapter);
adapter.startListening();
} else {
Query query = questionReference.orderByChild("tags").equalTo(ChosenTag);
options2 = new FirebaseRecyclerOptions.Builder<Question>()
.setQuery(query, Question.class)
.build();
adapter.updateOptions(options2);
}
}
To put it simply, if user has not chosen on the TopBarFragment then text is returning a null, adapter will then query on the current location of the user. Else if user chose a tag on the Top Bar Fragment, Top Bar Fragment then sends the tag to string, updates the String ChosenTag, then adapter will then update its options to options2, which queries on the chosen tag.
I am not able to refresh my recyclerview after choosing. Yet interfaces work fine. Is there any way to achieve this?
I've searched everywhere and it seems like none could help me out, so here is what i've done and this worked for me. Hope this helps.
I changed the Fragment B (TopBarFragment) to an Activity (TopBarActivity)
The reason why i changed the fragment to an activity is because i wanted the whole RecyclerView Adapters to stop listening whenever a user is directed to the TopBar Activity. This way i can initiate a new adapter upon returning to the Activity that holds the RecyclerViews.
The Plan
Make two sets of Adapters (one for defaultAdapter, the other for
what the user click on TopBarActivity. let's call it
menuItemAdapter).
Extras to send strings from TopBarActivity upon user clicks to MainActivity. From then Fragments will check the Strings of the
MainActivity.
Create two override methods (onPause & onResume), to either stop listening to the adapters, or start listening to them. We will
create conditions here to either listen to default adapters or
listen to what the user clicks. These Conditions will check if
Strings on point 2 exist or not. If it exists then listen to
menuItemAdapter else if strings does not exist, then listen to
defaultAdapters.
TopBarActivity Code
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_top_bar);
//I am using Recycler view to display the menu item lists on this activity.
//RecyclerView Codes goes here
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(getBaseContext(), MainActivity.class);
i.putExtra("CHOSENITEM", (model.getItemName()));
startActivity(i);
}
}
MainActivity
public class MainActivity extends AppCompatActivity {
private String choicesName = "";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
setupTopBarStringListener();
}
private void setupTopBarStringListener() {
choicesName = getIntent().getStringExtra("CHOSENITEM");
}
public String ChoicesString() {
return choicesName;
}
}
String ChoicesString() will then be called later on in Fragment A
Fragment A Code
public class QuestionFragment extends Fragment {
private String myStringFromActivity;
RecyclerView contentListRv;
FirebaseRecyclerOptions options, newoptions;
FirebaseRecyclerAdapter<Post, PostsViewHolder> adapter, newadapters;
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
Log.i(TAG, "onViewCreated: restart");
contentListRv= view.findViewById(R.id.contentListRv);
contentListRv.setLayoutManager(new LinearLayoutManager(getContext()));
HomeActivity activity = (HomeActivity) getActivity();
myStringFromActivity = activity.ChoicesString();
Log.i(TAG, "choices from activity= " + myStringFromActivity);
}
}
Add Default Recyclers to Fragment A
public void setupRecyclers() {
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot dschild : dataSnapshot.getChildren()) {
String location = "" + dschild.child("location").getValue();
Query query = questionReference.orderByChild("location").equalTo(location);
options = new FirebaseRecyclerOptions.Builder<Question>()
.setQuery(query, Post.class)
.build();
adapter = new FirebaseRecyclerAdapter<Post, PostsViewHolder>(options) {
#Override
protected void onBindViewHolder(#NonNull PostsViewHolder holder, int position, #NonNull Question model) {
holder.username.setText(model.getUsername());
}
#NonNull
#Override
public QuestionsViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.snippet_posts_view, viewGroup, false);
PostsViewHolder viewHolder = new PostsViewHolder(view);
return viewHolder;
}
};
contentListRV.setAdapter(adapter);
adapter.startListening();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
Then add menuItem Recyclers to Fragment A
public void menuItemRecyclers() {
Query query2 = questionReference.orderByChild("tags").equalTo(myStringFromActivity);
newoptions = new FirebaseRecyclerOptions.Builder<Question>()
.setQuery(query2, Post.class)
.build();
newadapters = new FirebaseRecyclerAdapter<Post, PostsViewHolder>(options) {
#Override
protected void onBindViewHolder(#NonNull PostsViewHolder holder, int position, #NonNull Question model) {
holder.username.setText(model.getUsername());
}
#NonNull
#Override
public QuestionsViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.snippet_posts_view, viewGroup, false);
PostsViewHolder viewHolder = new PostsViewHolder(view);
return viewHolder;
}
};
contentListRV.setAdapter(newadapters);
newadapters.startListening();
}
}
}
The code above shows that the String myStringFromActivity retrieved from MainActivity will be our query for the new Adapters.
Finally, we implement the onPause and onResume Methods to our Fragment A
#Override
public void onPause() {
super.onPause();
Log.i(TAG, "onStop: adapter stopped");
if(myStringFromActivity== null){
adapter.stopListening();
} else {
newadapters.stopListening();
}
}
#Override
public void onResume() {
super.onResume();
if(myStringFromActivity==null){
setupRecyclers();
Log.e(TAG, "onResume: String null" );
}else{
menuItemRecyclers();
}
}
Code above shows that if we String myStringFromActivity is null (user hasn't clicked on any items in our Topbaractivity, then it runs the default Recyclers. Else if the user picked on an item then it runs the menuItemRecyclers (based on what the user chooses).
I am still open to better implementation regarding this one. Hope this helps!
I'm having a problem with making a RecycleView in a fragment with data from Firebase. I expect the app to show the RecycleView after I clicked on a button to change from one fragment to the RecycleView fragment, but it does change the showed fragment but it does not show anything.
I know there are plenty of questions like this but I don't seem to find the correct solution to this problem.
I've made everything needed for a Firebase RecyclerView, and also tried to build it inside an activity instead fragment and it did work, but not with the fragment.
I've tried to initialize the adapter and recyclerview inside the onCreateView method, onActivityCreated, and onViewCreated method and none of them seem to be working.
Here's my fragment code:
private KidAdapter adapter;
private RecyclerView recyclerView;
Button button;
View view;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.uangsaku_menu_fragment, container, false);
button = view.findViewById(R.id.btn_add);
button.setOnClickListener(this);
recyclerView = view.findViewById(R.id.recyclerView);
recyclerView.setHasFixedSize(true);
setUpRecyclerView();
return view;
}
#Override
public void onClick(View v) {
if(v.getId() == R.id.btn_add){
Intent intent = new Intent(getActivity(), Register.class);
startActivity(intent);
}
}
public void setUpRecyclerView(){
Query query = FirebaseDatabase.getInstance().getReference("kids");
FirebaseRecyclerOptions<kidData> options = new FirebaseRecyclerOptions.Builder<kidData>()
.setQuery(query, kidData.class)
.build();
adapter = new KidAdapter(options);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(adapter);
}
#Override
public void onStart() {
super.onStart();
if (adapter != null) {
adapter.startListening();
}
}
#Override
public void onStop() {
super.onStop();
if (adapter != null) {
adapter.stopListening();
}
}
}
The adapter class
public class KidAdapter extends FirebaseRecyclerAdapter<kidData, KidAdapter.KidViewHolder> {
public KidAdapter(#NonNull FirebaseRecyclerOptions<kidData> options) {
super(options);
}
#Override
protected void onBindViewHolder(#NonNull KidViewHolder holder, int position, #NonNull kidData model) {
holder.nama.setText(model.getKidName());
holder.balance.setText(model.getKidBalance());
holder.limit.setText("Limit: "+model.getKidLimit());
holder.spending.setText("Spending xxx.xxx");
}
#NonNull
#Override
public KidViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.kids_card,
viewGroup, false);
return new KidViewHolder(v);
}
public class KidViewHolder extends RecyclerView.ViewHolder {
TextView nama, balance, limit, spending;
public KidViewHolder(#NonNull View itemView) {
super(itemView);
nama = itemView.findViewById(R.id.tv_nama);
balance = itemView.findViewById(R.id.tv_balance);
limit = itemView.findViewById(R.id.tv_dailylimit);
spending = itemView.findViewById(R.id.tv_dailyspending);
}
}
}
The kidData model class
public class kidData {
String kidName, kidEmail, kidDoB, kidLimit, kidBalance;
public kidData(){
}
public kidData(String kidName, String kidEmail, String kidDoB, String kidLimit, String kidBalance) {
this.kidName = kidName;
this.kidEmail = kidEmail;
this.kidDoB = kidDoB;
this.kidLimit = kidLimit;
this.kidBalance = kidBalance;
}
public String getKidName() {
return kidName;
}
public String getKidEmail() {
return kidEmail;
}
public String getKidDoB() {
return kidDoB;
}
public String getKidLimit() {
return kidLimit;
}
public String getKidBalance() {
return kidBalance;
}
}
The problem in your code is the use of the following line of code:
recyclerView.setHasFixedSize(true);
And this is because when using the latest version of Firebase-UI library, there is no need to set the size of the RecyclerView as fixed. The solution for solving this problem is to simply remove/comment the above line of code. That's it!
I try to remove a specific item from a listView but it's always remove the last item.
I create a custom adapter to my listview.
I try to search for a solution and i found some posts about this problem but I still didn't success to solve the problem
custom adapter below:
public class ListViewAdapter extends BaseAdapter{
public ArrayList<HashMap<String, String>> list;
public static final String WORD_COLUMN="First";
public static final String TRAN_COLUMN="Second";
Activity activity;
TextView txtFirst;
TextView txtSecond;
public ListViewAdapter(Activity activity,ArrayList<HashMap<String, String>> list){
super();
this.activity=activity;
this.list=list;
}
#Override
public int getCount() {
return list.size();
}
#Override
public Object getItem(int position) {
return list.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater=activity.getLayoutInflater();
if(convertView == null){
convertView=inflater.inflate(R.layout.column_row, null);
txtFirst=(TextView) convertView.findViewById(R.id.wordColumn);
txtSecond=(TextView) convertView.findViewById(R.id.tranColumn);
}
HashMap<String, String> map=list.get(position);
txtFirst.setText(map.get(WORD_COLUMN));
txtSecond.setText(map.get(TRAN_COLUMN));
return convertView;
}
}
activity code below:
public class MainActivity extends AppCompatActivity{
private ListView lv;
private ArrayList<HashMap<String, String>> hashList;
private ListViewAdapter adapter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView)findViewById(R.id.gvWords);
hashList = new ArrayList<HashMap<String, String>>();
adapter=new ListViewAdapter(this, hashList);
lv.setAdapter(adapter);
onClickButtons();
}
public void onClickButtons()
{
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
hashList.remove(i);
adapter.notifyDataSetChanged();
}
});
}
Thank's :)
You can try to access and remove clicked object via getItemAtPosition(position) and then remove it from ArrayList via .remove(Object o)
Your listener will therefore look like this:
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
hashList.remove(lv.getItemAtPosition(i));
adapter.notifyDataSetChanged();
}
});
This approach is mentioned in official doc: https://developer.android.com/reference/android/widget/AdapterView.OnItemClickListener.html
Implementers can call getItemAtPosition(position) if they need to access the data associated with the selected item.
after refreshing it's remove the right item
You've cached the two TextViews, which aren't updated as part of notifying the adapter.
Alter the convertView check and remove those fields, then lookup how to implement the ViewHolder pattern (or use a RecyclerView)
I found this problem,
and removed the if(convertView == null){ It's working,
Please let us know for this reason.