Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ViewPager with Fragments that contain TableLayouts loads slowly:

I'm developing an Activity with a ViewPager that contains Fragments (Max 4 fragment each time) when each containing TableLayout. So basically 4 Tables are loaded. This is the code for loading the data into the Fragment which is later attached to the ViewPager:

 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
{
    if (container == null) {
        return null;
    }

    LinearLayout.LayoutParams layoutParams=new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    layoutParams.gravity=Gravity.CENTER_VERTICAL;
    layoutParams.setMargins(10, 0, 0, 0);

    fragmetLayout = (LinearLayout)inflater.inflate(R.layout.grid_fragment_layout, container, false);
    table = (TableLayout) fragmetLayout.findViewById(R.id.tlGridTable);
    llParameterContainer =  (LinearLayout) fragmetLayout.findViewById(R.id.llParametersContainer);
    tabName = (TextView) fragmetLayout.findViewById(R.id.tvTabName);
    tabName.setText(tab.getTabName());
    //tabName.setLayoutParams(layoutParams);

    for (Parameter parameter : SGRaportManagerAppObj.getInstance().parametersRepository.getParametersRepository())
    {
        ImageView parameterPoint = new ImageView(getActivity());
        parameterPoint.setImageResource(R.drawable.parameter_chosen_point);
        parameterPoint.setPadding(10, 10, 10, 0);
        parameterPoint.setLayoutParams(layoutParams);
        llParameterContainer.addView(parameterPoint);

        TextView parameterTextView = new TextView(getActivity());
        parameterTextView.setText(parameter.getName()+":");
        parameterTextView.setGravity(Gravity.CENTER);
        parameterTextView.setTextColor(getResources().getColor(R.color.my_black));  
        parameterTextView.setPadding(5, 10, 3, 10);
        parameterTextView.setLayoutParams(layoutParams);
        llParameterContainer.addView(parameterTextView);
        TextView parameterChosenValueTextView = new TextView(getActivity());
        parameterChosenValueTextView.setText(parameter.getChosenValue());
        parameterChosenValueTextView.setGravity(Gravity.CENTER); 
        parameterChosenValueTextView.setPadding(0, 10, 0, 10);
        parameterChosenValueTextView.setLayoutParams(layoutParams);
        parameterChosenValueTextView.setTextColor(getResources().getColor(R.color.my_black));   
        llParameterContainer.addView(parameterChosenValueTextView);

    }
    //table.setStretchAllColumns(true);  
    //table.setShrinkAllColumns(true); 
    TableRow tableRow = new TableRow(getActivity());
    TableLayout.LayoutParams tableRowParams=new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT,TableLayout.LayoutParams.WRAP_CONTENT);
    tableRowParams.setMargins(2, 0, 0, 0);
    tableRow.setLayoutParams(tableRowParams);
    Log.d(TAG, "TAB FROM FRAGMENT:"+tab.toString());


    TableRow.LayoutParams tlparamsFirstColumn = new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT); 
    tlparamsFirstColumn.setMargins(4, 0, 2, 0);
    TableRow.LayoutParams tlparams = new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT); 
    tlparams.setMargins(0, 0, 2, 0);

    //The First row for the column names.
    for (int i= 0; i < tab.getGrid().getGridColumnsList().size(); i++)
    {
        TextView tvName = new TextView(getActivity());
        String columnName = tab.getGrid().getGridColumnsList().get(i).getGridColumnAlias();
        tvName.setText(columnName);
        Log.d(TAG, "COLUMN ALIAS FROM FRAGMENT: "+columnName);
        if (i == 0)
        {
            tvName.setLayoutParams(tlparamsFirstColumn);
        }
        else
        {
            tvName.setLayoutParams(tlparams);
        }
        tvName.setGravity(Gravity.CENTER);
        tvName.setTextColor(getResources().getColor(R.color.my_white));  
        tvName.setPadding(10, 10, 10, 10);
        tvName.setBackgroundDrawable(getResources().getDrawable(R.drawable.grid_column_name_background));   
        tableRow.addView(tvName);
    }
    table.addView(tableRow);

    for (int j = 0; j < tab.getGrid().getGridData().getNumRows(); j++)
    {
        TableRow newRow = new TableRow(getActivity());
        TableLayout.LayoutParams insideRowParams=new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT,TableLayout.LayoutParams.WRAP_CONTENT);
        tableRowParams.setMargins(0, 2, 0, 0);
        for (int k = 0; k < tab.getGrid().getGridData().getNumCols(j); k++)
        {
            TextView tvName = new TextView(getActivity());
            String columnName = tab.getGrid().getGridData().get(j, k);
            tvName.setText(columnName);

            if ( (j % 2) == 0)
            {
                tvName.setBackgroundDrawable(getResources().getDrawable(R.drawable.grid_first_row_background)); 
            }
            else
            {
                tvName.setBackgroundDrawable(getResources().getDrawable(R.drawable.grid_second_row_background));    
            }
            if (k == 0)
            {
                tvName.setLayoutParams(tlparamsFirstColumn);
            }
            else
            {
                tvName.setLayoutParams(tlparams);
            }
            tvName.setGravity(Gravity.CENTER);
            tvName.setTextColor(getResources().getColor(R.color.my_black));  
            tvName.setPadding(10, 10, 10, 10);
            newRow.addView(tvName);  
        }
        table.addView(newRow);          
    }
    return fragmetLayout;
}

