RoboAsyncTask custom loading animation - android

I have extended RoboAsyncTask implemented all needed methods, but while data loading it's displaying default loading animation.
I have made my custom loading dialog but default loading animation on fragment still displaying. It is possible to remove it? Or change animation image?
public class ContactFragment extends RoboSherlockListFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ContactLoadingTask contactLoadingTask = new ContactLoadingTask(this.getActivity());
contactLoadingTask.execute();
}
private class ContactLoadingTask extends RoboAsyncTask<List<Contact>> {
#Inject
ContactParser contactParser;
protected ContactLoadingTask(Context context) {
super(context);
}
#Override
public List<Contact> call() throws Exception {
return contactParser.getContacts();
}
#Override
protected void onSuccess(List<Contact> contacts) throws Exception {
setListAdapter(new ContactArrayAdapter(ContactFragment.this.getActivity(), contacts));
}
}
}
Launching this code i see loading animation on my fragment before contacts are loaded

RoboAsyncTask does not deal with any animation, or anything visible by the user. It only execute code in a background thread.
I think the animation you're talking about is played automatically by ListFragment when you call setListAdapter(), as stated in the setListShown() method documentation
Sadly it doesn't seem you can override this animation, so you might have to use a standard Fragment and manage the ListView yourself. You can look at the source code of ListFragment to see how this is done
If you just want to disable the animation, you might try to use setListShownNoAnimation just before setting your list adapter:
#Override
protected void onSuccess(List<Contact> contacts) throws Exception {
setListShownNoAnimation(true);
setListAdapter(new ContactArrayAdapter(ContactFragment.this.getActivity(), contacts));
}

Related

Debugging Mysterious RecyclerView Error that Closes App

I am working on an app with a RecyclerView, and I thought I almost had it running, but ran into a strange error.
I had made a few small changes to correct a small bug, and after that the app would no longer finish setting up the main activity layout. Instead I got the following screens:
App closed --- App keeps closing
My first thought was to look back at my small changes. But they did not cause this problem. (Undoing those changes did not fix the problem.)
I now believe that the problem is related to a change in the way the app operates between the first "successful" (though buggy) run and the following runs that fail.
In the first run, the app had to request permission from the user in order to access the documents folder. But after that, the app no longer needs to ask, because the user has already granted permission.
This means the order of execution has changed prior to the RecyclerView layout being created. But I can't (yet) figure out what's going wrong or how to fix it.
Here's the OnCreate() method in my main activity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_prog_summary);
}
Here's the OnCreate() method & other related methods in the superclass (the class that implements the permissions request):
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.state = savedInstanceState;
if (state != null) {
isInPermission = state.getBoolean(STATE_IN_PERMISSION, false);
}
if (hasAllPermissions(getDesiredPermissions())) {
onReady(state);
}
else if (!isInPermission) {
isInPermission = true;
ActivityCompat.requestPermissions(this,
netPermissions(getDesiredPermissions()),
REQUEST_PERMISSION);
}
}
#Override
public void onRequestPermissionsResult(int requestCode,
String[] permissions,
int[] grantResults) {
isInPermission = false;
if (requestCode == REQUEST_PERMISSION) {
if (hasAllPermissions(getDesiredPermissions())) {
onReady(state);
}
else {
onPermissionDenied();
}
}
}
Here's the OnReady() method in my activity class (called when permission is granted):
#Override
protected void onReady(Bundle savedInstanceState) {
_alEntries = new ArrayList();
TaskData tdSource = new TaskData();
// load task item array & trim the excess (unused) ArrayList space
tdSource.LoadData(this, _alEntries);
_alEntries.trimToSize();
// create summary item array & populate it based on task item array
_alSummaries = new ArrayList();
PopulateSummaryList();
_rv = (RecyclerView) findViewById(R.id.rvBookList);
_li = getLayoutInflater();
_rv.setLayoutManager(new LinearLayoutManager(this));
_adapter = new TaskItemAdapter();
_rv.setAdapter(_adapter);
}
The app actually closes when the call to setLayoutManager is executed. Any thoughts on where I've gone wrong or how to proceed in tracking this down?
Seems like it could be a NullPointerException in that your _rv variable is null. Judging by the code it makes sense, your superclass's onCreate method calls onReady() BEFORE setContentView() is called in your subclass's onCreate therefore by the time onReady() is called in your subclass findViewById(R.id.rvBookList) will return null as you haven't set the content of the class yet. To fix this I recommend making your superclass an abstract class. For example, in your superclass do this:
public abstract class BaseActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
this.state = savedInstanceState;
// ... remaining code from your superclass's onCreate()
}
public abstract int getLayoutId();
}
Now in your MainActivity, extend BaseActivity. Since BaseActivity is abstract and getLayoutId() is an abstract method, it will force you to implement getLayoutId() like so:
public class MainActivity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public int getLayoutId() {
return R.layout.activity_prog_summary;
}
}
Doing this will ensure that all classes that inherit your superclass will perform the correct order of operations, in that setContentView is called before anything else.

