RoomDB synchronization between Fragments in Android - android

I have an app which is storing data in Room Database. This is an movie app and when user add new movie to his list, it doesn't work in synchronization. When I close app and open again, list will being updated. Users adding film button is in one fragment, list is in another fragment. They are not in same fragment. It add database without problem but I need to make synchronization between these two fragments.
This is List Fragment.
public class ListFragment extends Fragment {
ImageView listTitleImage;
RecyclerView recyclerView;
List<MainData> dataList = new ArrayList<>();
LinearLayoutManager linearLayoutManager;
RoomDB database;
RecyclerViewFavoritesAdapter adapter;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_list,container,false);
listTitleImage = v.findViewById(R.id.listTitleImage);
recyclerView = v.findViewById(R.id.recyclerViewFavorites);
database = RoomDB.getInstance(v.getContext());
dataList = database.mainDao().getAll();
linearLayoutManager = new LinearLayoutManager(v.getContext());
recyclerView.setLayoutManager(linearLayoutManager);
adapter = new RecyclerViewFavoritesAdapter(v.getContext(),dataList);
recyclerView.setAdapter(adapter);
return v;
}
}
This is menu click method which includes adding in list.
#Override
public boolean onMenuItemClick(MenuItem item) {
if(item.getItemId() == R.id.menuAdd){
int movie_id = resultList.get(pos).getMovieId();
String title = resultList.get(pos).getTitle();
String posterpath = resultList.get(pos).getPoster_path();
database = RoomDB.getInstance(context);
MainData mainData = new MainData();
mainData.setMovie_id(movie_id);
mainData.setMovie_name(title);
mainData.setMovie_poster(posterpath);
int movie_id_in_database = database.mainDao().get_movie_id(movie_id);
if(movie_id_in_database == 0){
database.mainDao().insert(mainData);
} else{
Toast.makeText(context, "You already have that movie in list !", Toast.LENGTH_SHORT).show();
}
return true;
}
else if(item.getItemId() == R.id.menuShowDetails){
int movieId = resultList.get(pos).getMovieId();
// Intent intent1 = new Intent(context, MovieDetails.class);
// intent1.putExtra("movie_id",movieId);
// context.startActivity(intent1);
return true;
}
return false;
}

Related

Why my recyclerView doesn't display the new data after the adapter notify data is changed?

