I have this problem for over a week now and after going through dozens of topics here and on google, I still don't have an answer.
The issue is that I am dynamically loading data to long ListView, so the initialization doesn't last for ages. After changing phone orientation ListView is beeing resized and have empty elements inside (thats ok). So i thought that OnConfigurationChanged event will handle this. Unfortunately it's fired before widgeds are resized, so still I have empty list. And here is my question: Is it anyway to get an event (or Listener) that will fore after the widged is resized and drawn?
I already have in manifest:
android:configChanges="keyboardHidden|orientation|screenSize
And there is no way to turn it "off", because loading takes few seconds and I don't want to have a few seconds lag while chenging phone orientation.
P.S. Sorry for any language mistakes ;)
yes. You can use setOnGlobalLayoutListener() that will listen and you use a callback once layout is completed
Also on screen rotation, activity should be destroyed, recreate, so your loading should restart. Are you doing something strange?
I do this in one of my fragment :
root = inflater.inflate(R.layout.fragment_booking_accepted, null);
root.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Dbg.e(TAG, "- On Layout Completed ! ");
// If you need this to be called again, then run again addOnGlobalLayoutListener.
// root.getViewTreeObserver().removeGlobalOnLayoutListener(this);
isLayoutCompleted = true;
// YOU write here all the coed you might need. }}}} close brackets
Edit :
Most people think it's horrible to not let android do its job: destroy and recreate when screen rotates. But you can do what you want.
Loading an empty list view is very light. It's impossible it takes few seconds to load the layout. You HAVE to load the layout and your listview with a spinner to let the user know what his phone is doing (loading). Once data is loaded, notify data change to the adapter.
does it change your point of view? ask me, if I know, I'll try to answer.
Related
After user inputs parameters in MainActivity for my app (shown below), he taps Search, which calls MatchesActivity, which generates output on a new screen (shown further below), which is exited via tapping back.
But with MatchesActivity active, every time the device is rotated, Search is again executed because the Activity restarts. In the screenshot below, I rotated device from vertical to horizontal to vertical to horizontal back to vertical.
It looks silly.
The output is generated in MatchesActivity that is invoked in onCreate in MainActivity like so:
Intent matchesIntent;
matchesIntent = new Intent(MainActivity.this, MatchesActivity.class);
startActivity(matchesIntent);
Here's the essence of onCreate for MatchesActivity:
#Override protected void onCreate(Bundle savedInstanceState)
{
MainActivity.dbc.setDbProcesslistener(this); // to know txaMatches has been defined
MainActivity.dbc.findDBMatches(); // generate output
}
I did research. I found some complicated ways of preventing an activity from restarting when the device is rotated. For example .
I'm hoping for a simpler solution. Any ideas?
As you have found, one option is to prevent the activity from being recreated on configuration changes all together. This is not always the best option, as this will prevent other things depending on the configuration from being recreated/reloaded too (e.g. resources overridden with the "-land" qualifier).
Another option is to cache the result of the DB search somehow. This could be done by adding a wrapper around your database that memorizes the term and results of the last search. Another way to cache the results would be to use a fragment, and reuse that fragment across activity recreations. Whether a fragment is recreated along with its activity is controlled by this method:
http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean).
My solution was simple.
Introduce boolean variable outputIsShowing, set it to true in onCreate as MatchesActivity terminates, set it for false when onCreate or onResume are executed in MainActivity (i.e., when MatchesActivity terminates), and return immediately in onCreate for MatchesActivity if outputIsShowing is true.
So if MatchesActivity is active when device is rotated, outputIsShowing will be true, so don't execute again.
It may not be best practice, but I've extensively tested it under normal conditions and am happy enough so far. Not sure if anything is lurking out there as a "gotcha".
I plan to go back and study the suggestions made so far since the more general situation is definitely important. And I will have to do so if someone finds fault with what I've done.
#Override protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// usual details prior to asking for matches
if(outputIsShowing)
return;
MainActivity.dbc.setDbProcesslistener(this); // to know matches was defined
MainActivity.dbc.findDBMatches();
outputIsShowing = true;
}
* EDIT *
Strangely, after embedding TextView txaMatches in a ScrollView to accomplish smooth, accelerated scrolling, I had to remove the references to outputIsShowing in order to see output after two device orientation changes.
And now, maybe I'll submit another question to address the fact that, very infrequently after screensaver forces waking the device, the output does NOT show if that is where the focus was when screensaver became active. Tapping 'back' to get to user input screen and then immediately tapping Search restores all to normal until about 100 (give or take) screensaver instances later, the output is again missing.
Such a bug makes me think I ought to follow the advice above.
If I do, or when I figure out the problem, I'll edit this again.
I have loaded a huge layout XML ( > 1600 lines ) which has a view flipper with multiple layouts. It was first freezing at 'onCreate' but I changed to code to inflate the layout in background and pass the returned view to 'setContentView' at 'onPostExecute'. (This is working fine)
Now, when the activity is open (is in front) and the device is locked (or screen turned off), when I try to unlock the device, it freezes completely on some devices, and on other devices it shows a dialog saying "App isn't responding. Do you want to close it?" with options to "Wait" and "Ok".
Please help me to fix this issue.
Thanks
I finally figured out, what was causing the trouble.
I was using the LayoutInflater in an AsyncTask to inflate the whole XML into a View, like this...
#Override
protected View doInBackground(Void... params) {
Looper.prepare();
View view = getLayoutInflater().inflate(R.layout.my_large_xml, null, false);
return view;
}
#Override
protected void onPostExecute(Void result)
{
setContentView(view);
}
It turns out, that, when we set the content view using the view object, it freezes the device when activity is paused (I am not sure why).
But when I use the method setContentView(R.layout.my_large_xml) , it works perfectly fine. So I dropped the idea of loading the XML in background, and now I am doing it on my main thread in the conventional way, because we cannot call setContentView from another thread.
Sad ! Its not how I wanted it to be, as it freezes the app when the setContentView is called initially, but at least its working now. :(
I will not give up, and keep trying. And, in case if I find any better solution, I will update the answer. :)
I have a gridView which I populate with aJsonObject gotten from a website, with a CustomAdapter (baseadapter).
I'm using Volley library to manage the HTTP requests (it works fine, as far as I tested).
When I launch the app, the grid is populated correctly (50 Items).
But if I exit the app -with finish()- and then click on the launcher icon, the list stays empty (the http request is successfull, but getView is never called).
Relevant Notes:
I first had a similar problem for all instances (which I solved by properly setting the visibility of the gridview container -a RelativeLayout- to VISIBLE)
The getCount() function is always returning 50 items (which is the correct value).
After some tests, the 2nd time I launch the app the gridview container -and the gridview itself- has visibility set to 0 (not by me)
This is happening in all android versions.
Important: if I Force Stop the app, then launch it, the contents load just fine (the same if I clear cache).
Questions:
If getCount() is returning 50, why is getView not being called?
Why is my element "gone" even if I forced it to visible?
Not pasting code, as I think this is a conceptual problem, and not a coding problem, by now.
Thank you in advance, Droiders.
I temporarily solved this issue by killing the android process when the user exits/finishes the app,
public void onDestroy() {
super.onDestroy();
android.os.Process.killProcess(android.os.Process.myPid());
}
but I don't really like this workaround.
Any suggestions on how to solve the problem with more elegance will be appreciated.
I have a MainActivity that extends SherlockActivityFragment. This activity holds some fragments and a menu. Each menu item leads to new SherlockActivities. One particular SherlockActivity when it is loaded contains a carousel, that moves extremely slow.
However, once the orientation changes this carousel performs perfectly. I know that the Activity is reloaded on orientation change. But I have no idea what could start this to begin with, or even where to start looking.
Has anyone come across any similar issues? What did you do to profile the issue and what was your fix?
If you can think of relevant parts of the code (eg onCreate etc) please ask and I'll post it. Each activity has about 150 - 200 LoC)
Edit:
Here is the intent that I am using to start the SherlockActivity in question:
Intent rankIntent = new Intent(context, RankActivity.class);
rankIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(rankIntent);
break;
I've read that on orientation change onDestory() is called followed by onCreate. This makes me think that maybe some resource is freed that wasn't initially available. But the flag in the intent is intended to clear the backstack and any resources with it.
I've also tested with using the finish() method just before loading the new activity with no improvements.
This seems like a far reach but I recently had a problem I'd say could be similar.
I learnt that Android doesn't handle scaling images particularly well - could it be that the images are scaled before you change the orientation and not scaled once you do?
Answering the 2nd part of your question: since my app doesn't perform any resource-needy logic operations and is based on its UI, what I did was comment out some of its elements one at a time to see if it helps; that's how I found out an ImageView that was scaled up in my case.
I have a list of orders in one fragment. In a second fragment, I display the detail of the order, and I use a third fragment to display the buttons that change the status of the order.
In the list, each order is displayed with a background color that indicates its status, for example green for a completed delivery.
When in landscape mode, both the detail and list are shown. In portrait mode I use two separate activities.
This all works fine, up until I change the status of an order. I can't find a way to get the list to update.
As I understand it, what needs to happen is the adapter needs to have its notifyDateChanged() method called. I've tried calling it directly from the method that processes the button click, I've tried an asynctask, and I've tried a handler. My debug methods show that the call is happening, but the list doesn't get updated.
It's possible I'm doing something completely bone-headed, but I've double and triple checked things. I suspect there is some key element I don't understand. I hope someone else does and will tell me what I'm missing.
I had some code posted, but it was clearly wrong. Not sure what code to post, since I think this is more a conceptual than coding issue.
If you want to update a view, try to use :
Thread + Handler
AsyncTask
What I discovered, through the helpful comments of other posters, is that my problem wasn't that the adapter wasn't getting the notifyDataChanged() call on the correct thread. I put in debug code that proved that. The problem was that the background of the listview item was based on a change in the underlying data itself, so the real answer was to get the Adapter to refresh the Cursor. I made some changes. I modified the AsyncTask I was using to notify the adapter. Now the AsyncTask gets a new cursor and calls adapter.changeCursor(cursor) with the result. The AsyncTask is called from the MainActivity (which hosts both the list and detail fragments) when the status is changed. It's also called in the onResume() portion of the ListFragment code, so the list will be updated properly when coming back from the detail fragment. Works great.