Android number Picker in Fragment not displaying correct value - android

I'm using the default android NumberPicker and am having issues when using the setValue() method.
Just to note, the issues are occurring when i run API Level 16(Galaxy SII), I have tried an API Level 14 emulator and there are no issues so i'm a bit baffled.
My fragment contains number pickers and the numbers are saved to a DB table. I am returning the values to the number pickers when the fragment is accessed again.
Initially this works but if i replace the fragment in the activity, and then call the fragment again, the number pickers will display "0" but if i increase or decrease it, it will update from the number that it should be displaying. So 0 would jump to 5 if the number that is saved to the DB is 4.
Has anybody come across this and could they direct me on how to resolve this issue?
Thanks.

I can't be certain without seeing your code, but my guess is that your Fragment has not overridden the onAttach() Fragment class method.
By overriding that method and performing my setValue() in that function like so, I was able to fix this issue:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (number_picker != null)
number_picker.setValue(desired_level);
}

Related

Android Life Cycle confusion

Updated to describe the requested code; the updates are at the end.
I'm trying to do something that seems like it should be simple but the Android Life Cycle doesn't seem to be working as described. I expect this is a misunderstanding on my part, not a bug in Android!
I am new to Android and am just getting familiar with the Android Life Cycle after developing Java apps for Windows so I'm still struggling with a lot of new ideas, especially the Life Cycle.
I have nearly finished my first app, which is based on a RecyclerView. It shows a bunch of sales records (each representing one sale) of a small company. Users can click on a FloatingActionButton to report a new sale or click on edit or remove icons on the individual records to change the details of a sale or delete it entirely. Each of these things has its own activity. Also, there is a SettingsActivity to show settings.
One of the settings lets the user select from amongst three different date formats for displaying the date of the sale. When I back out of Settings (by hitting the back button), I want to see the visible rows using the newly-changed date format immediately but this is NOT happening. Up until now, I would just click on the buttons to take me to one of the other activities, then click the cancel button when I get there; on returning to my main activity, I would see the new date format.
I don't feel my user should have to go to that other activity; simply returning from Settings should change the dates immediately. I started looking at the Android Life Cycle. As I read the documentation, I should be overriding the onResume() method and issuing a notifyDataChanged() to the Adapter that controls the RecyclerView to get it to rebind the visible rows, using the new date format.
Unfortunately, that has no effect whatever that I can see. Here's my onResume:
#Override
protected void onResume() {
super.onResume();
mAdapter.notifyDataSetChanged();
}
Am I doing the notifyDataSetChanged() in the wrong method? If not, why doesn't it work? If it is the wrong method, which method should I be overriding?
Is there any particularly good text or video tutorial explaining the Life Cycle that I should be reading or viewing?
Updates
The date format is set in my SettingsActivity, which is the default one provided in the SettingsActivity template, tailored to meet my needs. All I've touched is the General settings. I replaced all of the ones from the template with six of my own settings. One is called Date format and defaults to YYYY-MM-DD; there are two other choices.
My main activity, which is the one that invokes SettingsActivity via a menu option, gets the value of the date format from the default SharedPreferences. This is because I don't want to look up the value of the Date format once for every row in the ArrayList but just once for all the rows in the ArrayList. I pass the value of the Date format to the Adapter via its constructor. The format gets used in OnBindViewHolder() to control the appearance of the date on the various sales records.
Define a set method to your adapter,
public class MyAdapter extends RecyclerView.Adapter<AdapterContactList.ViewHolder> {
private String dateFormat;
.....
public void setDateFormat(String dateFormat) {
this.dateFormat = dateFormat;
}
}
After user change the dateformat; set it to your adapter, then notify datas for change. That means, you need set new dateformat before notifyDataSetChanged method; because adapter doesn't know the new value.
#Override
protected void onResume() {
super.onResume();
String newDateFormat = getFromSharedPreferencesOrInstantly();
mAdapter.setDateFormat(newDateFormat);
mAdapter.notifyDataSetChanged();
}

How to save the state of views held in dynamic viewpager