In the parent activity, I have an edit text in a toolbar, and user can make a search through the data displayed by the recyclerview.
When the user push enter key down, the string in the edittext is sent to the fragment by :
Bundle bundle = new Bundle();
bundle.putString(predResult, placeid);
MapFragment mapFragment = new MapFragment();
ListRestFragment listRestFragment = new ListRestFragment();
mapFragment.setArguments(bundle);
listRestFragment.setArguments(bundle);
getSupportFragmentManager().beginTransaction()
.replace(R.id.map, mapFragment)
.replace(R.id.list_frag, listRestFragment)
.commit();
but, unfortunatly, the recyclerview is not resfreshed while my adapter is notified the data is changed as it shown below:
private void queryToList(Query query) {
query.addSnapshotListener((queryDocumentSnapshots, e) -> {
restaurantList = queryDocumentSnapshots.toObjects(Restaurant.class);
if (!myPrediction.contains("myPrediction")) {
System.out.println(myPrediction);
for (Restaurant item : restaurantList) {
if (item.getRestaurantID().contains(myPrediction)) {
restaurantListPred = new ArrayList<>();
restaurantListPred.add(item);
updateUI(restaurantListPred);
}
}
} else updateUI(restaurantList);
});
}
private void updateUI(List<Restaurant> restaurants) {
configureFab();
configureRecyclerView(restaurants);
}
private void configureRecyclerView(List<Restaurant> restaurant) {
this.adapter = new RestaurantAdapterClassic(restaurant);
this.recyclerView.setAdapter(this.adapter);
this.recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL);
recyclerView.addItemDecoration(new SimpleItemDecorator(getContext()));
}
the new List is updated automatically when the user makes his request, but the recyclerView doesn't display the new data.
if you want to check my adapter implementation:
public class RestaurantAdapterClassic extends RecyclerView.Adapter<RestaurantViewHolder> {
private List<Restaurant> restaurantsList;
// CONSTRUCTOR
public RestaurantAdapterClassic(List<Restaurant> restaurants) {
this.restaurantsList = restaurants;
}
#Override
public RestaurantViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// CREATE VIEW HOLDER AND INFLATING ITS XML LAYOUT
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.listview_pattern, parent, false);
return new RestaurantViewHolder(view);
}
#Override
public void onBindViewHolder(RestaurantViewHolder viewHolder, int position) {
viewHolder.updateWithRestaurant(this.restaurantsList.get(position));
}
// RETURN THE TOTAL COUNT OF ITEMS IN THE LIST
#Override
public int getItemCount() {
return this.restaurantsList.size();
}
public Restaurant getRestaurant(int position) {
return this.restaurantsList.get(position);
}
public void filterList(List<Restaurant> filteredList) {
restaurantsList = filteredList;
notifyDataSetChanged();
}
}
where is my error or my misunderstanding?
EDIT SOLUTION -
Create an Interface
Actually, to send the new data data from my Parent Activity to my Fragment with a listener to observe when the data changes.
Keep The data reference sent to the adapter
Actually, the main big problem that I had was my adapter doesn't refresh when I sent new array. The reason was an adapter creates a reference with the list/array. if you want to refresh it, you need to keep this reference by get the list, erase it, and put/add new data inside by the method addALL for example.
First of all, adapter.notifyDataSetChanged(); doesn't have any effect in the code as inside updateUI you create the adapter every time you call it.
I think, the main problem is in here:
if (item.getRestaurantID().contains(myPrediction)) {
restaurantListPred = new ArrayList<>();
restaurantListPred.add(item);
updateUI(restaurantListPred);
}
This block doesn’t execute at all. Thats why the list not updated.

RecyclerView Not Showing Data Passed From List Through Bundle

I have a recyclerview in a dialog fragment, which hosts a tab layout, containing a list of checkable items and a button each. When an item is selected, it is passed on click through the bundle to this parent dialog fragment like this:
doneBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
retrieveSessions();
}
});
private void retrieveSessions() {
NewProgramDialog dialog = new NewProgramDialog(); // parent dialog fragment is passed the list items here
Bundle bundle = new Bundle();
String s = new Gson().toJson(myItemsList);
bundle.putString(KEY_MY_SESSIONS, s);
bundle.putBoolean("from_selections", true);
dialog.setArguments(bundle);
dialog.show(getChildFragmentManager(), "NewProgramDialog");
dismiss();
}
The data for recyclerview is received through a bundle lie shown above and the list items are retrieved using Gson and converted to type String, a query is run on the database to get the session model class info.
These sessions are added to the adapter arraylist like below:
private void populateSessions() {
addProgramSessionsRV.setHasFixedSize(true);
addProgramSessionsRV.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false));
// TODO: 2/05/2019 show the added sessions here
Bundle bundle = getArguments();
if (bundle != null) {
String s = bundle.getString(KEY_MY_SESSIONS); / getting the items from bundle
myItemsList = new Gson().fromJson(s, ArrayList.class); // parsing to List<String> type
Log.d(TAG, "Items size:\t" + myItemsList.size()); //works
}
adapter = new AddProgramSessionsAdapter(getActivity(), sessionsList);
addProgramSessionsRV.setAdapter(adapter);
Log.d(TAG, "Items in adapter:\t" + adapter.getItemCount()); // shows total items received from bundle
for (String s : myItemsList){
Log.d(TAG, "Ids:\t" + s);
Sessions session = SQLite.select()
.from(Sessions.class)
.where(Sessions_Table.prog_sessionId.eq(s))
.querySingle();
sessionsList.add(session);
Log.d(TAG, "Session List Size:\t" + sessionsList.size());
adapter = new AddProgramSessionsAdapter(getActivity(), sessionsList);
addProgramSessionsRV.setAdapter(adapter);
Log.d(TAG, "Items in adapter:\t" + adapter.getItemCount());
}
}
Still the ui shows no items from this method. I have added the data manually using this snippet and it shows Session 1 item in view but not the data from db in for loop.
Test Case:
Sessions sessions = new Sessions();
sessions.setSession_name("Test one");
sessions.setProgram_id("234");
sessions.setProg_sessionId("91");
sessionsList.add(sessions);
adapter = new AddProgramSessionsAdapter(getActivity(), sessionsList);
addProgramSessionsRV.setAdapter(adapter);
Log.d(TAG, "Items in adapter:\t" + adapter.getItemCount());
Is there any reason why setting the adapter to a list which has items does not work in the for loop db query (works)? Please explain to me why the ui shows nothing. Thanks.
Here's the adapter code:
public class AddProgramSessionsAdapter extends RecyclerView.Adapter<AddProgramSessionViewHolder> {
private final Context context;
private List<Sessions> itemsList;
public AddProgramSessionsAdapter(Context context, List<Sessions> itemsList) {
this.context = context;
this.itemsList = itemsList;
}
#Override
public AddProgramSessionViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.add_program_sessions_items_layout, parent, false);
return new AddProgramSessionViewHolder(view);
}
#Override
public void onBindViewHolder(AddProgramSessionViewHolder viewholder, int position) {
Sessions sessions = itemsList.get(position);
viewholder.add_program_sessionNameTV.setText(sessions.session_name);
Picasso.with(context)
.load(R.drawable.app_logo_resized)
.into(viewholder.add_program_sessionIV);
}
#Override
public int getItemCount() {
if (itemsList == null) {
return 0;
}
return itemsList.size();
}
}

