Android Fragment display a loading while rendering layout

守給你的承諾、 提交于 2020-05-17 05:49:25

问题


Let's say I've got an Activity with only a FrameLayout (I've also try with the new FragmentContainerView) so I will be loading Fragments on it. Let's assume I've got two Fragments: fragA and fragB, I will first load fragA and with a button, present in that fragment, replace it with fragB.

I've define a function on my Activity so I can load a new Fragment on the container:

public void loadFragment(Fragment fragment) {
    FragmentTransaction fragTrans = getSupportFragmentManager()
            .beginTransaction()
            .replace(R.id.container, fragment)
            .addToBackStack(null);

    fragTrans.commit();
}

And I will call this from fragA to go to fragB. This far so good, everything works fine. Actually the problem I'm facing is not about functioning, is that I don't like the loading time of fragB, it takes about 2sec, and I'd like to display a loading to the user, but I'm not able to achieve it.

The first thing I've try is adding a gone ProgressBar to the activity layout, where the container is. Then my loadFragment function will first set this progressBar visible and then perform the fragment transaction and each fragment will hide (gone) this View before exiting the onCreateView method. But the problem is that the user never sees this ProgressBar, it's like the main UI is freezed while rendering the layout of the fragB and therefore does not update the UI making this progressBar usesless. I even see some frame skipped in the LogCat about 50 frames.

Both fragments have no logic just the onCreateView implemented where the layout is inflated. I can notice that if I change the layout of fragB to a simpler one it loads instantly, so I'm guessing the issue is related to the time it takes to render the layout. This heavy layout is a really big form with lots of TextInputLayout with TextInputEditText and MaterialSpinner (from GitHub).

I know I maybe can simplify the layout, but I'd like to know how can I display a loading while rendering. For example, I've seen some apps load some kind of dummy-view while loading and then replace it with real data.

One thing I've try is to load a dummy layout with a ProgressBar in the middle and in the onCreateView method post a Handler to inflate a real layout on the same container in the background, like this:

public View onCreateView(final LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_dummy, container, false);

    Handler mHandler = new Handler();
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            container.removeAllViews();
            View realView = inflater.inflate(R.layout.fragment_real, container);
        }
    });
}

This kinda work, as the viewing experience is nice, but when navigating back, the layout of fragB remains visible as background of the other fragments. I have not test it but maybe I can call container.removeAllViews(); before exiting the fragment and it will work, but still seems like a workaround rather than a solution, to me.

And other thing I've not try because maybe is an over-kill is to have an intermediate or loading fragment and load it always before the real fragment, and I will pass an Intent Extra to it so I can tell what's the real fragment I want to display. This intermediate fragment will not be added to the backstack.

How do you solve this kind of problems?


回答1:


I think you have to use AsycTask when your new fragment loading

1- Load the new fragment

2 -Start Async task

3 - Initialize Progressdialog in constructor of AsyncTask where you want to load the data

4 - Show Progressdialog in onPreExecute() in your AsyncTask with dialog.show()

5 - Dismiss the Progressdialog in onPostExecute() in your AsyncTask with dialog.dismiss()

6- Update the UI / set the new data to your adapter etc

Check here Show a loading spinner while a fragment is loading




回答2:


Well it seems that AsyncLayoutInflater is the way to go!

Here's how I use it:

private ViewGroup fragmentContainer;

public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View dummyView = inflater.inflate(R.layout.fragment_dummy_loading, container, false);

    fragmentContainer = container;
    AsyncLayoutInflater asyncLayoutInflater = new AsyncLayoutInflater(getContext());


    asyncLayoutInflater.inflate(R.layout.fragment_real_layout, container, new AsyncLayoutInflater.OnInflateFinishedListener() {
        @Override
        public void onInflateFinished(@NonNull View view, int resid, @Nullable ViewGroup parent) {
            fragmentContainer.removeAllViews();
            fragmentContainer.addView(view);
        }
    });


    return dummyView;
}


@Override
public void onStop() {
    super.onStop();
    fragmentContainer.removeAllViews();
}

Up there, fragment_dummy_loading is a layout with a ProgressBar and fragment_real_layout is the real heavy layout.

There's just one thing I don't get... and that's how can I bind Objects to the XML widgets (without using Android Data Binding, if possible) when the AsyncLayoutInflater fallsback to infalte in the UI thread.

According to the docs:

If the layout that is trying to be inflated cannot be constructed asynchronously for whatever reason, AsyncLayoutInflater will automatically fall back to inflating on the UI thread.

And I check the source of AsyncLayoutInflater and I can tell that the method onInflateFinished is called no matter if the inflation was in the background or in the main thread



来源:https://stackoverflow.com/questions/61034263/android-fragment-display-a-loading-while-rendering-layout

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!