UPDATE:

My ViewPagerActivity Layout:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<TabHost
    android:id="@android:id/tabhost"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_below="@+id/imageView3" >

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >
        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_weight="0"/>

        <android.support.v4.view.ViewPager
            android:id="@+id/pager"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="1" >
        </android.support.v4.view.ViewPager>

         <TabWidget
            android:id="@android:id/tabs"
            android:orientation="horizontal"
            android:layout_gravity="center_horizontal"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>
</TabHost>

 <ImageView
    android:id="@+id/imageView3"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="-3dp"
    android:background="@drawable/login_scr_top_bar"
    android:contentDescription="@drawable/login_scr_top_bar" />

<TextView
    android:id="@+id/tvReportName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="top|left"
    android:paddingLeft="15dp"
    android:paddingTop="13dp"
    android:text="@string/report_tabs"
    android:textColor="@color/my_black"
    android:textSize="18sp"
    android:textStyle="bold" />

<LinearLayout
    android:id="@+id/llTabsButtonsContainer"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"
    android:layout_alignParentRight="true"
    android:gravity="center_horizontal"
    android:orientation="horizontal"
    android:paddingBottom="5dp"
    android:paddingTop="5dp" > 
</LinearLayout>

<TextView
    android:id="@+id/tvReportTitle"
    android:layout_width="wrap_content"
    android:layout_height="20dp"
    android:layout_alignBaseline="@+id/tvReportName"
    android:layout_alignBottom="@+id/tvReportName"
    android:layout_toLeftOf="@+id/bBackToParameters"
    android:layout_toRightOf="@+id/tvReportName"
    android:text="TextView"
    android:textSize="18sp" />

<Button
    android:id="@+id/bBackToParameters"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignBottom="@+id/tvReportTitle"
    android:layout_marginRight="10dp"
    android:layout_alignParentRight="true"
    android:background="@drawable/button_back_to_parameters_selector"
    android:clickable="true"
    android:onClick="backToParametersButtonOnClick"
    android:padding="3dp"
    android:text="@string/back_to_parameters"
    android:textColor="@color/my_white"
    android:textStyle="bold" />

</RelativeLayout>

and My GridFramgnet Layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<LinearLayout
android:id="@+id/llParametersContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
</LinearLayout>

<TextView 
    android:id="@+id/tvTabName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" 
    android:textColor="@color/my_black"
    android:textStyle="bold"
    android:textSize="18sp"
    android:layout_marginLeft="10dp"
    android:text="It a test string for report Name"/>

    <ScrollView 
    android:id="@+id/layout" 
    android:layout_height="match_parent"         
    android:scrollbars="horizontal|vertical" 
    android:layout_width="match_parent"     
    android:layout_marginTop="5dip"     
    android:scrollbarStyle="outsideInset"
    android:fillViewport="true"> 

    <HorizontalScrollView 
        android:id="@+id/horizontalView" 
        android:layout_height="wrap_content"     
        android:scrollbars="horizontal|vertical" 
        android:layout_width="wrap_content"     
        android:layout_marginTop="5dip">

        <TableLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/tlGridTable" >   
        </TableLayout>
    </HorizontalScrollView>
   </ScrollView>
 </LinearLayout>

The Problem This for 3 Fragments takes about 10 seconds to load, so the user get a black screen for 10 second before the Activity is actually loaded. how can I avoid it? Running all the process of populating the tables in an AsyncTask will make my ViewPager Activity to load faster? can it even be done?

UPDATE: Most of the time is beeing spend to populate/create the TableLayout. If there is a big amount of data recieved from the server then it's needed to create a big amount of TextViews and set them in the TableLayout.

UPDATE #2: Well I'm over eliminating the problem because as I understand for my needs (horizontal and vertical scrolling of whole view) I have to use TableLayout, So I need to wait this time it takes to load it. But I need to find a way to load the data in the fragments at least with the user knowing that the data is being populated (displaying appropriate Dialog). I tried to put a dialog in the onCreate of the ViewPager before I start the initialization of the fragment but for some reason it not showing and I only see the black screen until the fragments are loaded.

UPDATE:

