RecyclerView restore items and managing pagination after rotation - android

I'm wondering if there is a way to restore, after rotation, my list of results without retaining the fragment.
Basically I have a fragment which calls, through a presenter, some api (using RxJava and Retrofit). I added pagination so I can make a call only when I need more data scrolling down.
I'm in the following scenario:
- I scroll the list down in order to call the second page from the web
- I rotate the screen
In this case what I would need is to show all the items, from page 1 and 2, and then scrolling to the correct position (for this I can use the onSaveInstanceState of the LayoutManager).
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FwApplication.component(getActivity()).inject(this);
if (savedInstanceState != null) {
mRxRunning = savedInstanceState.getBoolean(EXTRA_RX);
mQuery = savedInstanceState.getString(QUERY_ARG);
mCurrentPage = savedInstanceState.getInt(CURRENT_PAGE);
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putString(QUERY_ARG, mQuery);
outState.putInt(CURRENT_PAGE, mCurrentPage);
outState.putParcelable(LAYOUT_STATE, mRecyclerView.getLayoutManager().onSaveInstanceState());
super.onSaveInstanceState(outState);
}
#Override
public void onResume() {
super.onResume();
mPresenter.onResume(mRxRunning, mCurrentPage);
}
Is there a way to save the items without calling setRetainInstance? Moreover I would avoid to call the api in order to get all the items back.
The items are POJOs so it won't be a list of simple strings.

Try to edit the Manifest file. Add this line to block with activity:
android:configChanges="keyboardHidden|orientation|screenSize">
It will prevent Activity restart on such events (specially on orientation change)

Related

RecyclerView Data not visible when back button pressed or app closed

onSaveInstanceState and onRestoreInstanceState are not working. whenever i press back button or come out of the app the data of custom recyclerView is not visible until upload the next file. when i upload all the data comes back as I have stored it in shared preference.
Oncreate:
if (savedInstanceState != null) {
// Restore value of members from saved state
savedInstanceState.get(String.valueOf(savedInstanceState));
}
else
{
//initialize members with default values for a new instance
setContentView(R.layout.activity_resource);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
#Override
public void onRestoreInstanceState(#Nullable Bundle savedInstanceState, #Nullable PersistableBundle persistentState) {
super.onRestoreInstanceState(savedInstanceState, persistentState);
}
onSaveInstanceState stores view state automatically if all views inside layout has id.
For data restoring inside ListAdapter I would suggest sroring list inside ViewModel or at least Presenter depend on architecture you are using.

Showing fragment after activity fetches data

I'm fetching data in my activity that is needed by several fragments. After the data is returned, I create the fragments. I was doing this via an AsyncTask, but it led to occasional crashes if the data returned after a screen rotation or the app is backgrounded.
I read up and thought the solution to this was instead using an AsyncTaskLoader. Supposedly it won't callback if your activity's gone, so those errors should be solved. But this now crashes every time because "Can not perform this action (add fragment) inside of onLoadFinished".
How am I supposed to handle this? I don't want my fragments to each have to fetch the data, so it seems like the activity is the right place to put the code.
Thanks!
Edit 1
Here's the relevant code. I don't think the problem is with the code per-se, but more of my whole approach. The exception is pretty clear I shouldn't be creating fragments when I am. I'm just not sure how to do this otherwise.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportLoaderManager().initLoader(BREWERY_LOADER, null, this).forceLoad();
}
//================================================================================
// Loader handlers
//================================================================================
#Override
public Loader<Brewery> onCreateLoader(int id, Bundle args) {
int breweryId = getIntent().getIntExtra(EXTRA_BREWERY_ID, -1);
return new BreweryLoader(this, breweryId);
}
#Override
public void onLoadFinished(Loader<Brewery> loader, Brewery data) {
if (data != null) {
onBreweryReceived(data);
} else {
onBreweryError();
}
}
#Override
public void onLoaderReset(Loader<Brewery> loader) {
}
...
protected void onBreweryReceived(Brewery brewery) {
...
createFragments();
}
...
protected void createFragments() {
FragmentManager fm = getSupportFragmentManager();
//beers fragment
mBeersFragment = (BreweryBeersFragment)fm.findFragmentById(R.id.beersFragmentContainer);
if (mBeersFragment == null) {
mBeersFragment = new BreweryBeersFragment();
fm.beginTransaction()
.add(R.id.beersFragmentContainer, mBeersFragment)
.commit();
Bundle beersBundle = new Bundle();
beersBundle.putInt(BreweryBeersFragment.EXTRA_BREWERY_ID, mBrewery.getId());
mBeersFragment.setArguments(beersBundle);
}
}
Edit 2
My new strategy is to use an IntentService with a ResultReceiver. I null out callbacks in onPause so there's no danger of my activity being hit when it shouldn't be. This feels a lot more heavy-handed than necessary, but AsyncTask and AsyncTaskLoader neither seemed to have everything I needed. Creating fragments in those callback methods doesn't seem to bother Android either.
From the MVC (Model -- View -- Controller) viewpoint, both the Activity and its fragments are Controller, while it is Model that should be responsible for loading data. As to the View, it is defined by the layout xml, you can define custom View classes, but usually you don't.
So create a Model class. Model is responsible for what must survive a screen turn. (Likely, it will be a static singleton; note that Android can kill and re-create the process, so the singleton may get set to null.) Note that Activities use Bundles to send data to themselves in the future.

