Unable to restore the selection state of spinner - android

I'm using AppCompatSpinner with the ArrayAdapter. The data of the arrayAdapter is coming from room db and wrapped into livedata in ViewModel class.
Everything is working fine but when i navigate to other fragment and come back to Spinner fragment, the selection state is gone (and it set to selection = 0).
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mViewModel = ViewModelProviders.of(this, mViewModelFactory).get(InquiryViewModel.class);
mViewModel.getItemLiveData().observe(getViewLifecycleOwner(), items -> {
dropdownItemArrayAdapter = new ArrayAdapter<>(requireContext(), android.R.layout.simple_spinner_item, items);
dropdownItemArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
itemType.setAdapter(dropdownItemArrayAdapter);
});
}

Related

Populating spinner with EditText from another fragment in Android

I have two fragment.In first one is EditText witch taking some info from user,and in another fragment having Spinner.I am sending EditText,that was first converted to String, to Spinner in second fragment with Android Navigation.And it shows text just fine but when i wish to add another text to Spinner it just replace first text and showing only one text/item in spinner. So my question how to have save text that i previously sent and have nice drop down spinner. BTW i am using data binding i don't know if it is important information for solutions.
fragment from where i am sending data:
#Override
public void onClickComplete() {
String addCategoryString = addCategory.getText().toString();
FluctuatingCategoryFragmentDirections.ActionFluctuatingCategoryToCreatingBudgetingItem2 action
= FluctuatingCategoryFragmentDirections.actionFluctuatingCategoryToCreatingBudgetingItem2();
action.setFluctuatingCategory(addCategoryString);
navController.navigate(action);
}
receiving fragment:
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (getArguments() != null) {
CreatingBudgetingItemFragmentArgs args = CreatingBudgetingItemFragmentArgs.fromBundle(getArguments());
String getCategory = args.getFluctuatingCategory();
Log.i("Category: ", getCategory);
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item, android.R.id.text1);
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(spinnerAdapter);
spinnerAdapter.notifyDataSetChanged();
}
}
When you are navigating to FluctuatingCategoryToCreatingBudgetingItem, the fragment is getting recreated every time, that's why only the value you passed will show up. You must use ViewModel.
Setup a list which will contain the FluctuatingCategoryies and setup your CreatingBudgetingItemFragment to get list item from ViewModel.
Not giving you any code because I want you to start your learning curve. This
might help.

How to store recyclerview data , while changing view (Fragments) via Bottom Navigation?

I am following this question
how to store recyclerview data on onSaveInstanceState
I also found
How to save state of view class? and Fragment save view state.
Context
I give data in form of DataModel (implements Parcelable) to recyclerview in one of my Fragments.
using Bottom navifation and ROOM DB (to get and save Data).
what I have done yet
I used the code in first link and in my code. But I couldn't understand the fourth peace of code, which was used in there (I don't have a response.boddy(), Error).
Anyway every time changing the view savedInstanceState = null so the code is being redone.
what I want or question
I would like to not redo the work every time changing the view via bottom navigation?
what am I doing wrong, that data are not being saved in savedInstanceState?
my Fragment view
private ArrayList<DataModel> data;
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
root = inflater.inflate(R.layout.fragment_home, container, false);
currentContext = getContext();
recyclerView = (RecyclerView) root.findViewById(R.id.recyclerViewHome);
recyclerView.setHasFixedSize(true);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext());
recyclerView.setLayoutManager(layoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
this.data = new ArrayList<>();
adapter = new CustomAdapter(data, currentContext, 1);
recyclerView.setAdapter(adapter);
if (savedInstanceState != null) {
// Retrieve the data you saved
data = savedInstanceState.getParcelableArrayList("saved_data");
//Call method to reload adapter record
recyclerViewsaveInstance(data);
}
else {
//No data to retrieve
dataAsynTask; //deleted, Basicly I get the data from DB, convert it to DataModel and give them to recyclerview.
}
return root;
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
Log.i("savedInstanceState", "loading");
savedInstanceState.putParcelableArrayList("saved_data", this.data);
super.onSaveInstanceState(savedInstanceState);
}
public void recyclerViewsaveInstance(ArrayList<DataModel> dataset)
{
this.data = dataset;
adapter = new CustomAdapter(dataset, getContext(), 1);
recyclerView.setAdapter(adapter);//notify adapter about the new record
adapter.notifyDataSetChanged();
}
I think what you want to do here is to move your AsyncTask out to the Activity level, and have your Fragment ask the Activity for the List<DataModel>.
In your Fragment's onCreateView() method, instead of writing
this.data = new ArrayList<>();
and then checking savedInstanceState and conditionally executing an AsyncTask, you could write this:
this.data = ((MyActivity) getActivity()).getData();
Then, in your Activity, you'd implement this getData() method:
public List<DataModel> getData() {
if (data != null) {
return data;
} else {
// kick off AsyncTask here
return Collections.emptyList();
}
}
When the AsyncTask finishes, have it update the Activity's data field and also have it notify the Fragment that new data is available. In the Fragment, you'd have a method like this:
public void onDataFetched(List<DataModel> data) {
this.data = data;
adapter.notifyDataSetChanged();
}
The end result of all of the above is that the Activity is now responsible for loading the data and for saving it. The Fragment doesn't know anything about the database or caching; it just knows to ask the Activity for the data whenever it is shown.
Since the Activity stays alive while you switch between Fragments with your bottom nav, it will successfully keep the data alive when you switch tabs.