Fragment asynchronous Interface callback handling

I have a Fragment that does some communication with the internet trough some helper Class that requires an interface to handle asynchronous callbacks like this:
SomeInternetCommunicator.getResource(theResourceToGet, retrieverInterfaceInstance);
The interface looks like this:
public interface SomeInternetCommunicator {
void onResourceRetrieveSuccess(SomeResource resource);
void onResourceRetrieveFailed(CommunicatorException e);
}
The problem is that, sometimes the calls take too long, the user already navigated elsewhere and the Fragment that did the call to getResource is not part of the currently running Activity anymore (not showing, not on backstack). This causes many problems because this orphan Fragment is still attempting to react to the callbacks even if it is not part of the Activity anymore. For example, getContext() returns null.
Now my workaround on the Fragment looks like this:
SomeInternetCommunicator flocktrackerTrackerRetrieverInterface = new SomeInternetCommunicator() {
#Override
public void onResourceRetrieveSuccess(SomeResource resource) {
if(isVisible()){
doRetrievalPostProcessing(resource);
}
}
#Override
void onResourceRetrieveFailed(CommunicatorException e) {
if(isVisible()){
doExceptionHandling();
}
}
};
Using the isVisible() works because this ensures that the fragment is still on the foreground, part of the Activity and ready to do the handling. This, however, doesn't help me to cover the case for when the Fragment is not visible, but still part of the current Activity, limiting my possibilities for loading the Fragment before showing it. (Activity is in the background, Fragment is on the Backstack, Fragment loaded by a ViewPager).
Is there a better way to ensure that the Fragment is still on the current Activity's scope before I do the postprocessing? Maybe checking isAdded()?
This question seems to explain a broader but related situation, but it has no answers.
Thank you!
there're two usual approaches to this case:
the best approach is to have a way to clear the interface instance from the SomeInternetCommunicator. Something like:
.
#Override public void onDestroyView(){
SomeInternetCommunicator.removeMe(this);
}
if option (1) is not possible because SomeInternetCommunicator is a poorly coded library, you force option 1 to be possible by making a separate class to implement the interface.
.
public class SomeInternetCommunicatorInterceptor implements SomeInternetCommunicatorInterface {
private SomeInternetCommunicatorInterface listener;
public void setListener(SomeInternetCommunicatorInterface listener){
this.listener=listener;
}
#Override
public void onResourceRetrieveSuccess(SomeResource resource){
if(listener != null){
listener.onResourceRetrieveSuccess(resource);
}
}
#Override
public void onResourceRetrieveFailed(CommunicatorException e){
if(listener != null){
listener.onResourceRetrieveFailed(e);
}
}
}
then on your fragment you use this new class as:
private SomeInternetCommunicatorInterceptor interceptor;
public void onCreateView(....){
... your code
interceptor = new SomeInternetCommunicatorInterceptor();
}
onStart(){
interceptor.setListener(this);
}
onStop(){
interceptor.setListener(null);
}
public void onDestroyView(){
interceptor = null;
super.onDestroyView();
}
VERY IMPORTANT:
if you make SomeInternetCommunicatorInterceptor an internal class of the fragment you HAVE TO make it as static class

Is it a good practice to perform certain initialization code in Fragment constructor