FirestoreRecyclerAdapter - how do I know when data has been retrieved?

I'm retrieving data using a FirestoreRecyclerAdapter, and, on completion, I need to check whether any items have been retrieved or not. I can't figure out how to do this.
I'm calling it from a class called FragmentChartsList, shown below. This should set up the adapter initially, with "name" as the value for mOrder. Later, the Activity which contains this Fragment can call setOrderField() with a different value of mOrder, which the user has selected from a Spinner.
Each time setOrderField() is called, a new adapter instance is created and attached to the recyclerView. At this point I need to check whether the new version of the adapter contains any data, and either show a "no Charts found" message, or show the Charts which were retrieved (obviously if the list is just being sorted, then the number of items remains the same, but I'm going to be expanding this to allow the user to filter the Charts by different criteria, so the number of Charts returned will change).
Currently, setOrderField() calls refreshViewOnNewData(), which should find out how many Charts are being shown; if it's 0, it should show the "no Charts found" message, and if it's >0 it should show the RecyclerView containing the Charts.
At the moment, I'm always getting a value of 0 when I try to count the Charts. I suspect it's because the adapter hasn't finished retrieving them from the database yet, but I can't find anything that allows me to add some kind of "onComplete" listener so that I know it's finished.
Can anyone suggest how I can achieve this?
public abstract class FragmentChartsList extends Fragment {
private FirebaseFirestore mDatabaseRef;
private ChartListAdapter mAdapter;
private Query mChartsQuery;
private RecyclerView mRecycler;
private String mOrder = "name";
private TextView mLoadingList, mEmptyList;
public FragmentChartsList() {}
#Override
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View rootView = inflater.inflate(
R.layout.fragment_charts_list, container, false);
mRecycler = rootView.findViewById(R.id.charts_list);
mRecycler.setHasFixedSize(true);
mLoadingList = rootView.findViewById(R.id.loading_list);
mEmptyList = rootView.findViewById(R.id.empty_list);
// Set up Layout Manager, and set Recycler View to use it
LinearLayoutManager mManager = new LinearLayoutManager(getActivity());
mManager.setReverseLayout(true);
mManager.setStackFromEnd(true);
mRecycler.setLayoutManager(mManager);
// Connect to the database
mDatabaseRef = FirebaseFirestore.getInstance();
setOrderField(mOrder); // Initialised to "name"
return rootView;
}
#Override
public void onStart() {
super.onStart();
mAdapter.startListening();
}
#Override
public void onStop() {
super.onStop();
mAdapter.stopListening();
}
#Override
public void onDestroy() {
super.onDestroy();
mAdapter.stopListening();
}
// HELPER FUNCTIONS
public void setOrderField(String order) {
mOrder = order;
mChartsQuery = getQuery(mDatabaseRef, mOrder);
// Update recycler options
FirestoreRecyclerOptions<Chart> recyclerOptions = new FirestoreRecyclerOptions.Builder<Chart>()
.setQuery(mChartsQuery, Chart.class)
.build();
mAdapter = new ChartListAdapter(recyclerOptions, getActivity());
mAdapter.startListening();
mRecycler.swapAdapter(mAdapter, true);
refreshViewOnNewData();
}
private void refreshViewOnNewData() {
// Hide "loading" text
mLoadingList.setVisibility(View.GONE);
// Check number of charts being shown
//if (mAdapter != null && (mAdapter.getCount() > 0)) {
// If > 0, show Charts
mEmptyList.setVisibility(View.GONE);
mRecycler.setVisibility(View.VISIBLE);
} else {
// If number of Charts = 0
// show "no charts"
mEmptyList.setVisibility(View.VISIBLE);
mRecycler.setVisibility(View.GONE);
}
}
}
The adapter class looks like this:
public class ChartListAdapter extends FirestoreRecyclerAdapter<Chart, ChartViewHolder> {
private Activity mActivity;
private int mCount;
public ChartListAdapter(FirestoreRecyclerOptions<Chart> recyclerOptions, Activity activity) {
super(recyclerOptions);
mActivity = activity;
}
#Override
protected void onBindViewHolder(#NonNull ChartViewHolder holder, int position, #NonNull Chart model) {
final String chartKey = this.getSnapshots().getSnapshot(position).getId();
model.setKey(chartKey);
// Set click listener for the chart
// On click, the user can view the chart
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(mActivity, ActivityViewChart.class);
intent.putExtra("ChartKey", chartKey);
mActivity.startActivity(intent);
}
});
// Implement long-click menu
mActivity.registerForContextMenu(holder.itemView);
// Bind Chart to ViewHolder
holder.bindToChart(model);
}
#NonNull
#Override
public ChartViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_chart, parent, false);
return new ChartViewHolder(view);
}
#Override
public void onDataChanged() {
super.onDataChanged();
mCount = getItemCount();
}
public int getCount() {
return mCount;
}
}
Figured this out... I needed to set a listener on the query instead.
So, instead of having the call to refreshViewOnNewData from setOrder above, I now have:
mChartsQuery.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot queryDocumentSnapshots, #Nullable FirebaseFirestoreException e) {
if (queryDocumentSnapshots != null) {
mLoadingList.setVisibility(View.GONE);
if(queryDocumentSnapshots.size() > 0) {
mEmptyList.setVisibility(View.GONE);
mRecycler.setVisibility(View.VISIBLE);
}else {
mEmptyList.setVisibility(View.VISIBLE);
mRecycler.setVisibility(View.GONE);
}
}
}
});
}
Also removed mCount from the adapter class, along with getCount and onDataChanged