Refreshing a recyclerview in a fragment

I have 2 activities: RoutineViewer and ExerciseViewer. Both activities contain fragments that are sub-classes of ParentFragment.java.
RoutineViewer's fragment (RoutineViewerFragment) is displaying a recyclerview. The recyclerview's data is from a static ArrayList in ParentFragment.java.
From RoutineViewerFragment, I can start the ExerciseViewer activity, and the fragment inside it (ExerciseViewerFragment) is used to make changes to customRoutineExercise in ParentFragment.java. After making the changes in ExerciseViewerFragment, I can call requireActivity().finish() on ExerciseViewer.java, which will take me back to RoutineViewerFragment. However, my problem is that the data in RoutineViewerFragment is not reflecting the changes I made in ExerciseViewerFragment.
Some solutions I have tried (but didn't work in this case) from looking at other similar problems:
// In RoutineViewerFragment onResume() with if else conditions
mAdapter.notifyItemChanged(// insert relevant position);
// In RoutineViewer.java onResume() with if else
getSupportFragmentManager().beginTransaction.detach(currentfragment).attach(currentfragment).commit()
I'm guessing that the reason why mAdapter.notifyItemChanged(position) isn't working is because I'm not directly passing the static customRoutineExercise in the constructor of mAdapter, but rather a value of the HashMap instead.
As for the .detach().attach(), I've read that it forces the onCreateView() to run again, which theoretically should refresh the recyclerview with the updated data, but it isn't.
I've left the important code segments below. Any help is appreciated, thanks!
ParentFragment.java
protected static int selectedCustomRoutine;
protected static int selectedCustomExercise;
protected static ArrayList<customRoutine> customRoutineInfo = new ArrayList<>();
protected static HashMap <Integer, ArrayList<customExercise>> customRoutineExercise = new HashMap<>();
RoutineViewer.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_routine_viewer);
Intent intent = getIntent();
routineType = intent.getStringExtra("routineType");
routinePosition = intent.getIntExtra("routinePosition", 0);
selectedFragment = new RoutineViewerCustomFragment(routinePosition);
getSupportFragmentManager().beginTransaction().replace(R.id.routineV_fragment_container, selectedFragment).commit();
break;
}
RoutineViewerFragment.java
private int routinePosition;
public RoutineViewerCustomFragment(int routinePosition) {
// constructor
this.routinePosition = routinePosition;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_routine_viewer_custom, container, false);
buildRecyclerView(rootView);
return rootView;
}
// other code
private void buildRecyclerView(View rootView) {
mRecyclerView = rootView.findViewById(R.id.recyclerView);
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(getActivity());
mAdapter = new RoutineViewerCustomAdapter(customRoutineExercise.get(routinePosition));
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mAdapter);
}
// Other code
private void goExerciseViewer(int position) {
selectedCustomExercise = //number;
selectedCustomRoutine = //number;
}
ExerciseViewerFragment.java
private void saveChanges() {
customRoutineExercise.get(selectedCustomRoutine).get(selectedCustomExercise).setSetNumber(Integer.parseInt(editText1.getText().toString()));
customRoutineExercise.get(selectedCustomRoutine).get(selectedCustomExercise).setRepNumber(Integer.parseInt(editText2.getText().toString()));
}
Code Smell: The recyclerview's data is from a static ArrayList in ParentFragment.java.
Don't do this.
Have a repository where you store your "recycler view list" (exercises?), and have the recyclerView request (or observe if you use a ViewModel) a list.
On the fragment where you modify this list, have said fragment talk to the same repository either via its own viewModel or a shared one or whatever you use (search for Android ViewModels jetPack if unfamiliar).
This way, when the recyclerView comes back, it will ask for a new list (and will get the updated copy) or if observing, it will be notified that a new list is available.
The list you pass to the adapter is/should be a copy/immutable.
This is not the same but it's an Activity that has a RecyclerView and "asks" directly for the data to a repo. (This example has nothing to do with your problem, but notice how the adapter is dummy, it's just told to display the list).
https://github.com/Gryzor/CheckBoxCounter/blob/master/app/src/main/java/com/androidonlinux/checkboxcounter/MainActivity.kt

