fragment transactions in onLoadFinished()

12762 views
Skip to first unread message

Nikolay Elenkov

unread,
Sep 15, 2011, 12:31:35 AM9/15/11
to android-d...@googlegroups.com
I have a simple activity with a list fragment on the left and a details
fragment on the right. Tapping a list item kicks off a loader which
gets some data over HTTP and delivers it in onLoadFinished().
That works fine, but I'd like to change the detail fragment at this
point. Calling FragmentTransaction.commit() results in an
IllegalStateException, as explained in the docs [1].
FragmentTransaction.commitAllowingStateLoss() seems to
work, but feels wrong. What would be the right/preferred way
to do this? postDealyed()?


1. http://developer.android.com/reference/android/app/LoaderManager.LoaderCallbacks.html#onLoadFinished(android.content.Loader<D>,
D)

Dianne Hackborn

unread,
Sep 15, 2011, 3:17:01 AM9/15/11
to android-d...@googlegroups.com
postDelayed is even worse.

Just understand that if the activity's fragment state has already been saved, your code will need to correctly update it if it is later restarted from the state.


--
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to android-d...@googlegroups.com
To unsubscribe from this group, send email to
android-develop...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en



--
Dianne Hackborn
Android framework engineer
hac...@android.com

Note: please don't send private questions to me, as I don't have time to provide private support, and so won't reply to such e-mails.  All such questions should be posted on public forums, where I and others can see and answer them.

DH

unread,
Nov 11, 2011, 8:17:29 PM11/11/11
to Android Developers
I'm trying to display a DialogFragment and am facing the same problem.


How do you approach this?


My loader gets data from our server and I would like to display an
error dialog when for example a network problem occurs.

Thanks.
David.

On Sep 14, 11:17 pm, Dianne Hackborn <hack...@android.com> wrote:
> postDelayed is even worse.
>
> Just understand that if the activity's fragment state has already been
> saved, your code will need to correctly update it if it is later restarted
> from the state.
>
> On Wed, Sep 14, 2011 at 9:31 PM, Nikolay Elenkov
> <nikolay.elen...@gmail.com>wrote:
>
>
>
>
>
>
>
>
>
> > I have a simple activity with a list fragment on the left and a details
> > fragment on the right. Tapping a list item kicks off a loader which
> > gets some data over HTTP and delivers it in onLoadFinished().
> > That works fine, but I'd like to change the detail fragment at this
> > point. Calling FragmentTransaction.commit() results in an
> > IllegalStateException, as explained in the docs [1].
> > FragmentTransaction.commitAllowingStateLoss() seems to
> > work, but feels wrong. What would be the right/preferred way
> > to do this? postDealyed()?
>
> > 1.
> >http://developer.android.com/reference/android/app/LoaderManager.Load...
> > <D>,
> > D)
>
> > --
> > You received this message because you are subscribed to the Google
> > Groups "Android Developers" group.
> > To post to this group, send email to android-d...@googlegroups.com
> > To unsubscribe from this group, send email to
> > android-develop...@googlegroups.com
> > For more options, visit this group at
> >http://groups.google.com/group/android-developers?hl=en
>
> --
> Dianne Hackborn
> Android framework engineer
> hack...@android.com

Dianne Hackborn

unread,
Nov 11, 2011, 11:13:44 PM11/11/11
to android-d...@googlegroups.com
It's a bad user experience to show a dialog (or do any other major shift in the UI) as the result of a loader.  Here is what you are doing: setting off some operation to run in the background for an in-determinant amount of time, which upon completion may throw something in front of the user yanking them out of whatever they were doing.

My suggestion is to show the user whatever information about the loader result in-line in the same way you would show the data from it.

ashughes