Refresh fragment listview on Activity back button press

I have a fragment which contains a listview of some items. On click of an item in the list view an Activity is started to view the details about the list view. There is a back button on the Activity which brings us back to the fragment page. Once the back button is pressed and the activity page closes I want the data to be refreshed in the fragment list view. I have tried onResume() method but it does not refresh the data.
Can someone please help me how to achieve this am pretty new at this.
public class FavCommittee extends Fragment {
public ArrayList<Committee> favCommitteeData = new ArrayList<>();
#Nullable
#Override
public void onResume(){
super.onResume();
final SharedPreferences pref = getContext().getSharedPreferences("MyFav", 0);
final SharedPreferences.Editor editor = pref.edit();
View rootView =getView();
Map<String,?> entries = pref.getAll();
final Set<String> keys = entries.keySet();
int count=0;
for (String key:keys) {
String val = pref.getString(key, null);
String[] store = val.split(":");
if (store[0].equals("committee"))
count=count+1;
}
for (int i=0;i<keys.size();i++) {
String val = pref.getString(Integer.toString(i), null);
if (val != null) {
String[] store = val.split(":");
if (store[0].equals("committee")) {
count = count - 1;
new GetAllCommittees(getContext(), rootView).execute(store[1], Integer.toString(count));
}
}
} final ListView yourListView = (ListView) rootView.findViewById(R.id.house_listview);
yourListView.setOnItemClickListener(new android.widget.AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,int position, long id) {
Committee obj = (Committee)yourListView.getItemAtPosition(position);
Intent intent = new Intent(FavCommittee.this.getActivity(), committee_info.class);
intent.putExtra("Committee",obj.getCommittee_id());
startActivity(intent);
}
});
}
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.house, container, false);
return rootView;
}
When you are transitioning between Fragments, call addToBackStack() as part of your FragmentTransaction:
FragmentTransaction tx = fragmentManager.beginTransation();
tx.replace( R.id.fragment, new MyFragment() ).addToBackStack( "tag").commit();
If you require more detailed control (i.e. when some Fragments are visible, you want to suppress the back key) you can set an OnKeyListener on the parent view of your fragment:
//You need to add the following line for this solution to work,
fragment.getView().setFocusableInTouchMode(true);
fragment.getView().requestFocus();
fragment.getView().setOnKeyListener( new OnKeyListener()
{
#Override
public boolean onKey( View v, int keyCode, KeyEvent event )
{
if( keyCode == KeyEvent.KEYCODE_BACK )
{
return true;
}
return false;
}
} );
getSupportFragmentManager().beginTransaction().add(R.id.fragContainer, new FavCommittee (), FavCommittee .class.getName()).commit();
Try this code.
Put the code where u add your Fragment in Activity