Please refer to last question as I understand it's not possible to fix the loading times:

The Question: Is there a way to create a ViewPager with almost empty fragments(only a separate headline for each one), present them to the user and then, after the activity is already loaded and visible (and not a black screen is shown), present a Dialog and populate the fragments in while the Dialog is shown?

Any help would be appreciated.

Thanks.

like image 792
Emil Adz Avatar asked Mar 21 '13 23:03

Emil Adz


People also ask

What is difference between ViewPager and ViewPager2?

ViewPager2 is an improved version of the ViewPager library that offers enhanced functionality and addresses common difficulties with using ViewPager . If your app already uses ViewPager , read this page to learn more about migrating to ViewPager2 .

Is ViewPager deprecated?

setOnPageChangeListener (listener: ViewPager. OnPageChangeListener!) This function is deprecated.


2 Answers

This for 3 Fragments takes about 10 seconds to load, so the user get a black screen for 10 second before the Activity is actually loaded.

The reason for that is the amount of widgets you use in your layouts plus the depth of the view hierarchy of your Activity. From what you posted you have something like this:

Possible wrapper for ViewPager in the Activity -> ViewPager -> At least a ScrollView(as you say something about scrolling vertically and horizontally) -> Linearlayout -> TableLayout -> TableRow -> content of TableRow

That is a depth of 7(if I'm not mistaken the support fragment framework also inserts some views in between). This is very bad for performance. You also have a lot of widgets(assuming those for loops run for at most 10 iterations, otherwise it's just really bad) in a single Fragment and taking in consideration that the second fragment will also have its view loaded you can see that this will be again a performance hit.

It would be helpful to post your current layouts to see if they couldn't be optimized.

There are small improvements that you could make to your current code but this will not help you to go from 10 seconds to at most 1 second like you should:

There is no point in this code:

if (container == null) {
    return null;
}

I don't know what this does:

SGRaportManagerAppObj.getInstance().parametersRepository.getParametersRepository()

but see if you don't create useless objects(Is that a singleton?). Don't put it in the for loop as it will be run every time, use a normal for to avoid the use of the Iterator:

final int count = SGRaportManagerAppObj.getInstance().parametersRepository.getParametersRepository().size();
for (int i = 0; i < count; i++) {
     final Parameter parameter = SGRaportManagerAppObj.getInstance().parametersRepository.getParametersRepository().get(i);
     // ... rest of code

You should do the same in the other for loops.

Retrieve those resources(colors and drawables) outside the for loops, in the loop only assign them to the views.

Edit: Your layout is very deep and with too many views so the performance will be poor. The only way to overcome this is to use a widget that recycles its views(I know what you said). Alternatives would be:

  • creating those views on a background thread(I wouldn't do this because it's very risky)
  • you could modify the fragment to simply inflate the layout in the onCreateView and return combined with showing a loading ProgressDialog. After the Fragment becomes visible you would start creating the real view of the Fragment in chunks and finally attach it to the fragment. This would be very bad for the user experience.

And as you use a ViewPager will have additional work to make things go alright between the page switch. Maybe you could rethink your current setup.

like image 168
user Avatar answered Sep 18 '22 10:09

user


Since you use pager, you load more then just the first fragment when you start the activity. Make the initial fragment view with a progress bar visible and the table hidden and load the data only when you switch to that Fragment.

in the activity use

mPager.setOnPageChangeListener(new OnPageChangeListener() {

            @Override
            public void onPageSelected(int position) {
                Fragment f = mAdapter.getFragment(mPager.getCurrentItem());
                f.loadData(); // when you finish loading the data, hide the progress bar and show the table

            }

            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageScrollStateChanged(int state) {
            }
        });

To use mAdapter.getFragment(), inherit your adapter from:

    public abstract class SmartFragmentPagerAdapter extends FragmentPagerAdapter{

    private SparseArray<String> mFragmentTags;
    private FragmentManager mFragmentManager;

    public SmartFragmentPagerAdapter(FragmentManager fragmentManager) {
        super(fragmentManager);
        mFragmentManager = fragmentManager;
        mFragmentTags = new SparseArray<String>();
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object obj = super.instantiateItem(container, position);
        if (obj instanceof Fragment) {
            // record the fragment tag here.
            Fragment f = (Fragment) obj;
            String tag = f.getTag();
            mFragmentTags.put(position, tag);
        }
        return obj;
    }

    public Fragment getFragment(int position) {
        String tag = mFragmentTags.get(position);
        if (tag == null)
            return null;
        return mFragmentManager.findFragmentByTag(tag);
    }

    public FragmentManager getFragmentManager(){
        return mFragmentManager;
    }

}
like image 43
Asaf Pinhassi Avatar answered Sep 22 '22 10:09

Asaf Pinhassi