I have an enhanced loop, which will dynamically inflate however many layouts relevant to the number of values held in my array.
This works perfectly however, there is a method being called on each iteration, which also works but there is a big bug that I need help resolving.
Imagine there are 5 items in my array, therefore 5 layouts are inflated, in these layouts there is a little scratchcard type section on the layout.
Now if the user is on page 1, uses the scratchcard, then moves on to page 2, uses the scratchcard etc etc, it works fine.
But if the user is on page 1 and then goes to say, page 5 and then back to page 1 (basically in a random order), the scratchcard doesn't work.
From my understanding, the reason for this is that the method is being called an implemented on each iteration and the view is losing its state if the user scrolls back or scrolls in random orders.
Therefore I need a way to save the created view state in my viewpager.
Is this possible for my scenario? I have tried my best to find a solution, but cannot find something that feels relevant to my question.
Here is a snippet of the code in question. Thanks for any guidance or suggestions!
for (String x : array1) {
//loop out the number of layouts relative to the number of questions held in x
View current_layout = LayoutInflater.from(getActivity()).inflate(R.layout.question_fragment, null);
//use the pageAdapter to add the layout to the users view
pagerAdapter.addView(current_layout);
//call method to add functionality to the scratchcard
isCorrect(current_layout);
}
public void isCorrect(View current_layout) {
ScratchoffController controller1 = new ScratchoffController(getActivity())
.setThresholdPercent(0.40d)
.setTouchRadiusDip(getActivity(), 30)
.setFadeOnClear(true)
.setClearOnThresholdReached(true)
.setCompletionCallback(() -> {
})
.attach(current_layout.findViewById(R.id.scratch_view1), current_layout.findViewById(R.id.scratch_view_behind1));
ScratchoffController controller2 = new ScratchoffController(getActivity())
.setThresholdPercent(0.40d)
.setTouchRadiusDip(getActivity(), 30)
.setFadeOnClear(true)
.setClearOnThresholdReached(true)
.setCompletionCallback(() -> {
})
.attach(current_layout.findViewById(R.id.scratch_view2), current_layout.findViewById(R.id.scratch_view_behind2));
ScratchoffController controller3 = new ScratchoffController(getActivity())
.setThresholdPercent(0.40d)
.setTouchRadiusDip(getActivity(), 30)
.setFadeOnClear(true)
.setClearOnThresholdReached(true)
.setCompletionCallback(() -> {
})
.attach(current_layout.findViewById(R.id.scratch_view3), current_layout.findViewById(R.id.scratch_view_behind3));
ScratchoffController controller4 = new ScratchoffController(getActivity())
.setThresholdPercent(0.40d)
.setTouchRadiusDip(getActivity(), 30)
.setFadeOnClear(true)
.setClearOnThresholdReached(true)
.setCompletionCallback(() -> {
})
.attach(current_layout.findViewById(R.id.scratch_view4), current_layout.findViewById(R.id.scratch_view_behind4));
}
I ussually use ViewPager with Fragments and what you mention has happend to me when I try to keep references to the Fragment instances (in my case) outside of the viewpager.
This happens because the viewpager may create new instances of the Fragment it contains when you re-vist the tab in the way you mention. When this happens, the instance reference you hold outside of the viewpager is not anymore what the viewpager is showing.
In your case , according to this question, you have to oveeride instatiateItem and destroyItem. I think you can use these methods to save state restore state, and also you could update any external reference when instantiateItem is called.

Preferred method of loading dependent queries with LoaderManager

What is the preferred method of loading dependant queries with the LoaderManager API in Android? As of now the best I could come up with is something along the lines of:
#Override
public void onCreate( Bundle savedInstanceState ) {
getLoaderManager().initLoader( FIRST, null, this );
}
#Override
public void onLoadFinished( Loader<Cursor> loader, Cursor data ) {
switch ( loader.getId() ) {
case FIRST:
Bundle args = new Bundle();
args.putInt( ID, somethingFromData( data ) );
getLoaderManager().restartLoader( SECOND, args, this );
break;
case SECOND:
somethingElseFromData( data );
break;
}
}
This works fine most of the time, but it crashes horribly under one special case. Say I launch a second activity or push a fragment on top of this that modifies the data of FIRST. Now when I navigate back to the activity/fragment with the code above it first refreshes me with the old data of FIRST and SECOND, and since FIRST initiates SECOND, SECOND is reloaded with the new data. Now since FIRST is changed it is loaded again, which causes yet another load of SECOND to initiate.
First of all if you count that that sums up to two loads of FIRST (one old and one new) and three loads of SECOND (two old and one new), which seams at least a bit wasteful. I don't really mind that, except it is a hassle to debug, but it also seems to me to behave non-deterministically, because you don't know which loads will finish first. Will I end up with the new data for SECOND if the relation between FIRST and SECOND changed, or will I end up with the cached values?
I know that I can mitigate this by keeping score of when to restart the second loader, but there has to be a better way of doing this.
To clarify a bit: The problem is most prominent if rows in FIRST contains a reference to rows in SECOND and after the back navigation the row(s) in FIRST loaded does not point to the same row(s) in SECOND as before.
Given that the only thing your first loader does is effectively prepare arguments for your second loader, you should subclass your own AsyncTaskLoader and do the whole operation within one loader.
This article contains a very in-depth example of a custom AsyncTaskLoader, which I'm sure you could adapt to your own needs. You also should look at the CursorLoader source code for a better grasp of how to write your own.