Cannot filter RecyclerView using Spinner

I'm an Android developer and I'm facing a kind of awkward situation here: I filled up a spinner with static values and I'm playing around with the items in the spinner to filter a list of strings made of static values as well (It's actually a list of countries from A-Z). The issue is with the filtering itself. Whenever I'm testing for the occurrence of a country in my list I keep on having false being returned. Here is the code:
private RecyclerView mRecyclerView;
private Spinner mSpinner;
private List<String> countries = new ArrayList<>();
private TestCountriesAdapter adapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_country, container, false);
countries = Arrays.asList(getActivity().getResources().getStringArray(R.array.countries_array));
// This List is Not Empty !!.. I already cross-checked over and over
return rootView;
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
mRecyclerView = (RecyclerView)view.findViewById(R.id.recyler_view);
mSpinner = (Spinner)view.findViewById(R.id.spinner_projects);
initRecyclerView();
mSpinner.setOnItemSelectedListener(this);
}
private void initRecyclerView()
{
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
adapter = new TestCountriesAdapter(getActivity(), countries);
mRecyclerView.setAdapter(adapter);
}
Here is the issue:
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
{
Toast.makeText(getActivity(), "Size of List = " + countries.size(), Toast.LENGTH_SHORT).show();
// Here again the List is Non-empty
List<String> filteredList = new ArrayList<>();
switch (position)
{
case 1:
for (String country: countries)
{
if (country.startsWith("A")
|| country.startsWith("B")
|| country.startsWith("C")
|| country.startsWith("D")
|| country.startsWith("E")
)
filteredList.add(country);
}
break;
case 2:
for (String country: countries)
{
if (country.startsWith("F")
|| country.startsWith("G")
|| country.startsWith("H")
|| country.startsWith("I")
|| country.startsWith("j")
)
filteredList.add(country);
}
break;
case 3:
for (String country: countries)
{
if (country.startsWith("K")
|| country.startsWith("L")
|| country.startsWith("M")
|| country.startsWith("N")
|| country.startsWith("O")
)
filteredList.add(country);
}
break;
default:
break;
}
countries = filteredList;
adapter.notifyDataSetChanged();
}
In each of the "cases" whenever I do country.startsWith ("a letter"), I get false being returned.
I really don't know I keep on having this "false".
What did I do wrong ??...what was my mistake ??

Categories

Resources