SaveInstanceState on ListView objects

I have an activity that perform a search of movies and parse the JSON results into "Movie" Objects.
after the parsing the Movie objects are being inserted into a ListView Adapter.
When i change oriantation the activity restarts and the results are gone.
How can i prevent this from happen or save the Objects and restore them onRestoreInstanceState?
I have already tried onConfigurationChanged.
You need to do a little bit of refactoring.
First implement onSaveInstanceState and save your list to a bundle
#Override
protected void onSaveInstanceState (Bundle outState){
outState.putSerializable("mylist", mylist);
}
Then on your OnCreate check if your bundle has the list and opulate your listview without fetching again your list
#Override
protected void onCreate (Bundle savedInstanceState){
super.onCreate(savedInstanceState);
if(savedInstanceState!=null && savedInstanceState.getSerializable("mylist")!=null){
populateListView(savedInstanceState.getSerializable("mylist"));
}else{
fetchList();
}
}

Fragment saveInstanceState is coming as null after orientation change

I have an activity with action bar tab. Each tab contain a fragment. Now when I rotate my device, bundle in my corresponding fragment is coming as null. This is taken care when I using device post android 3.2, but it is happening when device is Andoird3.0. I am having a headache after working on this issue. I crossed check various link on SO, but no help. Although I have given enough details, still will provide some code snippet as at various cases user ask for code snippet.
In my fragment class I am storing this value
#Override
public void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putBoolean("textboxVisible", true);
}
this is storing one boolean variable which it retrived as below.
/**
* Function called after activity is created. Use this
* method to restore the previous state of the fragment
*/
#Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null)
{
//restore the state of the text box
boolean textboxVisible = savedInstanceState.getBoolean("textboxVisible");
if (textboxVisible)
{
//do some stuff
}
}
}
but after rotation savedInstanceState is coming as null.
I don't what is going wrong. I have read in some document that below 3.2 the onCreateView() of
fragment is not called with bundle value. But to deal with this. Any help will be appreciated.
if you use setRetainInstance(true) the savedInstance bundle is always gonna be null after orientation changed. SO you cannot really save something with it, but what you can do if you need to save something, is to put it in a data member of the fragment, because setRetainInstance(true) preserves the fragment and doesn't destroy it, so after the device was rotated you gonna have the same values.
Try to get the savedInstanceState in onCreate of the Fragment.
Like
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
if (savedInstanceState != null) {
// IT MUST NOT BE NULL HERE
}
}
Please try... i hope it will work

View was clear data when screen rotation in Android

I have a View that was created on runtime then I draw some canvas on that View(runtime) after that I rotated my screen.All data was gone(reset).So I put the some code in AndroidManifest.xml like this
android:configChanges="keyboardHidden|orientation"
in my <activity> then I put a #Override function
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(R.layout.main);
LinearLayout layout = (LinearLayout) findViewById(R.id.myPaint);
layout.addView(mView);
}
but everything couldn't solved my problem.I want to keep my data from View(runtime) on every single rotation.
That's my onCreate function.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mView = new MyView(this);
setContentView(mView);
mView.requestFocus();
setContentView(R.layout.main);
LinearLayout layout = (LinearLayout) findViewById(R.id.myPaint);
layout.addView(mView);
}
You need to save and load the data you want to retain. Even though you're handling the screen rotation yourself when you modified the Manifest the way you did, you're still reloading the view yourself. Reread the reference document on Handling Runtime Changes. You need to store your data and reload it accordingly. Otherwise it will be lost when the application restarts or when you reload your ContentView.
http://developer.android.com/guide/topics/resources/runtime-changes.html
You could approach this a few ways.
I assume MyView is your own class which extends View. If so there are two methods which you may care to know, onSaveInstanceState() and onRestoreInstanceState(). When saving you create a parcelable that will contain enough data for you to re-render your view if it were to be destroyed and recreated.
class MyView extends View {
private String mString;
onDraw(Canvas v) { ... }
Parcelable onSaveInstanceState() {
Bundle b = new Bundle();
bundle.putString("STRING", mString);
return b;
void onRestoreInstanceState(Parcelable c) {
Bundle b = (Bundle) c;
mString = bundle.getString("STRING", null);
}
}
Activity has similar state saving mechanics allowed in onCreate and onSaveInstanceState() (inside Activity, not View in this case) which will allow the activity to reset the state of it's view to the state it desires.
This should solve most of your worries. If you are wanting to use the onConfigurationChanged method, then you should reclarify your question as it is not clear what the current behavior is that you aren't expecting in each situation (only using onConfigurationChanged, or only using onCreate, or using both, etc).
I've just used my data-class as singleton (java-pattern).
And it works fine.
--> Application is a Stop-Timer for Racing, where i can stop time from different opponents on the track, so i need the data for longer time, also if the view is repainted.
regz
public class Drivers {
// this is my singleton data-class for timing
private static Drivers instance = null;
public static Drivers getInstance() {
if (instance == null) {
instance = new Drivers();
}
return instance;
}
// some Timer-Definitions.......
}
Then in MainActivity:
// now the class is static, and will alive during application is running
private Drivers drivers = Drivers.getInstance();
#Override
public void onClick(View v) {
if (v == runButton1) {
drivers.startTimer1();
// do some other crazy stuff ...
}
}
// here i put out the current timing every second
private myUpdateFunction(){
time01.setText(drivers.getTimer1());
// update other timers etc ...
}

Categories

Resources