Issue with Android fragments: getView() returns null

Our app has several fragments. In one of them user fills several TextEdit fields. When he finishes he presses a button in the ActionBar to save the data. The Action just calls a private method named "saveData" that fetches all data from the fields and submit it to our server.
We have many stack traces from our users showing that getView() returns null in method saveData, but for just a small part of them. For most of them there is no problem at all. We cant reproduce the problem and we cant understand what might be causing it. The code is pretty simple:
View vw = this.getView();
EditText et;
et = (EditText)vw.findViewById(R.id.editEmail);
String email = et.getText().toString().trim();
et = (EditText)vw.findViewById(R.id.editPassword);
String password = et.getText().toString().trim();
The action is added in osResume, see below:
public void onResume() {
super.onResume();
MainActivity act = (MainActivity)this.getActivity();
act.bar.removeAllActions();
act.bar.addAction(new SaveAction());
}
Any ideas? How can we reproduce it?
Can you tell from your logs whether the problem is always for the same users / devices ?
I see from the code that you have submitted that the view is in the same fragment - is that actually the case ?
It's POSSIBLE that a fragment no longer in view can have their view destroyed in order to free up resources. e.g.
getView() returns null
If I suspected that this might be the case then I would attempt to recreate the problem on a phone / tablet / emulator with limited resources.
Good luck !

ListView empty despite items added to adapter sometime when resuming application

I have an application in the market with a bug which I can not seem to solve. Today I have tracked it down to the following method.
public void updateDealList(ArrayList<Deal> deals) {
// first call or showing bookmakrs (also gets called other times when adapter is null)
if (dealListAdapter == null || showingBookmarks || !dealListAdapter.moreDealsToDownload()) {
dealListAdapter = new EndlessDealListAdapter(getActivity(),
R.layout.deal_list_item, deals);
setListAdapter(dealListAdapter);
listView = getListView();
if (getActivity().findViewById(R.id.right_fragment_container)!=null){ // if tablet
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
listView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_LEFT);
}
showingBookmarks=false;
Log.d("UPDATE_DEAL_LIST", "Notified list created new" + dealListAdapter.getCount());
return; //PROBLEM OCCURS WHEN APP COMES IN HERE AFTER RESUMING
}
dealListAdapter.getDealListAdapter().clear();
for (Deal d: deals){
dealListAdapter.getDealListAdapter().add(d);
Log.d("UPDATE_DEAL_LIST", "added " + d.title);
}
dealListAdapter.notifyDataSetChanged();
Log.d("UPDATE_DEAL_LIST", "Notified list " + dealListAdapter.getCount());
}
This method is passed an arraylist of deal objects which are pulled down from the internet. When the application is first opened data is pulled from the internet and the above method is called which creates a new adapter which is set for the ListFragment. This method is also called when the user requests more deals.
The problem I am having is that the list sometimes thinks its empty dispite the adapter containing deals. This seems to occur when a use resume the application when their device is low on memory (I presume part of the application has been removed from ram). This method is called and the dealListAdapter is null so a new one is created and deals are added. Despite this happening the list remains empty and the user has to force close the app to get it working again.
The line below shows tat when the method is called it enters the if and 21 deals are added to the adapter. Unfortunately the list is empty to the user.
05-23 01:52:32.879: D/UPDATE_DEAL_LIST(3478): Notified list created new21
One idea (pretty long shot :)
If the app itself wasn't killed, just the activity, the system tries to recreate the activity's last state, calling the onRestoreInstanceState().
This method will not be called if the app was killed - so this is one of the big differences between the two.
The ListActivity overrides the onRestoreInstanceState(). It ensures, that the List exists, before it goes on. If the list does not exist, it inflates it from the default place.
If you're setting the contentView in the onResume() try to move it to onCreate(), that may solve the problem.
I can help more, if I see the activty's code.
You can try one or more of the following options
Try calling setListShown(false) just before setting the list adapter.
Otherwise, do
setListShown(false);
setListAdapter(null);
Otherwise, do requestLayout()

Categories

Resources