Communicate between Adapter, fragments and Sqlite Data base to refresh Fragments

I have three fragments that contains three ListView created using a BaseAdapter. Like the image below:
Anyway my listView is fetching data from an SqliteDatabase. What I need to know is: when I delete an item from my ListView My(Favorite,Rejected) Fragments ListViews are not notified and are not refreshing.
What I have tried so far is :
Call listView.invalidateViews() after notifyDataSetChanged() in the onResume() Method of my fragments .
I tried these solution two Android ListView not refreshing after notifyDataSetChanged
My code is :
In My BaseAdapter I'am using these method to refresh my adapter :
public void UpdateView(List<Voiture> items)
{
this.voitureList = items;
notifyDataSetChanged();
}
In My fragments I'am using these method to notify the adapter :
#Override
public void onResume() {
super.onResume();
adapterLogin.UpdateView(databaseHelper.getAllVoiture(username,currentLength));
listView.setAdapter(new AdapterLogin(getActivity(),voitureList,username,currentLength,1));
}
In the OncreateView() Method I'am using :
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
inflate = inflater ;
x = inflater.inflate(R.layout.fragemnt_favoris,null);
empty = (TextView) x.findViewById(R.id.texteempty);
listView = (ListView) x.findViewById(R.id.list);
activity = (Main2Activity) getActivity() ;
username = activity.getUsername();
databaseHelper = new DatabaseHelper(getActivity());
databaseHelper.InsertActivity(2,username);
voitureList = databaseHelper.getAllVoitureFavourite(1,username);
adapterLogin = new AdapterLogin(getActivity(),voitureList,username,currentLength,2);
if (voitureList.size()>0)
{
listView.setAdapter(adapterLogin);
((BaseAdapter)listView.getAdapter()).notifyDataSetChanged();
}
else
{
empty.setVisibility(View.VISIBLE);
}
// Inflate the layout for this fragment
return x;
}
Any help would be greatly appreciated.
Thanks to Paul answer the idea was to add the notifyDataSetInvalidated in my UpdateView() Method in my adapter and it works just fine and my fragments are refreshing correctly now :
void notifyDataSetInvalidated ()
the method Notifies the attached observers that the underlying data is no longer valid or available. Once invoked this adapter is no longer valid and should not report further data set changes.
refer link :
notifyDataSetInvalidated

Cursor Adapter for the List view is not showing most recent values

I am using a SimpleCrusorAdaptor to display a list of items in a list view. When an item in listview is selected it starts an other activity, this activity changes the puzzle status which is shown in the list view. When this activity terminates and activity containing the list view again becomes active, list view shows the old status,
How can I ensure if the activity containing list view resumes, the cursor adapter updated the values ? Some thing to be done in onResume() of the cativity ?
private static String[] FROM = { PuzzleDatabase.KEY_PUZZLE_TITLE,
PuzzleDatabase.KEY_PUZZLE_STATUS };
private static int[] TO = { R.id.puzzle_title, R.id.puzzle_status };
ListView listView = (ListView) findViewById(R.id.puzzle_list);
this.cursor = ps.puzzleDatabase.getPuzzleTitles();
// Set up data binding
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
R.layout.puzzle_list_row, cursor, FROM, TO);
// Assign adapter to ListView
listView.setAdapter(adapter);
for the updating values try doing this
class YourClass extends Activity{
//other members
private Bundle savedInstanceState; //add this to your code
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.savedInstanceState = savedInstanceState; //add this to your code
//your other code here
}
#Override
protected void onResume() { //add this function to your code
datasource.open(); //change datasource to your own database class's object
super.onResume();
onCreate(savedInstanceState);
}
}
The cursor doesn't change its contents, just because the underlying dataset changes its contents! The cursor contains the data that resulted from the query, at the time the query was made.
You need a Loader. You can see find example code here:
https://github.com/marakana/yamba/blob/yambaII/Yamba/src/com/marakana/android/yamba/TimelineActivity.java
You must, first, initialize the loaderManager. Next hand it a loader when it calls you back. Finally, you must swap the loader into the adapter when you it calls you back after the loader has run.
Of course, you have to notify the cursor that it is out of date, too. There is example code for that, here:
https://github.com/marakana/yamba/blob/yambaII/YambaService/src/com/marakana/android/yamba/svc/data/YambaProvider.java
See, e.g., line 182

Categories

Resources