unread,
Feb 13, 2012, 9:44:23 PM2/13/12
to android-d...@googlegroups.com
I am also trying to figure out the correct (or suggested) way to perform a fragment transaction from onLoadFinished(). I understand that it is "bad user experience" to cause the UI to change in some way the user is not expecting while they are doing something. However I have a dual-pane view containing a list fragment on the left with a content fragment on the right based on which list item is selected. My list data is loaded via a Loader and I want to select a list item and show its contents (using a fragment transaction) once the list is loaded. This seems reasonable to me since the an indeterminate progress bar will be showing until the loader finishes, then the previous item (or item 0 if there is no previous saved item) should be show in the content panel on the right. This is expected behavior by the user, however I can't perform a fragment transaction in onLoadFinished() to make this happen. Is there some other way I should be doing this?

Thanks,
Andrew

Cybrosys

unread,
Feb 27, 2012, 10:58:23 AM2/27/12
to android-d...@googlegroups.com
Well this is just ridiculous.

I have a login screen and when the user presses "Log in" I want to display a ProgressDialogFragment saying "Logging in. Please wait...". I have a AsyncTaskLoader that does the authentication against a Web Service and returns the result. Once I have the result I want to remove the ProgressDialogFragment... but according to you that's impossible because I am not allowed to talk to fragments in the onLoadFinished callback?

My previous solution was using the AsyncTask<T> and ProgressDialog class but they are "deprecated" and I was told to use Fragments and Loaders instead since they have been backported.

I would really like to know what you consider to be "best-practice" when it comes to doing async work and notifying the user?

Dianne Hackborn

unread,
Feb 28, 2012, 8:16:42 PM2/28/12
to android-d...@googlegroups.com
It's already been mentioned:



You just need to be explicit that this is what you want to have happen.

--
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to android-d...@googlegroups.com
To unsubscribe from this group, send email to
android-develop...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en

Nadeem Hasan

unread,
Feb 29, 2012, 4:40:38 PM2/29/12
to android-d...@googlegroups.com
I have this in my base Activity:

    protected View createContentView( int id ) {
        View view = inflate( id );
        return createContentView( view );
    }

    public View createContentView( View view ) {
        FrameLayout root = new FrameLayout( this );
        root.setLayoutParams(new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT ) );

        LinearLayout pframe = new LinearLayout( this );
        pframe.setId( R.id.INTERNAL_PROGRESS_CONTAINER_ID );
        pframe.setOrientation( LinearLayout.VERTICAL );
        pframe.setGravity( Gravity.CENTER );

        ProgressBar progress = new ProgressBar( this, null, android.R.attr.progressBarStyleLarge );
        pframe.addView( progress, new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT ) );
        root.addView( pframe, new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT ) );

        FrameLayout lframe = new FrameLayout( this );
        lframe.setId( R.id.INTERNAL_FRAGMENT_CONTAINER_ID );
        lframe.setVisibility( View.GONE );

        lframe.addView( view, new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT ) );
        root.addView( lframe, new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT ) );

        return root;
    }

    public void setContentShown( View view, boolean shown ) {
        View progress = view.findViewById( R.id.INTERNAL_PROGRESS_CONTAINER_ID );
        View content = view.findViewById( R.id.INTERNAL_FRAGMENT_CONTAINER_ID );
        if ( shown ) {
            progress.startAnimation( AnimationUtils.loadAnimation( this, R.anim.fade_out ) );
            content.startAnimation( AnimationUtils.loadAnimation( this, R.anim.fade_in ) );
            progress.setVisibility( View.GONE );
            content.setVisibility( View.VISIBLE );
        } else {
            progress.startAnimation( AnimationUtils.loadAnimation( this, R.anim.fade_in ) );
            content.startAnimation( AnimationUtils.loadAnimation( this, R.anim.fade_out ) );
            progress.setVisibility( View.VISIBLE );
            content.setVisibility( View.GONE );
        }
    }

    protected void setContentShown( boolean shown ) {
        View root = findViewById( android.R.id.content );
        setContentShown( root, shown );
    }

Then in my Fragment I do the following:

    @Override
    public View onCreateView( LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState ) {
        View view = inflater.inflate( R.layout.detail_view, container, false );
        return createContentView( view );
    }

Here detail_view is the actual content layout.

When my data is ready, I populate it in detail_view and then call:

   setContentShown( true );

Hope this helps. BTW, this is fully inspired by ListFragment implementation of content.
Reply all
Reply to author
Forward
0 new messages