Sometimes, I have some headless fragments, which I need to run some initialization even before onCreate
For instance,
public class NetworkMonitorFragment extends Fragment {
public static NetworkMonitorFragment newInstance() {
return new NetworkMonitorFragment();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public void init() {
// This function shall be call even before onCreate.
}
}
NetworkMonitorFragment networkMonitorFragment = NetworkMonitorFragment.newInstance();
networkMonitorFragment.init();
I was wondering, is it a good practice, to have certain initialization inside Fragment constructor? Is there any drawback for doing so? The reason I'm asking, because I don't see many code example for doing so.
public class NetworkMonitorFragment extends Fragment {
public static NetworkMonitorFragment newInstance() {
return new NetworkMonitorFragment();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public NetworkMonitorFragment() {
init();
}
private void init() {
// This function shall be call even before onCreate.
}
}
NetworkMonitorFragment networkMonitorFragment = NetworkMonitorFragment.newInstance();
You certainly can, even other methods like Fragment.instantiate(Context context, String fname, Bundle args) calls newInstance() which calls default constructor. Although you must be aware of some things:
You should not do any stuff that is not independent of fragment's
state, lifecycle or Android's context
You should not do any stuff that takes up most of the 16ms UI
drawing window
You should not spawn new threads there
So while variable instantiation or some quick calculations based on external context, let's say, Date, for example, is perfectly fine, but decoding even a small bitmap either synchronously or asynchronously is a quick way to break things.
If execution and results of this function are not tied to fragment lifecycle and not dependent on parent activity then, I guess, it's just a matter of preference.
You can get more specific answers by describing what this function does.
If you want to do something before fragment created, do it in onAttach
(onAttach always calls before onCreate, look fragment lifecycle)
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
init();
...
}
If you want to sure view is created, do it in onViewCreated with same way.

Using async Task to load an apps listView with images in Android

I have a fragment dialog containing a list of all the apps installed on the device.
Sometimes the loading takes some time and I would like to show a ProgressDialog while it loads.
I've tried the following code which didn't do any good :
#Override
public void onAttach(Activity activity) {
// Show progress dialog
showProgressDialog(activity);
super.onAttach(activity);
}
private void showProgressDialog(Activity activity) {
mProgressDialog = new ProgressDialog(activity);
mProgressDialog.setTitle(R.string.loading);
mProgressDialog.setMessage(getString(R.string.shared_options_wait_while_applist_loads));
mProgressDialog.show();
}
The onCreate loads the whole list and the app images, and then I use :
#Override
public void onStart() {
stopProgressDialog();
super.onStart();
}
private void stopProgressDialog() {
mProgressDialog.dismiss();
}
Now I'm thinking about loading the whole list in a async task, but I can't figure what should the async task do, it should probably load the list, but how can I wait for the list and get the list when it's ready ? (I believe something like a callback should be used?)
Thanks
You can watch dataset changes in your adapter by adding a DataSetObserver.
#Override
public void onCreate(Bundle savedInstanceSate){
super.onCreate(savedInstanceState);
setContentview(R.layout.main_view);
ListView myList = (ListView) findViewById(R.id.my_list);
MyDataTypeAdapter adapter = new MyDataTypeAdapter();
myList.setAdapter(adapter);
adapter.registerDataSetObserver( new DataSetObserver(){
#Override
public void onChange(){
//do stuff here
}
});
}
This way onChange will be loaded when your dataSet changes and you can download your image there.
However, I will better do an AsyncTask for each row in your adapter and download it's image independently. You could also use UniversalImageLoader library for this purpose.
you can try Android Bitmap Fun example
Android Image Demo : http://developer.android.com/training/displaying-bitmaps/index.html
This example with GridView, you can use the same adapter for ListView by changing view in listItem.

Android Presentation Class - How to make changes on the Presentation View dynamically

My presentation is working finally. I have one main activity for my first screen and one Presentation for my second Screen.
My problem is, that I can't change the content on my presentation view.
Why can't I change a TextView after the presentation is shown on the second screen?
Calling the method changeText("Test123") in the MainActivity crashes my app.
public class MainActivity extends Activity {
private PresentationActivity presentationActivity;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// init Presentation Class
DisplayManager displayManager = (DisplayManager) this.getSystemService(Context.DISPLAY_SERVICE);
Display[] presentationDisplays = displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
if (presentationDisplays.length > 0) {
// If there is more than one suitable presentation display, then we could consider
// giving the user a choice. For this example, we simply choose the first display
// which is the one the system recommends as the preferred presentation display.
Display display = presentationDisplays[0];
PresentationActivity presentation = new PresentationActivity(this, display);
presentation.show();
this.presentationActivity = presentation;
}
}
public void changeText (String s) {
this.presentationActivity.setText(s);
}
}
public class PresentationActivity extends Presentation {
private TextView text;
private PresentationActivity presentation;
public PresentationActivity(Context outerContext, Display display) {
super(outerContext, display);
// TODO Auto-generated constructor stub
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_presentation);
TextView text = (TextView) findViewById(R.id.textView1);
this.text = text;
// works fine:
text.setText("test");
}
public void setText(String s){
// error
this.text.setText(s);
}
Well, I looked in the LogCat.
The exception was:
E/AndroidRuntime(13950): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
The code in my MainActivity runs on another thread. To do UI work from here I need to use runOnUiThread. This solution I found in this answer.
My changeText methode looks like this now:
public void changeText (String s) {
runOnUiThread(new Runnable() {
public void run() {
presentationActivity.setImageView(position);
}
});
}
Thanks for the help! Now I know how to use LogCat for things like that.
You got this issue because the context of presentation is different from that of the containing Activity:
A Presentation is associated with the target Display at creation time
and configures its context and resource configuration according to the
display's metrics.
Notably, the Context of a presentation is different from the context
of its containing Activity. It is important to inflate the layout of a
presentation and load other resources using the presentation's own
context to ensure that assets of the correct size and density for the
target display are loaded.
Hope this will justify your mentioned solution as well.

Categories

Resources